diff --git a/lib/content-services/src/lib/extensions-manager/index.ts b/lib/content-services/src/lib/extensions-manager/index.ts new file mode 100644 index 0000000000..f22da90e09 --- /dev/null +++ b/lib/content-services/src/lib/extensions-manager/index.ts @@ -0,0 +1,18 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './public-api'; diff --git a/lib/content-services/src/lib/extensions-manager/models/app-config-plugin-ref.ts b/lib/content-services/src/lib/extensions-manager/models/app-config-plugin-ref.ts new file mode 100644 index 0000000000..a642eef5c9 --- /dev/null +++ b/lib/content-services/src/lib/extensions-manager/models/app-config-plugin-ref.ts @@ -0,0 +1,22 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class AppConfigPluginRef { + plugins: { + [key: string]: boolean; + } = {}; +} diff --git a/lib/content-services/src/lib/extensions-manager/models/extension-composition-entry.ts b/lib/content-services/src/lib/extensions-manager/models/extension-composition-entry.ts new file mode 100644 index 0000000000..5844d71c2f --- /dev/null +++ b/lib/content-services/src/lib/extensions-manager/models/extension-composition-entry.ts @@ -0,0 +1,22 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ExtensionComposition } from './extension-composition'; + +export class ExtensionCompositionEntry { + entry: ExtensionComposition; +} diff --git a/lib/content-services/src/lib/extensions-manager/models/extension-composition.ts b/lib/content-services/src/lib/extensions-manager/models/extension-composition.ts new file mode 100644 index 0000000000..ae71c1b259 --- /dev/null +++ b/lib/content-services/src/lib/extensions-manager/models/extension-composition.ts @@ -0,0 +1,29 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AppConfigPluginRef } from './app-config-plugin-ref'; +import { RouteRef, RuleRef, ActionRef } from '@alfresco/adf-extensions'; + +export class ExtensionComposition { + appConfig: AppConfigPluginRef = new AppConfigPluginRef(); + rules?: Array = []; + routes?: Array = []; + actions?: Array = []; + features?: { + [key: string]: any; + } = {}; +} diff --git a/lib/content-services/src/lib/extensions-manager/models/extension-info.model.ts b/lib/content-services/src/lib/extensions-manager/models/extension-info.model.ts new file mode 100644 index 0000000000..c2d07a6923 --- /dev/null +++ b/lib/content-services/src/lib/extensions-manager/models/extension-info.model.ts @@ -0,0 +1,37 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ActionRef, RouteRef, RuleRef } from '@alfresco/adf-extensions'; + +export class ExtensionInfoModel { + $id: string; + $name: string; + $version: string; + $vendor: string; + $license: string; + $description?: string; + $dependencies?: Array; + $compatibilities?: Array; + + extensionId: string; + rules?: Array; + routes?: Array; + actions?: Array; + features?: { + [key: string]: any; + }; +} diff --git a/lib/content-services/src/lib/extensions-manager/public-api.ts b/lib/content-services/src/lib/extensions-manager/public-api.ts new file mode 100644 index 0000000000..321d5c27d1 --- /dev/null +++ b/lib/content-services/src/lib/extensions-manager/public-api.ts @@ -0,0 +1,22 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './services/extension-manager.service'; +export * from './models/app-config-plugin-ref'; +export * from './models/extension-info.model'; +export * from './models/extension-composition'; +export * from './models/extension-composition-entry'; diff --git a/lib/content-services/src/lib/extensions-manager/services/extension-manager.service.spec.ts b/lib/content-services/src/lib/extensions-manager/services/extension-manager.service.spec.ts new file mode 100644 index 0000000000..0e75b49f61 --- /dev/null +++ b/lib/content-services/src/lib/extensions-manager/services/extension-manager.service.spec.ts @@ -0,0 +1,81 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ExtensionManagerService } from './extension-manager.service'; +import { TestBed } from '@angular/core/testing'; +import { CoreTestingModule } from '../testing/core.testing.module'; +import { ExtensionComposition, ExtensionCompositionEntry } from '@alfresco/js-api'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { first } from 'rxjs/operators'; + +describe('ExtensionManagerService', () => { + let extensionManagerService: ExtensionManagerService; + let httpTestingController: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [CoreTestingModule, HttpClientTestingModule] + }); + extensionManagerService = TestBed.inject(ExtensionManagerService); + httpTestingController = TestBed.inject(HttpTestingController); + }); + + it('should fetch saved plugin state for provided instance', () => { + const getSpy = spyOn(extensionManagerService.settingsApi, 'getSavedExtensionState').and.returnValue( + Promise.resolve(new ExtensionCompositionEntry()) + ); + const instanceId = 'test-instance-id'; + extensionManagerService.getSavedPluginState(instanceId); + expect(getSpy).toHaveBeenCalledOnceWith('test-instance-id'); + }); + + it('should fetch saved plugin state for provided instance', () => { + const extensionComposition = new ExtensionComposition(); + const instanceId = 'test-instance-id'; + const putSpy = spyOn(extensionManagerService.settingsApi, 'publishExtensionConfig').and.returnValue(Promise.resolve()); + extensionManagerService.publishExtensionConfig(instanceId, extensionComposition); + expect(putSpy).toHaveBeenCalledOnceWith('test-instance-id', extensionComposition); + }); + + it('should fetch extension info from pluginInfo.json of a running instance of ADW', (done) => { + extensionManagerService + .getPluginInfo('test-adw-url') + .pipe(first()) + .subscribe((data) => { + expect(data).toEqual([]); + done(); + }); + const req = httpTestingController.expectOne('test-adw-url/pluginInfo.json'); + expect(req.request.method).toEqual('GET'); + req.flush([]); + httpTestingController.verify(); + }); + + it('should fetch extension defaults from appConfig.json of a running instance of ADW', (done) => { + extensionManagerService + .getDefaultPluginState('test-adw-url') + .pipe(first()) + .subscribe((data) => { + expect(data).toEqual({ plugins: {} }); + done(); + }); + const req = httpTestingController.expectOne('test-adw-url/app.config.json'); + expect(req.request.method).toEqual('GET'); + req.flush({ plugins: {} }); + httpTestingController.verify(); + }); +}); diff --git a/lib/content-services/src/lib/extensions-manager/services/extension-manager.service.ts b/lib/content-services/src/lib/extensions-manager/services/extension-manager.service.ts new file mode 100644 index 0000000000..552371cc3f --- /dev/null +++ b/lib/content-services/src/lib/extensions-manager/services/extension-manager.service.ts @@ -0,0 +1,60 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; +import { from, Observable, of } from 'rxjs'; +import { SettingsApi } from '@alfresco/js-api'; +import { HttpClient } from '@angular/common/http'; +import { AlfrescoApiService } from '../../services/alfresco-api.service'; +import { ExtensionInfoModel } from '../models/extension-info.model'; +import { ExtensionCompositionEntry } from '../models/extension-composition-entry'; +import { AppConfigPluginRef } from '../models/app-config-plugin-ref'; +import { ExtensionComposition } from '../models/extension-composition'; + +@Injectable({ + providedIn: 'root' +}) +export class ExtensionManagerService { + constructor(private apiService: AlfrescoApiService, private httpClient: HttpClient) {} + + private _settingsApi: SettingsApi; + + get settingsApi(): SettingsApi { + this._settingsApi = this._settingsApi ?? new SettingsApi(this.apiService.getInstance()); + return this._settingsApi; + } + + getPluginInfo(instanceUrl: string): Observable { + return this.httpClient.get(`${instanceUrl}/pluginInfo.json`); + } + + getSavedPluginState(instanceId: string): Observable { + // TODO: Update below code once backend APIs are working + // eslint-disable-next-line no-console + console.log(instanceId); + return of(new ExtensionCompositionEntry()); + // return from(this.settingsApi.getSavedExtensionState(instanceId)); + } + + getDefaultPluginState(instanceUrl: string): Observable { + return this.httpClient.get(`${instanceUrl}/app.config.json`); + } + + publishExtensionConfig(instanceId: string, pluginConfig: ExtensionComposition): Observable { + return from(this.settingsApi.publishExtensionConfig(instanceId, pluginConfig)); + } +} diff --git a/lib/content-services/src/public-api.ts b/lib/content-services/src/public-api.ts index aa031f92d0..7813cf1459 100644 --- a/lib/content-services/src/public-api.ts +++ b/lib/content-services/src/public-api.ts @@ -51,6 +51,7 @@ export * from './lib/api-factories'; export * from './lib/mock/alfresco-api.service.mock'; export * from './lib/agent/index'; export * from './lib/search-ai/index'; +export * from './lib/extensions-manager/index'; export * from './lib/content.module'; export * from './lib/material.module'; diff --git a/lib/js-api/src/api/content-rest-api/api/index.ts b/lib/js-api/src/api/content-rest-api/api/index.ts index 899efe5d3b..669ce5b69e 100644 --- a/lib/js-api/src/api/content-rest-api/api/index.ts +++ b/lib/js-api/src/api/content-rest-api/api/index.ts @@ -34,6 +34,7 @@ export * from './queries.api'; export * from './ratings.api'; export * from './renditions.api'; export * from './search-ai.api'; +export * from './settings.api'; export * from './sharedlinks.api'; export * from './sites.api'; export * from './tags.api'; diff --git a/lib/js-api/src/api/content-rest-api/api/settings.api.ts b/lib/js-api/src/api/content-rest-api/api/settings.api.ts new file mode 100644 index 0000000000..1a6d7fc164 --- /dev/null +++ b/lib/js-api/src/api/content-rest-api/api/settings.api.ts @@ -0,0 +1,63 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BaseApi } from './base.api'; +import { throwIfNotDefined } from '../../../assert'; + +export class SettingsApi extends BaseApi { + /** + * Gets the published extension configuration from + * the database + * @param instanceId Unique ID for a running instance of ADW + * for which configuration is to be fetched + */ + getSavedExtensionState(instanceId: string): Promise { + throwIfNotDefined(instanceId, 'instanceId'); + + const pathParams = { + instanceId + }; + + return this.get({ + path: '/settings/{instanceId}', + pathParams + }); + } + + /** + * Publish the extension configuration for an ADW instance + * in the database + * @param instanceId Unique ID for a running instance of ADW + * for which configuration is to be published + * @param extensionConfig Extension configuration that is to + * be saved + */ + publishExtensionConfig(instanceId: string, extensionConfig: any): Promise { + throwIfNotDefined(instanceId, 'instanceId'); + throwIfNotDefined(extensionConfig, 'extensionConfig'); + + const pathParams = { + instanceId + }; + + return this.put({ + path: '/settings/{instanceId}', + pathParams, + bodyParam: extensionConfig + }); + } +}