From 1ab385a3c9326f7f1c44c6613437cec571845cc4 Mon Sep 17 00:00:00 2001 From: Mykyta Maliarchuk <84377976+nikita-web-ua@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:37:57 +0200 Subject: [PATCH] [MNT-25629] Add batch document-runtime endpoint support (#12121) * [MNT-25629] Add batch document-runtime endpoint support * [MNT-25629] cr fixes * [MNT-25629] cr fixes * [MNT-25629] add tests for api method --- docs/core/services/process-content.service.md | 24 +++++- .../api/activiti-rest-api/api/content.api.ts | 35 ++++++++ .../api/activiti-rest-api/docs/ContentApi.md | 38 +++++++++ .../docs/RelatedProcessTask.md | 1 + .../model/relatedProcessTask.ts | 1 + lib/js-api/test/mockObjects/index.ts | 1 + .../process-services/content.mock.ts | 42 ++++++++++ .../test/process-services/contentApi.spec.ts | 79 +++++++++++++++++++ .../services/process-content.service.spec.ts | 12 +++ .../form/services/process-content.service.ts | 21 +++++ 10 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 lib/js-api/test/mockObjects/process-services/content.mock.ts create mode 100644 lib/js-api/test/process-services/contentApi.spec.ts diff --git a/docs/core/services/process-content.service.md b/docs/core/services/process-content.service.md index 1f335f490b..aee0f67f5b 100644 --- a/docs/core/services/process-content.service.md +++ b/docs/core/services/process-content.service.md @@ -66,12 +66,19 @@ Manipulates content related to a Process Instance or Task Instance in APS. - _taskId:_ `string` - ID of the target task - _opts:_ `any` - (Optional) Options supported by JS-API - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`` - Metadata for the content -- **getProcessesAndTasksOnContent**(sourceId: `string`, source: `string`, size?: `number`, page?: `number`): [`Observable`](http://reactivex.io/documentation/observable.html)<[`ResultListDataRepresentationRelatedProcessTask`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/activiti-rest-api/docs/ResultListDataRepresentation%C2%ABRelatedContentRepresentation%C2%BB.md)>
+- **getProcessesAndTasksOnContent**(sourceId: `string`, source: `string`, size?: `number`, page?: `number`): [`Observable`](http://reactivex.io/documentation/observable.html)<[`ResultListDataRepresentationRelatedProcessTask`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/activiti-rest-api/docs/ResultListDataRepresentationRelatedProcessTask.md)>
Lists processes and tasks on workflow started with provided document - _sourceId:_ `string` - id of the document that workflow or task has been started with - _source:_ `string` - source of the document that workflow or task has been started with - _size:_ `number` - size of the entries to get - _page:_ `number` - page number +- **getProcessesAndTasksOnContentBatch**(sourceIds: `string[]`, source: `string`, size?: `number`, page?: `number`): [`Observable`](http://reactivex.io/documentation/observable.html)<[`ResultListDataRepresentationRelatedProcessTask`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/activiti-rest-api/docs/ResultListDataRepresentationRelatedProcessTask.md)>
+ Batch variant of getProcessesAndTasksOnContent. Accepts multiple source document ids in one request (up to 500) and returns the related processes and tasks for all of them. + - _sourceIds:_ `string[]` - ids of the documents to query process participation for (up to 500) + - _source:_ `string` - source of the documents that workflows or tasks have been started with + - _size:_ `number` - (Optional) size of the entries to get + - _page:_ `number` - (Optional) page number + - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)<[`ResultListDataRepresentationRelatedProcessTask`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/activiti-rest-api/docs/ResultListDataRepresentationRelatedProcessTask.md)> - **handleError**(error: `any`): [`Observable`](http://reactivex.io/documentation/observable.html)``
Reports an error message. - _error:_ `any` - Data object with optional `message` and `status` fields for the error @@ -422,6 +429,21 @@ this.contentService.getProcessesAndTasksOnContent(sourceId, source).subscribe( }); ``` +#### getProcessesAndTasksOnContentBatch(sourceIds: string[], source: string, size?: number, page?: number): Observable`` + +Batch variant of `getProcessesAndTasksOnContent`. Accepts multiple source document ids in one request (up to 500) and returns the related processes and tasks for all of them. + +```ts +const sourceIds = ['sourceId1', 'sourceId2']; +const source = 'source'; +this.contentService.getProcessesAndTasksOnContentBatch(sourceIds, source).subscribe( + res => { + console.log('Response: ', res); + }, error => { + console.log('Error: ', error); + }); +``` + ## Details ## Importing diff --git a/lib/js-api/src/api/activiti-rest-api/api/content.api.ts b/lib/js-api/src/api/activiti-rest-api/api/content.api.ts index 4946dcbd17..560a5ae662 100644 --- a/lib/js-api/src/api/activiti-rest-api/api/content.api.ts +++ b/lib/js-api/src/api/activiti-rest-api/api/content.api.ts @@ -27,6 +27,11 @@ import { throwIfNotDefined } from '../../../assert'; * Content service. */ export class ContentApi extends BaseApi { + /** + * Maximum number of source document ids accepted by the batch document-runtime endpoint in a single request. + */ + static readonly DOCUMENT_RUNTIME_BATCH_SIZE_LIMIT = 500; + /** * Attach existing content to a process instance * @@ -318,4 +323,34 @@ export class ContentApi extends BaseApi { } }); } + + /** + * Batch variant of getProcessesAndTasksOnContent. Accepts multiple source document ids + * in one request (up to 500) and returns the related processes and tasks for all of them. + * + * @param sourceIds - ids of the documents to query process participation for (up to 500) + * @param source - source of the documents that workflows or tasks have been started with + * @param size - size of the entries to get + * @param page - page number + * @return Promise + * @throws {Error} if sourceIds exceeds DOCUMENT_RUNTIME_BATCH_SIZE_LIMIT + */ + getProcessesAndTasksOnContentBatch( + sourceIds: string[], + source: string, + size?: number, + page?: number + ): Promise { + throwIfNotDefined(sourceIds, 'sourceIds'); + throwIfNotDefined(source, 'source'); + + if (sourceIds.length > ContentApi.DOCUMENT_RUNTIME_BATCH_SIZE_LIMIT) { + throw new Error(`sourceIds length exceeds the maximum batch size of ${ContentApi.DOCUMENT_RUNTIME_BATCH_SIZE_LIMIT}`); + } + + return this.post({ + path: '/api/enterprise/document-runtime', + bodyParam: { sourceIds, source, size, page } + }); + } } diff --git a/lib/js-api/src/api/activiti-rest-api/docs/ContentApi.md b/lib/js-api/src/api/activiti-rest-api/docs/ContentApi.md index 0c06566f1b..4b40ca763f 100644 --- a/lib/js-api/src/api/activiti-rest-api/docs/ContentApi.md +++ b/lib/js-api/src/api/activiti-rest-api/docs/ContentApi.md @@ -17,6 +17,7 @@ Method | HTTP request | Description [**getRelatedContentForProcessInstance**](ContentApi.md#getRelatedContentForProcessInstance) | **GET** /enterprise/process-instances/{processInstanceId}/content | List content attached to a process instance [**getRelatedContentForTask**](ContentApi.md#getRelatedContentForTask) | **GET** /enterprise/tasks/{taskId}/content | List content attached to a task [**getProcessesAndTasksOnContent**](ContentApi.md#getProcessesAndTasksOnContent) | **GET** enterprise/content/document-runtime | Lists processes and tasks on workflow started with provided document +[**getProcessesAndTasksOnContentBatch**](ContentApi.md#getProcessesAndTasksOnContentBatch) | **POST** /enterprise/document-runtime | Batch lookup of processes and tasks for multiple source documents # **createRelatedContentOnProcessInstance** @@ -530,5 +531,42 @@ contentApi.getProcessesAndTasksOnContent('sourceId', 'source').then((data) => { [**ResultListDataRepresentationRelatedProcessTask**](ResultListDataRepresentationRelatedProcessTask.md) + +# **getProcessesAndTasksOnContentBatch** +> ResultListDataRepresentationRelatedProcessTask getProcessesAndTasksOnContentBatch(sourceIds, source, size, page) +Batch variant of getProcessesAndTasksOnContent. Accepts up to 500 source document ids in one request and returns the related processes and tasks for all of them. + +### Example +```javascript +import ContentApi from 'ContentApi'; +import { AlfrescoApi } from '@alfresco/js-api'; + +const alfrescoApi = new AlfrescoApi(); +alfrescoApi.setConfig({ + hostEcm: 'http://127.0.0.1:8080' +}); + +const contentApi = new ContentApi(alfrescoApi); + +contentApi.getProcessesAndTasksOnContentBatch(['id1', 'id2'], 'source').then((data) => { + console.log('API called successfully. Returned data: ' + data); +}, function(error) { + console.error(error); +}); + +``` + +### Parameters + +| Name | Type | Description | Notes | +|---------------|--------------|-----------------------------------------|----------| +| **sourceIds** | **string[]** | List of source document ids (up to 500) | | +| **source** | **string** | Source repository identifier | | +| **size** | **number** | Page size | optional | +| **page** | **number** | Page number (zero-based) | optional | + +### Return type + +[**ResultListDataRepresentationRelatedProcessTask**](ResultListDataRepresentationRelatedProcessTask.md) diff --git a/lib/js-api/src/api/activiti-rest-api/docs/RelatedProcessTask.md b/lib/js-api/src/api/activiti-rest-api/docs/RelatedProcessTask.md index 44f1290097..5be10280f2 100644 --- a/lib/js-api/src/api/activiti-rest-api/docs/RelatedProcessTask.md +++ b/lib/js-api/src/api/activiti-rest-api/docs/RelatedProcessTask.md @@ -5,3 +5,4 @@ | ------------ | ------------- | ------------- | ------------- | | **processId** | **string** | | [optional] [default to undefined] | | **taskId** | **string** | | [optional] [default to undefined] | +| **sourceId** | **string** | | [optional] [default to undefined] | diff --git a/lib/js-api/src/api/activiti-rest-api/model/relatedProcessTask.ts b/lib/js-api/src/api/activiti-rest-api/model/relatedProcessTask.ts index 4333c2a958..13d4f8e33e 100644 --- a/lib/js-api/src/api/activiti-rest-api/model/relatedProcessTask.ts +++ b/lib/js-api/src/api/activiti-rest-api/model/relatedProcessTask.ts @@ -18,4 +18,5 @@ export interface RelatedProcessTask { processId?: string; taskId?: string; + sourceId?: string; } diff --git a/lib/js-api/test/mockObjects/index.ts b/lib/js-api/test/mockObjects/index.ts index 1f51a36400..d57de77598 100644 --- a/lib/js-api/test/mockObjects/index.ts +++ b/lib/js-api/test/mockObjects/index.ts @@ -39,6 +39,7 @@ export * from './goverance-services/security-groups.mock'; export * from './goverance-services/security-marks.mock'; export * from './process-services/bpm-auth.mock'; +export * from './process-services/content.mock'; export * from './process-services/process.mock'; export * from './process-services/process-instance-variables.mock'; export * from './process-services/models.mock'; diff --git a/lib/js-api/test/mockObjects/process-services/content.mock.ts b/lib/js-api/test/mockObjects/process-services/content.mock.ts new file mode 100644 index 0000000000..4e0f29d0c2 --- /dev/null +++ b/lib/js-api/test/mockObjects/process-services/content.mock.ts @@ -0,0 +1,42 @@ +/*! + * @license + * Copyright © 2005-2026 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 { BaseMock } from '../base.mock'; + +export class ContentMock extends BaseMock { + getProcessesAndTasksOnContentBatch200(): void { + this.mock() + .post('/activiti-app/api/enterprise/document-runtime') + .reply(200, { + size: 2, + total: 2, + start: 0, + data: [ + { + sourceId: 'node-1;1.0@site1', + processId: '42', + taskId: null + }, + { + sourceId: 'node-2;1.0@site1', + processId: null, + taskId: '7' + } + ] + }); + } +} diff --git a/lib/js-api/test/process-services/contentApi.spec.ts b/lib/js-api/test/process-services/contentApi.spec.ts new file mode 100644 index 0000000000..54640f09a6 --- /dev/null +++ b/lib/js-api/test/process-services/contentApi.spec.ts @@ -0,0 +1,79 @@ +/*! + * @license + * Copyright © 2005-2026 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 assert from 'assert'; +import { resetGlobalMockAgent } from '../mockObjects/base.mock'; +import { BpmAuthMock, ContentMock } from '../mockObjects'; +import { AlfrescoApi, ActivitiContentApi } from '../../src'; +import { describe, it, beforeEach, afterEach } from 'node:test'; + +describe('Activiti Content Api', () => { + let authResponseBpmMock: BpmAuthMock; + let contentMock: ContentMock; + let alfrescoJsApi: AlfrescoApi; + let contentApi: ActivitiContentApi; + + const hostBpm = 'https://127.0.0.1:9999'; + + beforeEach(async () => { + authResponseBpmMock = new BpmAuthMock(hostBpm); + contentMock = new ContentMock(hostBpm); + + authResponseBpmMock.get200Response(); + + alfrescoJsApi = new AlfrescoApi({ + hostBpm, + provider: 'BPM' + }); + + contentApi = new ActivitiContentApi(alfrescoJsApi); + + await alfrescoJsApi.login('admin', 'admin'); + }); + + afterEach(() => { + resetGlobalMockAgent(); + }); + + describe('getProcessesAndTasksOnContentBatch', () => { + it('should return related processes and tasks for the given source ids', async () => { + contentMock.getProcessesAndTasksOnContentBatch200(); + + const result = await contentApi.getProcessesAndTasksOnContentBatch(['node-1;1.0@site1', 'node-2;1.0@site1'], 'alfresco-1-repoAlfresco'); + + assert.equal(result.size, 2); + assert.equal(result.data[0].sourceId, 'node-1;1.0@site1'); + assert.equal(result.data[0].processId, '42'); + assert.equal(result.data[1].sourceId, 'node-2;1.0@site1'); + }); + + it('should throw when sourceIds exceeds DOCUMENT_RUNTIME_BATCH_SIZE_LIMIT', () => { + const oversizedIds = Array.from({ length: ActivitiContentApi.DOCUMENT_RUNTIME_BATCH_SIZE_LIMIT + 1 }, (_, i) => `node-${i}`); + + assert.throws( + () => contentApi.getProcessesAndTasksOnContentBatch(oversizedIds, 'alfresco-1-repoAlfresco'), + (err: Error) => { + assert.equal( + err.message, + `sourceIds length exceeds the maximum batch size of ${ActivitiContentApi.DOCUMENT_RUNTIME_BATCH_SIZE_LIMIT}` + ); + return true; + } + ); + }); + }); +}); diff --git a/lib/process-services/src/lib/form/services/process-content.service.spec.ts b/lib/process-services/src/lib/form/services/process-content.service.spec.ts index 8bd2545c4c..823793b1cd 100644 --- a/lib/process-services/src/lib/form/services/process-content.service.spec.ts +++ b/lib/process-services/src/lib/form/services/process-content.service.spec.ts @@ -232,4 +232,16 @@ describe('ProcessContentService', () => { }); }); }); + + it('should call getProcessesAndTasksOnContentBatch on contentApi with correct parameters', (done) => { + const sourceIds = ['node1;1.0@site', 'node2;1.0@site']; + const source = 'alfresco-1-repoAlfresco'; + + spyOn(service.contentApi, 'getProcessesAndTasksOnContentBatch').and.returnValue(Promise.resolve({ data: [] })); + + service.getProcessesAndTasksOnContentBatch(sourceIds, source).subscribe(() => { + expect(service.contentApi.getProcessesAndTasksOnContentBatch).toHaveBeenCalledWith(sourceIds, source, undefined, undefined); + done(); + }); + }); }); diff --git a/lib/process-services/src/lib/form/services/process-content.service.ts b/lib/process-services/src/lib/form/services/process-content.service.ts index af406673d3..d433b0ec25 100644 --- a/lib/process-services/src/lib/form/services/process-content.service.ts +++ b/lib/process-services/src/lib/form/services/process-content.service.ts @@ -204,6 +204,27 @@ export class ProcessContentService { return from(this.contentApi.getProcessesAndTasksOnContent(sourceId, source, size, page)).pipe(catchError((err) => this.handleError(err))); } + /** + * Batch variant of getProcessesAndTasksOnContent. Accepts multiple source document ids + * in one request (up to 500) and returns the related processes and tasks for all of them. + * + * @param sourceIds - ids of the documents to query process participation for (up to 500) + * @param source - source of the documents that workflows or tasks have been started with + * @param size - size of the entries to get + * @param page - page number + * @returns Observable + */ + getProcessesAndTasksOnContentBatch( + sourceIds: string[], + source: string, + size?: number, + page?: number + ): Observable { + return from(this.contentApi.getProcessesAndTasksOnContentBatch(sourceIds, source, size, page)).pipe( + catchError((err) => this.handleError(err)) + ); + } + /** * Creates a JSON representation of data. *