Skip to content

Commit

Permalink
feat(vscode): Provide SettingsAdapter for VSCode
Browse files Browse the repository at this point in the history
At the moment, the Kaoto settings are passed individually to VSCode,
meaning that in case we want to add new settings, we need to pass
individual parameters to the `KaotoEditorApp` class.

This commit provides a SettingsAdapter so every new setting will be
added directly to the adapter.

relates: https://issues.redhat.com/browse/KTO-441
relates: KaotoIO#1221
  • Loading branch information
lordrip committed Jul 24, 2024
1 parent 6872f16 commit da4cd4b
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 63 deletions.
8 changes: 6 additions & 2 deletions packages/ui/src/models-api.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
/** Models components */
export * from './models';
/**
* Models components
*
* This file shouldn't export anything other than models, for instance, no components, no hooks, etc.
*/
export * from './models/settings';
6 changes: 0 additions & 6 deletions packages/ui/src/models/settings/abstract-settings-adapter.ts

This file was deleted.

18 changes: 10 additions & 8 deletions packages/ui/src/models/settings/default-settings-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { ROOT_PATH, setValue } from '../../utils';
import { AbstractSettingsAdapter } from './abstract-settings-adapter';
import { SettingsModel } from './settings.model';
import { AbstractSettingsAdapter, ISettingsModel, SettingsModel } from './settings.model';

export class DefaultSettingsAdapter extends AbstractSettingsAdapter {
private readonly defaultSettings = new SettingsModel();
export class DefaultSettingsAdapter implements AbstractSettingsAdapter {
private settings: ISettingsModel;

constructor(settings?: Partial<ISettingsModel>) {
this.settings = new SettingsModel(settings);
}

getSettings() {
return this.defaultSettings;
return this.settings;
}

saveSettings(settings: SettingsModel) {
setValue(this.defaultSettings, ROOT_PATH, settings);
saveSettings(settings: ISettingsModel) {
Object.assign(this.settings, settings);
}
}
1 change: 0 additions & 1 deletion packages/ui/src/models/settings/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from './abstract-settings-adapter';
export * from './default-settings-adapter';
export * from './settings.model';
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { LocalStorageKeys } from '../local-storage-keys';
import { LocalStorageSettingsAdapter } from './localstorage-settings-adapter';
import { SettingsModel } from './settings.model';

describe('LocalStorageSettingsAdapter', () => {
it('should create an instance with the default settings', () => {
const adapter = new LocalStorageSettingsAdapter();

expect(adapter.getSettings()).toEqual(new SettingsModel());
});

it('should save and retrieve settings', () => {
const adapter = new LocalStorageSettingsAdapter();
const newSettings: SettingsModel = { catalogUrl: 'http://example.com' };

adapter.saveSettings(newSettings);

expect(adapter.getSettings()).toEqual(newSettings);
});

it('should retrieve the saved settings from localStorage after creating a new instance', () => {
const localStorageGetItemSpy = jest.spyOn(Storage.prototype, 'getItem');

new LocalStorageSettingsAdapter();

expect(localStorageGetItemSpy).toHaveBeenCalledWith(LocalStorageKeys.Settings);
});

it('should save the settings to localStorage', () => {
const localStorageSetItemSpy = jest.spyOn(Storage.prototype, 'setItem');

const adapter = new LocalStorageSettingsAdapter();
const newSettings: SettingsModel = { catalogUrl: 'http://example.com' };

adapter.saveSettings(newSettings);

expect(localStorageSetItemSpy).toHaveBeenCalledWith(LocalStorageKeys.Settings, JSON.stringify(newSettings));
});
});
14 changes: 6 additions & 8 deletions packages/ui/src/models/settings/localstorage-settings-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import { LocalStorageKeys } from '../local-storage-keys';
import { AbstractSettingsAdapter } from './abstract-settings-adapter';
import { SettingsModel } from './settings.model';
import { AbstractSettingsAdapter, ISettingsModel, SettingsModel } from './settings.model';

export class LocalStorageSettingsAdapter extends AbstractSettingsAdapter {
private readonly settings: SettingsModel;
export class LocalStorageSettingsAdapter implements AbstractSettingsAdapter {
private settings: ISettingsModel;

constructor() {
super();

const rawSettings = localStorage.getItem(LocalStorageKeys.Settings) ?? '{}';
const parsedSettings = JSON.parse(rawSettings);
this.settings = new SettingsModel(parsedSettings);
}

getSettings(): SettingsModel {
getSettings(): ISettingsModel {
return this.settings;
}

saveSettings(settings: SettingsModel): void {
saveSettings(settings: ISettingsModel): void {
localStorage.setItem(LocalStorageKeys.Settings, JSON.stringify(settings));
this.settings = { ...settings };
}
}
17 changes: 11 additions & 6 deletions packages/ui/src/models/settings/settings.model.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
const DEFAULT_SETTINGS: SettingsModel = {
catalogUrl: '',
};
export interface ISettingsModel {
catalogUrl: string;
}

export interface AbstractSettingsAdapter {
getSettings(): ISettingsModel;
saveSettings(settings: ISettingsModel): void;
}

export class SettingsModel {
export class SettingsModel implements ISettingsModel {
catalogUrl: string = '';

constructor(options: Partial<SettingsModel> = {}) {
Object.assign(this, DEFAULT_SETTINGS, options);
constructor(options: Partial<ISettingsModel> = {}) {
Object.assign(this, options);
}
}
10 changes: 4 additions & 6 deletions packages/ui/src/multiplying-architecture/KaotoBridge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CatalogLoaderProvider } from '../providers/catalog.provider';
import { DeleteModalContextProvider } from '../providers/delete-modal.provider';
import { RuntimeProvider } from '../providers/runtime.provider';
import { SchemasLoaderProvider } from '../providers/schemas.provider';
import { SettingsContext } from '../providers/settings.provider';
import { SourceCodeApiContext } from '../providers/source-code.provider';
import { VisibleFlowsProvider } from '../providers/visible-flows.provider';
import { EventNotifier } from '../utils';
Expand Down Expand Up @@ -45,18 +46,15 @@ interface KaotoBridgeProps {
* ChannelType where the component is running.
*/
channelType: ChannelType;

/**
* Catalog URL to load the components and schemas.
*/
catalogUrl: string;
}

export const KaotoBridge = forwardRef<EditorApi, PropsWithChildren<KaotoBridgeProps>>((props, forwardedRef) => {
const ReloadProvider = useReload();
const eventNotifier = EventNotifier.getInstance();
const sourceCodeApiContext = useContext(SourceCodeApiContext);
const sourceCodeRef = useRef<string>('');
const settingsAdapter = useContext(SettingsContext);
const catalogUrl = settingsAdapter.getSettings().catalogUrl;

/**
* Callback is exposed to the Channel that is called when a new file is opened.
Expand Down Expand Up @@ -145,7 +143,7 @@ export const KaotoBridge = forwardRef<EditorApi, PropsWithChildren<KaotoBridgePr

return (
<ReloadProvider>
<RuntimeProvider catalogUrl={props.catalogUrl}>
<RuntimeProvider catalogUrl={catalogUrl}>
<SchemasLoaderProvider>
<CatalogLoaderProvider>
<CatalogTilesProvider>
Expand Down
45 changes: 24 additions & 21 deletions packages/ui/src/multiplying-architecture/KaotoEditorApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
import { Notification } from '@kie-tools-core/notifications/dist/api';
import '@patternfly/react-core/dist/styles/base.css'; // This import needs to be first
import { RefObject, createRef } from 'react';
import { AbstractSettingsAdapter } from '../models/settings';
import { EntitiesProvider } from '../providers/entities.provider';
import { SettingsProvider } from '../providers/settings.provider';
import { SourceCodeProvider } from '../providers/source-code.provider';
import { KaotoBridge } from './KaotoBridge';
import { KaotoEditor } from './KaotoEditor';
Expand All @@ -23,7 +25,7 @@ export class KaotoEditorApp implements Editor {
constructor(
private readonly envelopeContext: KogitoEditorEnvelopeContextType<KaotoEditorChannelApi>,
private readonly initArgs: EditorInitArgs,
private readonly catalogUrl: string,
private readonly settingsAdapter: AbstractSettingsAdapter,
) {
this.editorRef = createRef<EditorApi>();
}
Expand Down Expand Up @@ -75,26 +77,27 @@ export class KaotoEditorApp implements Editor {
return (
<SourceCodeProvider>
<EntitiesProvider>
<KaotoBridge
ref={this.editorRef}
channelType={this.initArgs.channel}
onReady={() => this.envelopeContext.channelApi.notifications.kogitoEditor_ready.send()}
onNewEdit={(edit) => {
this.envelopeContext.channelApi.notifications.kogitoWorkspace_newEdit.send(edit);
}}
setNotifications={(path, notifications) =>
this.envelopeContext.channelApi.notifications.kogitoNotifications_setNotifications.send(
path,
notifications,
)
}
onStateControlCommandUpdate={(command) =>
this.envelopeContext.channelApi.notifications.kogitoEditor_stateControlCommandUpdate.send(command)
}
catalogUrl={this.catalogUrl}
>
<KaotoEditor />
</KaotoBridge>
<SettingsProvider adapter={this.settingsAdapter}>
<KaotoBridge
ref={this.editorRef}
channelType={this.initArgs.channel}
onReady={() => this.envelopeContext.channelApi.notifications.kogitoEditor_ready.send()}
onNewEdit={(edit) => {
this.envelopeContext.channelApi.notifications.kogitoWorkspace_newEdit.send(edit);
}}
setNotifications={(path, notifications) =>
this.envelopeContext.channelApi.notifications.kogitoNotifications_setNotifications.send(
path,
notifications,
)
}
onStateControlCommandUpdate={(command) =>
this.envelopeContext.channelApi.notifications.kogitoEditor_stateControlCommandUpdate.send(command)
}
>
<KaotoEditor />
</KaotoBridge>
</SettingsProvider>
</EntitiesProvider>
</SourceCodeProvider>
);
Expand Down
10 changes: 10 additions & 0 deletions packages/ui/src/multiplying-architecture/KaotoEditorChannelApi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { KogitoEditorChannelApi } from '@kie-tools-core/editor/dist/api';
import { ISettingsModel } from '../models/settings';

export interface KaotoEditorChannelApi extends KogitoEditorChannelApi {
/**
* @deprecated Use `getVSCodeKaotoSettings` instead
* Returns the URL of the catalog.
*/
getCatalogURL(): Promise<string | undefined>;

/**
* Returns the Kaoto VSCode settings defined.
*/
getVSCodeKaotoSettings(): Promise<ISettingsModel>;
}
25 changes: 20 additions & 5 deletions packages/ui/src/multiplying-architecture/KaotoEditorFactory.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
import { KaotoEditorApp } from './KaotoEditorApp';
import {
Editor,
EditorFactory,
EditorInitArgs,
KogitoEditorEnvelopeContextType,
} from '@kie-tools-core/editor/dist/api';
import { KaotoEditorChannelApi } from './KaotoEditorChannelApi';
import { DefaultSettingsAdapter } from '../models';
import { CatalogSchemaLoader, isDefined } from '../utils';
import { KaotoEditorApp } from './KaotoEditorApp';
import { KaotoEditorChannelApi } from './KaotoEditorChannelApi';

export class KaotoEditorFactory implements EditorFactory<Editor, KaotoEditorChannelApi> {
public async createEditor(
envelopeContext: KogitoEditorEnvelopeContextType<KaotoEditorChannelApi>,
initArgs: EditorInitArgs,
): Promise<Editor> {
let catalogUrl = await envelopeContext.channelApi.requests.getCatalogURL();
const settings = await envelopeContext.channelApi.requests.getVSCodeKaotoSettings();
const settingsAdapter = new DefaultSettingsAdapter(settings);
this.updateCatalogUrl(settingsAdapter, initArgs);

return Promise.resolve(new KaotoEditorApp(envelopeContext, initArgs, settingsAdapter));
}

/**
* Updates the catalog URL in the settings if it is not defined, to include the embedded catalog.
* It uses the resourcesPathPrefix from the initArgs to build the default catalog URL.
*
* @param settingsAdapter The settings adapter to update the catalog URL
* @param initArgs The init args to get the resources path prefix
*/
private updateCatalogUrl(settingsAdapter: DefaultSettingsAdapter, initArgs: EditorInitArgs) {
let catalogUrl = settingsAdapter.getSettings().catalogUrl;

if (!isDefined(catalogUrl) || catalogUrl === '') {
catalogUrl = `${initArgs.resourcesPathPrefix}${CatalogSchemaLoader.DEFAULT_CATALOG_PATH.replace('.', '')}`;
settingsAdapter.saveSettings({ ...settingsAdapter.getSettings(), catalogUrl });
}

return Promise.resolve(new KaotoEditorApp(envelopeContext, initArgs, catalogUrl));
}
}

0 comments on commit da4cd4b

Please sign in to comment.