graphdb-workbench

Context API

Stateful context services and the ContextService abstraction for managing shared application state in GraphDB Workbench.

Contents

Overview

A context service is the single owner of one piece of shared state (selected repository, language, security, …) in packages/api. Consumers in every package read the current value from it and subscribe to changes. See state ownership.

The ContextService API (context.service.ts):

Core Concepts

Abstract ContextService

The ContextService class is generic and requires a type parameter TFields that defines the fields the service can handle. Each field corresponds to a property of the service and is managed via the context map.

Key methods:

Utility Types

SnakeToPascalCase converts SNAKE_CASE field names to PascalCase for method naming.

DeriveContextServiceContract requires an update method for each field. For example, a field SELECTED_REPOSITORY requires an updateSelectedRepository method.

Implementation Example: RepositoryContextService

RepositoryContextService owns the repository list and the selected repository.

Fields

readonly SELECTED_REPOSITORY = 'selectedRepository';
readonly REPOSITORY_LIST = 'repositoryList';

Methods

Updating

Reading

Subscribing

Step-by-Step Guide to Using ContextService

1. Define Context Fields and Parameters

type RepositoryContextFields = {
  readonly REPOSITORY_LIST: string;
  readonly SELECTED_REPOSITORY: string;
};

type RepositoryContextFieldParams = {
  readonly REPOSITORY_LIST: RepositoryList;
  readonly SELECTED_REPOSITORY: RepositoryReference;
};

2. Extend ContextService

export class RepositoryContextService extends ContextService<RepositoryContextFields>
  implements DeriveContextServiceContract<RepositoryContextFields, RepositoryContextFieldParams> {
  readonly SELECTED_REPOSITORY = 'selectedRepository';
  readonly REPOSITORY_LIST = 'repositoryList';

  updateRepositoryList(repositories: RepositoryList): void {
    this.updateContextProperty(this.REPOSITORY_LIST, repositories);
  }

  getRepositoryList(): RepositoryList {
    return this.getContextPropertyValue(this.REPOSITORY_LIST) || new RepositoryList([]);
  }

  onRepositoryListChanged(callbackFunction: ValueChangeCallback<RepositoryList | undefined>): () => void {
    return this.subscribe(this.REPOSITORY_LIST, callbackFunction);
  }

  // updateSelectedRepository, getSelectedRepository, onSelectedRepositoryChanged …
}

3. Get the Service

import { service, RepositoryContextService } from '@ontotext/workbench-api';

const repositoryContextService = service(RepositoryContextService);

4. Read, Update and Subscribe

const selected = repositoryContextService.getSelectedRepository();

await repositoryContextService.updateSelectedRepository({id: 'my-repo', location: ''});

const unsubscribe = repositoryContextService.onSelectedRepositoryChanged((repository) => {
  // runs now with the current value, then on every change
});

// When the consumer is destroyed:
unsubscribe();

Subscription behaviour

Restoring values from storage

When a persisted context value changes in localStorage (for example in another tab), the Persistence API passes the raw string to the context service that canHandle the property. Override getDeserializedContext() to map a property to a function that turns that string back into a model; RepositoryContextService parses the stored RepositoryReference and looks the repository up in the list.


See also: Developers Guide