From 57bf066164b9579c6453c1cf90ff1b1d776e626a Mon Sep 17 00:00:00 2001 From: Vito Albano Date: Thu, 7 May 2026 09:12:07 +0100 Subject: [PATCH] AAE-30882 Remove superagent for fetch (#11856) * [AAE-30882] - Remove superagent for fetch * [AAE-30882] - Fixing comments * [AAE-30882] - fixde failing unit tests * [AAE-30882] - fixde failing unit tests * [AAE-30882] - fixd sonarcloud comment --- lib/js-api/jest.config.ts | 3 +- lib/js-api/package.json | 1 - lib/js-api/src/alfrescoApiClient.ts | 4 +- lib/js-api/src/authentication/oauth2Auth.ts | 9 +- lib/js-api/src/fetchHttpClient.ts | 527 ++++++++ lib/js-api/src/superagentHttpClient.ts | 376 ------ lib/js-api/src/test-fetch-setup.ts | 20 + lib/js-api/test/auth.spec.ts | 8 +- lib/js-api/test/bpmAuth.spec.ts | 8 +- .../test/content-services/nodeApi.spec.ts | 4 +- .../test/content-services/peopleApi.spec.ts | 12 +- lib/js-api/test/fetchHttpClient.spec.ts | 1100 +++++++++++++++++ .../test/jest-jsdom-fetch-environment.ts | 72 ++ lib/js-api/test/mockObjects/base.mock.ts | 103 +- .../content-services/agent.mock.ts | 3 +- .../content-services/categories.mock.ts | 45 +- .../content-services/comment.mock.ts | 5 +- .../content-services/custom-model.mock.ts | 7 +- .../content-services/discovery.mock.ts | 3 +- .../content-services/ecm-auth.mock.ts | 34 +- .../content-services/find-nodes.mock.ts | 11 +- .../content-services/groups.mock.ts | 17 +- .../mockObjects/content-services/node.mock.ts | 29 +- .../content-services/people.mock.ts | 5 +- .../content-services/rendition.mock.ts | 7 +- .../content-services/search-ai.mock.ts | 7 +- .../content-services/search.mock.ts | 3 +- .../mockObjects/content-services/tag.mock.ts | 23 +- .../content-services/upload.mock.ts | 9 +- .../content-services/version.mock.ts | 11 +- .../content-services/webscript.mock.ts | 9 +- .../authority-clearance.mock.ts | 10 +- .../goverance-services/file-plans.mock.ts | 28 +- .../goverance-services/gs-sites.mock.ts | 3 +- .../node-security-marks.mock.ts | 5 +- .../security-groups.mock.ts | 11 +- .../goverance-services/security-marks.mock.ts | 15 +- .../test/mockObjects/oauth2/oauth.mock.ts | 3 +- .../process-services/bpm-auth.mock.ts | 20 +- .../process-services/model-json.mock.ts | 7 +- .../process-services/models.mock.ts | 3 +- .../process-instance-variables.mock.ts | 21 +- .../process-services/process.mock.ts | 5 +- .../process-services/profile.mock.ts | 7 +- .../process-services/reports.mock.ts | 25 +- .../process-services/task-form.mock.ts | 36 +- .../process-services/tasks.mock.ts | 19 +- .../process-services/user-filters.mock.ts | 3 +- lib/js-api/test/oauth2Auth.spec.ts | 7 +- .../test/oauth2AuthImplicitFlow.spec.ts | 46 +- lib/js-api/test/superagentHttpClient.spec.ts | 90 -- lib/js-api/test/upload.spec.ts | 8 +- package-lock.json | 196 +-- package.json | 6 +- 54 files changed, 2127 insertions(+), 922 deletions(-) create mode 100644 lib/js-api/src/fetchHttpClient.ts delete mode 100644 lib/js-api/src/superagentHttpClient.ts create mode 100644 lib/js-api/src/test-fetch-setup.ts create mode 100644 lib/js-api/test/fetchHttpClient.spec.ts create mode 100644 lib/js-api/test/jest-jsdom-fetch-environment.ts delete mode 100644 lib/js-api/test/superagentHttpClient.spec.ts diff --git a/lib/js-api/jest.config.ts b/lib/js-api/jest.config.ts index b29e31ba11..a67579774e 100644 --- a/lib/js-api/jest.config.ts +++ b/lib/js-api/jest.config.ts @@ -2,7 +2,8 @@ export default { displayName: 'js-api', preset: '../../jest.preset.js', - testEnvironment: 'jsdom', + testEnvironment: '/test/jest-jsdom-fetch-environment.ts', + setupFiles: ['/src/test-fetch-setup.ts'], setupFilesAfterEnv: ['/src/test-setup.ts'], collectCoverage: true, coverageReporters: ['html', ['text-summary', { file: 'summary.txt' }], 'text-summary'], diff --git a/lib/js-api/package.json b/lib/js-api/package.json index cd4e4e4d6d..150aaf4ed4 100644 --- a/lib/js-api/package.json +++ b/lib/js-api/package.json @@ -15,7 +15,6 @@ "url": "https://github.com/Alfresco/alfresco-ng2-components/issues" }, "dependencies": { - "superagent": "^9.0.1", "eventemitter3": "^5.0.1", "tslib": "^2.6.1" }, diff --git a/lib/js-api/src/alfrescoApiClient.ts b/lib/js-api/src/alfrescoApiClient.ts index 5b456ec39d..a7a6b1505a 100644 --- a/lib/js-api/src/alfrescoApiClient.ts +++ b/lib/js-api/src/alfrescoApiClient.ts @@ -18,7 +18,7 @@ import { EventEmitter } from 'eventemitter3'; import { AlfrescoApiConfig } from './alfrescoApiConfig'; import { Authentication } from './authentication/authentication'; -import { SuperagentHttpClient } from './superagentHttpClient'; +import { FetchHttpClient } from './fetchHttpClient'; import { Emitters, HttpClient, LegacyHttpClient, RequestOptions, SecurityOptions } from './api-clients/http-client.interface'; import { paramToString } from './utils'; import { Storage } from './storage'; @@ -105,7 +105,7 @@ export class AlfrescoApiClient implements LegacyHttpClient { this.host = host; this.storage = Storage.getInstance(); // fallback for backward compatibility - this.httpClient = httpClient || new SuperagentHttpClient(); + this.httpClient = httpClient || new FetchHttpClient(); } // EventEmitter delegation methods diff --git a/lib/js-api/src/authentication/oauth2Auth.ts b/lib/js-api/src/authentication/oauth2Auth.ts index e9ad8c8b1f..0df2703307 100644 --- a/lib/js-api/src/authentication/oauth2Auth.ts +++ b/lib/js-api/src/authentication/oauth2Auth.ts @@ -24,6 +24,7 @@ import { AlfrescoApi } from '../alfrescoApi'; import { Storage } from '../storage'; import { HttpClient } from '../api-clients/http-client.interface'; import { PathMatcher } from '../utils/path-matcher'; +import { isBrowser } from '../utils'; declare const Buffer: any; @@ -355,7 +356,7 @@ export class Oauth2Auth extends AlfrescoApiClient { } isRedirectionUrl() { - return window.location.hash && window.location.hash.split('&')[0].indexOf('session_state') === -1; + return window.location.hash?.split('&')[0].indexOf('session_state') === -1; } genNonce(): string { @@ -500,7 +501,7 @@ export class Oauth2Auth extends AlfrescoApiClient { } silentRefresh(): void { - if (typeof document === 'undefined') { + if (!isBrowser()) { this.pollingRefreshToken(); return; } @@ -591,7 +592,7 @@ export class Oauth2Auth extends AlfrescoApiClient { resolve(data); }, (error) => { - if ((error.error && error.error.status === 401) || error.status === 401) { + if (error.error?.status === 401 || error.status === 401) { this.emit('unauthorized'); } this.emit('error'); @@ -640,7 +641,7 @@ export class Oauth2Auth extends AlfrescoApiClient { resolve(data); }, (error) => { - if (error.error && error.error.status === 401) { + if (error.error?.status === 401) { this.emit('unauthorized'); } this.emit('error'); diff --git a/lib/js-api/src/fetchHttpClient.ts b/lib/js-api/src/fetchHttpClient.ts new file mode 100644 index 0000000000..03488b45d1 --- /dev/null +++ b/lib/js-api/src/fetchHttpClient.ts @@ -0,0 +1,527 @@ +/*! + * @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 { Authentication } from './authentication/authentication'; +import { RequestOptions, HttpClient, SecurityOptions, Emitters } from './api-clients/http-client.interface'; +import { Oauth2 } from './authentication/oauth2'; +import { BasicAuth } from './authentication/basicAuth'; +import { isBrowser, paramToString } from './utils'; + +declare const Blob: any; +declare const Buffer: any; +declare const process: any; +declare const XMLHttpRequest: any; + +export class FetchHttpClient implements HttpClient { + timeout: number | { deadline?: number; response?: number } = undefined; + private readonly customFetch?: typeof fetch; + + constructor(customFetch?: typeof fetch) { + this.customFetch = customFetch; + } + + private getFetch(): typeof fetch { + // eslint-disable-next-line no-underscore-dangle + return this.customFetch || (typeof process !== 'undefined' && (process as any).__test_fetch__) || globalThis.fetch; + } + + private hasNativeXhr(): boolean { + // eslint-disable-next-line no-underscore-dangle + if (this.customFetch || (typeof process !== 'undefined' && (process as any).__test_fetch__)) { + return false; + } + return typeof XMLHttpRequest !== 'undefined'; + } + + post(url: string, options: RequestOptions, securityOptions: SecurityOptions, emitters: Emitters): Promise { + return this.request(url, { ...options, httpMethod: 'POST' }, securityOptions, emitters); + } + + put(url: string, options: RequestOptions, securityOptions: SecurityOptions, emitters: Emitters): Promise { + return this.request(url, { ...options, httpMethod: 'PUT' }, securityOptions, emitters); + } + + get(url: string, options: RequestOptions, securityOptions: SecurityOptions, emitters: Emitters): Promise { + return this.request(url, { ...options, httpMethod: 'GET' }, securityOptions, emitters); + } + + delete(url: string, options: RequestOptions, securityOptions: SecurityOptions, emitters: Emitters): Promise { + return this.request(url, { ...options, httpMethod: 'DELETE' }, securityOptions, emitters); + } + + request(url: string, options: RequestOptions, securityOptions: SecurityOptions, emitters: Emitters): Promise { + const { httpMethod, queryParams, headerParams, formParams, bodyParam, contentType, accept, responseType, returnType } = options; + + const headers = this.buildHeaders(headerParams, securityOptions, contentType, accept); + const queryString = FetchHttpClient.buildQueryString(queryParams); + const fullUrl = queryString ? `${url}${url.includes('?') ? '&' : '?'}${queryString}` : url; + const body = this.buildBody(contentType, formParams, bodyParam); + const hasBody = body !== undefined && httpMethod !== 'GET' && httpMethod !== 'HEAD'; + const withCredentials = securityOptions.withCredentials || securityOptions.isBpmRequest; + + if (hasBody && this.hasNativeXhr()) { + return this.requestWithXhr(fullUrl, httpMethod, headers, body, withCredentials, returnType, responseType, securityOptions, emitters); + } + + return this.requestWithFetch( + fullUrl, + httpMethod, + headers, + hasBody ? body : undefined, + withCredentials, + returnType, + responseType, + securityOptions, + emitters + ); + } + + private requestWithFetch( + url: string, + method: string, + headers: Record, + body: any, + withCredentials: boolean, + returnType: string, + responseType: string, + securityOptions: SecurityOptions, + emitters: Emitters + ): Promise { + const { eventEmitter } = emitters; + const controller = new AbortController(); + const timeoutMs = typeof this.timeout === 'number' ? this.timeout : this.timeout?.deadline; + + if (timeoutMs) { + if (typeof AbortSignal.timeout === 'function') { + AbortSignal.timeout(timeoutMs).addEventListener('abort', () => controller.abort()); + } else { + setTimeout(() => controller.abort(), timeoutMs); + } + } + + const init: RequestInit = { + method, + headers, + signal: controller.signal, + credentials: withCredentials ? 'include' : 'same-origin' + }; + + if (body !== undefined) { + init.body = body; + } + + const fn = this.getFetch(); + + const promise: any = new Promise((resolve, reject) => { + const execute = async () => { + const response = await fn(url, init); + + if (!response.ok) { + const errorText = await response.text().catch(() => ''); + const error: any = new Error(errorText || response.statusText); + error.status = response.status; + error.response = response; + + FetchHttpClient.emitErrorEvents(error, response.status, emitters); + // eslint-disable-next-line prefer-promise-reject-errors + reject({ error, status: response.status, message: errorText || response.statusText }); + return; + } + + if (securityOptions.isBpmRequest) { + const setCookie = response.headers.get('set-cookie'); + if (setCookie) { + securityOptions.authentications.cookie = setCookie; + } + } + + const data = await this.deserializeResponse(response, returnType, responseType); + eventEmitter.emit('success', data); + resolve(data as T); + }; + + execute().catch((error: any) => { + if (error.name === 'AbortError') { + eventEmitter.emit('abort'); + reject(error); + return; + } + if (!error.status) { + FetchHttpClient.emitErrorEvents(error, 0, emitters); + } + // eslint-disable-next-line prefer-promise-reject-errors + reject(error.status ? { error, status: error.status, message: error.message } : { error }); + }); + }); + + promise.abort = () => { + controller.abort(); + return promise; + }; + + return promise; + } + + private requestWithXhr( + url: string, + method: string, + headers: Record, + body: any, + withCredentials: boolean, + returnType: string, + responseType: string, + securityOptions: SecurityOptions, + emitters: Emitters + ): Promise { + const { eventEmitter } = emitters; + const timeoutMs = typeof this.timeout === 'number' ? this.timeout : this.timeout?.deadline; + let xhr: any; + + const promise: any = new Promise((resolve, reject) => { + xhr = new XMLHttpRequest(); + xhr.open(method, url, true); + + for (const [key, value] of Object.entries(headers)) { + xhr.setRequestHeader(key, value); + } + + if (withCredentials) { + xhr.withCredentials = true; + } + + if (timeoutMs) { + xhr.timeout = timeoutMs; + } + + if (returnType === 'blob' || returnType === 'Blob' || responseType === 'blob' || responseType === 'Blob' || returnType === 'Binary') { + xhr.responseType = 'blob'; + } + + xhr.upload.onprogress = (event: any) => { + if (event.lengthComputable) { + const percent = Math.round((event.loaded / event.total) * 100); + eventEmitter.emit('progress', { total: event.total, loaded: event.loaded, percent }); + } + }; + + xhr.onload = () => { + if (xhr.status >= 200 && xhr.status < 300) { + if (securityOptions.isBpmRequest) { + const setCookie = xhr.getResponseHeader('set-cookie'); + if (setCookie) { + securityOptions.authentications.cookie = setCookie; + } + } + + const data = this.deserializeXhrResponse(xhr, returnType, responseType); + eventEmitter.emit('success', data); + resolve(data as T); + } else { + const errorText = xhr.responseText || xhr.statusText; + const error: any = new Error(errorText); + error.status = xhr.status; + error.message = errorText; + + FetchHttpClient.emitErrorEvents(error, xhr.status, emitters); + // eslint-disable-next-line prefer-promise-reject-errors + reject({ error, status: xhr.status, message: errorText }); + } + }; + + xhr.onerror = () => { + const error = new Error('Network request failed'); + FetchHttpClient.emitErrorEvents(error, 0, emitters); + // eslint-disable-next-line prefer-promise-reject-errors + reject({ error }); + }; + + xhr.onabort = () => { + eventEmitter.emit('abort'); + reject(new DOMException('The operation was aborted.', 'AbortError')); + }; + + xhr.ontimeout = () => { + const error = new Error('Request timed out'); + FetchHttpClient.emitErrorEvents(error, 0, emitters); + // eslint-disable-next-line prefer-promise-reject-errors + reject({ error }); + }; + + xhr.send(body); + }); + + promise.abort = () => { + if (xhr) { + xhr.abort(); + } + return promise; + }; + + return promise; + } + + private deserializeXhrResponse(xhr: any, returnType: string, responseType: string): any { + if (returnType === 'blob' || returnType === 'Blob' || responseType === 'blob' || responseType === 'Blob' || returnType === 'Binary') { + return xhr.response; + } + + if (returnType === 'String') { + return xhr.responseText; + } + + const contentType = xhr.getResponseHeader('content-type') || ''; + if (contentType.includes('text/html')) { + return xhr.responseText; + } + + try { + const text = xhr.responseText; + if (!text) { + return {}; + } + const data = JSON.parse(text); + if (returnType && Array.isArray(data)) { + return data.map((element: any) => new (returnType as any)(element)); + } + if (returnType && typeof returnType === 'function') { + return new (returnType as any)(data); + } + return data; + } catch { + return xhr.responseText || ''; + } + } + + setCsrfToken(headers: Record): void { + const token = FetchHttpClient.createCSRFToken(); + headers['X-CSRF-TOKEN'] = token; + + if (!isBrowser()) { + headers['Cookie'] = 'CSRF-TOKEN=' + token + ';path=/'; + } + + try { + document.cookie = 'CSRF-TOKEN=' + token + ';path=/'; + } catch { + /* continue regardless of error */ + } + } + + private buildHeaders( + headerParams: Record, + securityOptions: SecurityOptions, + contentType: string, + accept: string + ): Record { + const headers: Record = { + ...securityOptions.defaultHeaders, + ...FetchHttpClient.normalizeParams(headerParams) + }; + + this.applyAuthHeaders(headers, securityOptions.authentications); + + if (securityOptions.isBpmRequest && securityOptions.enableCsrf) { + this.setCsrfToken(headers); + } + + if (securityOptions.isBpmRequest && securityOptions.authentications.cookie && !isBrowser()) { + headers['Cookie'] = headers['Cookie'] + ? headers['Cookie'] + '; ' + securityOptions.authentications.cookie + : securityOptions.authentications.cookie; + } + + if (contentType && contentType !== 'multipart/form-data') { + headers['Content-Type'] = contentType; + } else if (contentType !== 'multipart/form-data' && !headers['Content-Type']) { + headers['Content-Type'] = 'application/json'; + } + + if (accept) { + headers['Accept'] = accept; + } + + return headers; + } + + private buildBody(contentType: string, formParams: Record, bodyParam: string | object): any { + if (contentType === 'application/x-www-form-urlencoded') { + const params = FetchHttpClient.normalizeParams(formParams); + return new URLSearchParams(params).toString(); + } + + if (contentType === 'multipart/form-data') { + const normalizedParams = FetchHttpClient.normalizeParams(formParams); + const formData = new FormData(); + for (const [key, value] of Object.entries(normalizedParams)) { + formData.append(key, value as any); + } + return formData; + } + + if (bodyParam) { + return typeof bodyParam === 'string' ? bodyParam : JSON.stringify(bodyParam); + } + + return undefined; + } + + private static emitErrorEvents(error: any, status: number, emitters: Emitters): void { + const { eventEmitter, apiClientEmitter } = emitters; + + apiClientEmitter.emit('error', error); + eventEmitter.emit('error', error); + + if (status === 401) { + apiClientEmitter.emit('unauthorized'); + eventEmitter.emit('unauthorized'); + } + + if (status === 403) { + apiClientEmitter.emit('forbidden'); + eventEmitter.emit('forbidden'); + } + } + + private applyAuthHeaders(headers: Record, authentications: Authentication): void { + if (!authentications) { + return; + } + + switch (authentications.type) { + case 'basic': { + const basicAuth: BasicAuth = authentications.basicAuth; + if (basicAuth.username || basicAuth.password) { + const encoded = + typeof btoa === 'function' + ? btoa((basicAuth.username || '') + ':' + (basicAuth.password || '')) + : Buffer.from((basicAuth.username || '') + ':' + (basicAuth.password || '')).toString('base64'); + headers['Authorization'] = 'Basic ' + encoded; + } + break; + } + case 'activiti': { + if (authentications.basicAuth.ticket) { + headers['Authorization'] = authentications.basicAuth.ticket; + } + break; + } + case 'oauth2': { + const oauth2: Oauth2 = authentications.oauth2; + if (oauth2.accessToken) { + headers['Authorization'] = 'Bearer ' + oauth2.accessToken; + } + break; + } + default: + throw new Error('Unknown authentication type: ' + authentications.type); + } + } + + private async deserializeResponse(response: Response, returnType: string, responseType: string): Promise { + if (returnType === 'blob' || returnType === 'Blob' || responseType === 'blob' || responseType === 'Blob' || returnType === 'Binary') { + const blob = await response.blob(); + if (isBrowser()) { + return blob; + } + const arrayBuffer = await blob.arrayBuffer(); + return Buffer.from(arrayBuffer); + } + + if (returnType === 'String') { + return response.text(); + } + + const contentTypeHeader = response.headers.get('content-type') || ''; + if (contentTypeHeader.includes('text/html')) { + return response.text(); + } + + const text = await response.text(); + if (!text) { + return {}; + } + try { + const data = JSON.parse(text); + if (returnType && Array.isArray(data)) { + return data.map((element: any) => new (returnType as any)(element)); + } + if (returnType && typeof returnType === 'function') { + return new (returnType as any)(data); + } + return data; + } catch { + return text; + } + } + + private static createCSRFToken(): string { + if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { + return crypto.randomUUID(); + } + const bytes = new Uint8Array(16); + crypto.getRandomValues(bytes); + bytes[6] = (bytes[6] & 0x0f) | 0x40; + bytes[8] = (bytes[8] & 0x3f) | 0x80; + const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`; + } + + private static buildQueryString(params: Record): string { + if (!params) { + return ''; + } + const normalized = FetchHttpClient.normalizeParams(params); + const searchParams = new URLSearchParams(); + for (const [key, value] of Object.entries(normalized)) { + if (Array.isArray(value)) { + value.forEach((v: any) => searchParams.append(key, v)); + } else { + searchParams.append(key, value); + } + } + return searchParams.toString(); + } + + private static normalizeParams(params: Record): Record { + if (!params) { + return {}; + } + const newParams: Record = {}; + for (const [key, value] of Object.entries(params)) { + if (value != null) { + newParams[key] = FetchHttpClient.isFileParam(value) || Array.isArray(value) ? value : paramToString(value); + } + } + return newParams; + } + + private static isFileParam(param: any): boolean { + if (typeof Buffer === 'function' && (param instanceof Buffer || param?.path)) { + return true; + } + if (typeof Blob === 'function' && param instanceof Blob) { + return true; + } + if (typeof File === 'function' && param instanceof File) { + return true; + } + if (typeof File === 'object' && param instanceof File) { + return true; + } + return false; + } +} diff --git a/lib/js-api/src/superagentHttpClient.ts b/lib/js-api/src/superagentHttpClient.ts deleted file mode 100644 index 721e408f2a..0000000000 --- a/lib/js-api/src/superagentHttpClient.ts +++ /dev/null @@ -1,376 +0,0 @@ -/*! - * @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 superagent, { Response, SuperAgentRequest } from 'superagent'; -import { Authentication } from './authentication/authentication'; -import { RequestOptions, HttpClient, SecurityOptions, Emitters } from './api-clients/http-client.interface'; -import { Oauth2 } from './authentication/oauth2'; -import { BasicAuth } from './authentication/basicAuth'; -import { isBrowser, paramToString } from './utils'; -import { EventEmitterInstance } from './types'; - -declare const Blob: any; -declare const Buffer: any; - -const isProgressEvent = (event: ProgressEvent | unknown): event is ProgressEvent => (event as ProgressEvent)?.lengthComputable; - -export class SuperagentHttpClient implements HttpClient { - /** - * The default HTTP timeout for all API calls. - */ - timeout: number | { deadline?: number; response?: number } = undefined; - - post(url: string, options: RequestOptions, securityOptions: SecurityOptions, emitters: Emitters): Promise { - return this.request(url, { ...options, httpMethod: 'POST' }, securityOptions, emitters); - } - - put(url: string, options: RequestOptions, securityOptions: SecurityOptions, emitters: Emitters): Promise { - return this.request(url, { ...options, httpMethod: 'PUT' }, securityOptions, emitters); - } - - get(url: string, options: RequestOptions, securityOptions: SecurityOptions, emitters: Emitters): Promise { - return this.request(url, { ...options, httpMethod: 'GET' }, securityOptions, emitters); - } - - delete(url: string, options: RequestOptions, securityOptions: SecurityOptions, emitters: Emitters): Promise { - return this.request(url, { ...options, httpMethod: 'DELETE' }, securityOptions, emitters); - } - - request(url: string, options: RequestOptions, securityOptions: SecurityOptions, emitters: Emitters): Promise { - const { httpMethod, queryParams, headerParams, formParams, bodyParam, contentType, accept, responseType, returnType } = options; - const { eventEmitter, apiClientEmitter } = emitters; - - let request = this.buildRequest( - httpMethod, - url, - queryParams, - headerParams, - formParams, - bodyParam, - contentType, - accept, - responseType, - eventEmitter, - returnType, - securityOptions - ); - - if (returnType === 'Binary') { - request = request.buffer(true).parse(superagent.parse['application/octet-stream']); - } - - const promise: any = new Promise((resolve, reject) => { - request.on('abort', () => { - eventEmitter.emit('abort'); - }); - request.end((error: any, response: Response) => { - if (error) { - apiClientEmitter.emit('error', error); - eventEmitter.emit('error', error); - - if (error.status === 401) { - apiClientEmitter.emit('unauthorized'); - eventEmitter.emit('unauthorized'); - } - - if (response?.text) { - error = error || {}; - reject(Object.assign(error, { message: response.text })); - } else { - // eslint-disable-next-line prefer-promise-reject-errors - reject({ error }); - } - } else { - if (securityOptions.isBpmRequest) { - const hasSetCookie = Object.prototype.hasOwnProperty.call(response.header, 'set-cookie'); - if (response.header && hasSetCookie) { - // mutate the passed value from AlfrescoApiClient class for backward compatibility - securityOptions.authentications.cookie = response.header['set-cookie'][0]; - } - } - let data = {}; - if (response.type === 'text/html') { - data = SuperagentHttpClient.deserialize(response); - } else { - data = SuperagentHttpClient.deserialize(response, returnType); - } - - eventEmitter.emit('success', data); - resolve(data); - } - }); - }); - - promise.abort = function () { - request.abort(); - return this; - }; - - return promise; - } - - private buildRequest( - httpMethod: string, - url: string, - queryParams: { [key: string]: any }, - headerParams: { [key: string]: any }, - formParams: { [key: string]: any }, - bodyParam: string | object, - contentType: string, - accept: string, - responseType: string, - eventEmitter: EventEmitterInstance, - returnType: string, - securityOptions: SecurityOptions - ) { - const request = superagent(httpMethod, url); - - const { isBpmRequest, authentications, defaultHeaders = {}, enableCsrf, withCredentials = false } = securityOptions; - - // apply authentications - this.applyAuthToRequest(request, authentications); - - // set query parameters - request.query(SuperagentHttpClient.normalizeParams(queryParams)); - - // set header parameters - request.set(defaultHeaders).set(SuperagentHttpClient.normalizeParams(headerParams)); - - if (isBpmRequest && enableCsrf) { - this.setCsrfToken(request); - } - - if (withCredentials) { - request.withCredentials(); - } - - // add cookie for activiti - if (isBpmRequest) { - request.withCredentials(); - if (securityOptions.authentications.cookie) { - if (!isBrowser()) { - request.set('Cookie', securityOptions.authentications.cookie); - } - } - } - - // set request timeout - request.timeout(this.timeout); - - if (contentType && contentType !== 'multipart/form-data') { - request.type(contentType); - } else if (!(request as any).header['Content-Type'] && contentType !== 'multipart/form-data') { - request.type('application/json'); - } - - if (contentType === 'application/x-www-form-urlencoded') { - request.send(SuperagentHttpClient.normalizeParams(formParams)).on('progress', (event: any) => { - this.progress(event, eventEmitter); - }); - } else if (contentType === 'multipart/form-data') { - const _formParams = SuperagentHttpClient.normalizeParams(formParams); - for (const key in _formParams) { - if (Object.prototype.hasOwnProperty.call(_formParams, key)) { - if (SuperagentHttpClient.isFileParam(_formParams[key])) { - // file field - request.attach(key, _formParams[key]).on('progress', (event: ProgressEvent) => { - // jshint ignore:line - this.progress(event, eventEmitter); - }); - } else { - request.field(key, _formParams[key]).on('progress', (event: ProgressEvent) => { - // jshint ignore:line - this.progress(event, eventEmitter); - }); - } - } - } - } else if (bodyParam) { - request.send(bodyParam).on('progress', (event: any) => { - this.progress(event, eventEmitter); - }); - } - - if (accept) { - request.accept(accept); - } - - if (returnType === 'blob' || returnType === 'Blob' || responseType === 'blob' || responseType === 'Blob') { - request.responseType('blob'); - } else if (returnType === 'String') { - request.responseType('string'); - } - - return request; - } - - setCsrfToken(request: SuperAgentRequest): void { - const token = SuperagentHttpClient.createCSRFToken(); - request.set('X-CSRF-TOKEN', token); - - if (!isBrowser()) { - request.set('Cookie', 'CSRF-TOKEN=' + token + ';path=/'); - } - - try { - document.cookie = 'CSRF-TOKEN=' + token + ';path=/'; - } catch { - /* continue regardless of error */ - } - } - - /** - * Applies authentication headers to the request. - * @param request The request object created by a superagent() call. - * @param authentications authentications - */ - private applyAuthToRequest(request: SuperAgentRequest, authentications: Authentication) { - if (authentications) { - switch (authentications.type) { - case 'basic': { - const basicAuth: BasicAuth = authentications.basicAuth; - if (basicAuth.username || basicAuth.password) { - request.auth(basicAuth.username || '', basicAuth.password || ''); - } - break; - } - case 'activiti': { - if (authentications.basicAuth.ticket) { - request.set({ Authorization: authentications.basicAuth.ticket }); - } - break; - } - case 'oauth2': { - const oauth2: Oauth2 = authentications.oauth2; - if (oauth2.accessToken) { - request.set({ Authorization: 'Bearer ' + oauth2.accessToken }); - } - break; - } - default: - throw new Error('Unknown authentication type: ' + authentications.type); - } - } - } - - private progress(event: ProgressEvent | unknown, eventEmitter: EventEmitterInstance): void { - if (isProgressEvent(event)) { - const percent = Math.round((event.loaded / event.total) * 100); - - const progress = { - total: event.total, - loaded: event.loaded, - percent - }; - - eventEmitter.emit('progress', progress); - } - } - - private static createCSRFToken(a?: any): string { - return a - ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) - : ([1e16] + (1e16).toString()).replace(/[01]/g, SuperagentHttpClient.createCSRFToken); - } - - /** - * Deserializes an HTTP response body into a value of the specified type. - * @param response A SuperAgent response object. - * @param returnType The type to return. Pass a string for simple types - * or the constructor function for a complex type. Pass an array containing the type name to return an array of that type. To - * return an object, pass an object with one property whose name is the key type and whose value is the corresponding value type: - * all properties on data will be converted to this type. - * @returns A value of the specified type. - */ - private static deserialize(response: Response, returnType?: any): any { - if (response === null) { - return null; - } - - let data = response.body; - - if (data === null) { - data = response.text; - } - - if (returnType) { - if (returnType === 'blob' && isBrowser()) { - data = new Blob([data], { type: response.header['content-type'] }); - } else if (returnType === 'blob' && !isBrowser()) { - data = new Buffer.from(data, 'binary'); - } else if (Array.isArray(data)) { - data = data.map((element) => new returnType(element)); - } else { - data = new returnType(data); - } - } - - return data; - } - - /** - * Normalizes parameter values: - *
    - *
  • remove nils
  • - *
  • keep files and arrays
  • - *
  • format to string with `paramToString` for other cases
  • - *
- * @param params The parameters as object properties. - * @returns normalized parameters. - */ - private static normalizeParams(params: { [key: string]: any }): { [key: string]: any } { - const newParams: { [key: string]: any } = {}; - - for (const key in params) { - if (Object.prototype.hasOwnProperty.call(params, key) && params[key] != null) { - const value = params[key]; - if (SuperagentHttpClient.isFileParam(value) || Array.isArray(value)) { - newParams[key] = value; - } else { - newParams[key] = paramToString(value); - } - } - } - return newParams; - } - - /** - * Checks whether the given parameter value represents file-like content. - * @param param The parameter to check. - * @returns true if param represents a file. - */ - private static isFileParam(param: any): boolean { - // Buffer in Node.js - if (typeof Buffer === 'function' && (param instanceof Buffer || param.path)) { - return true; - } - // Blob in browser - if (typeof Blob === 'function' && param instanceof Blob) { - return true; - } - // File in browser (it seems File object is also instance of Blob, but keep this for safe) - if (typeof File === 'function' && param instanceof File) { - return true; - } - // Safari fix - if (typeof File === 'object' && param instanceof File) { - return true; - } - - return false; - } -} diff --git a/lib/js-api/src/test-fetch-setup.ts b/lib/js-api/src/test-fetch-setup.ts new file mode 100644 index 0000000000..c0a1ca5811 --- /dev/null +++ b/lib/js-api/src/test-fetch-setup.ts @@ -0,0 +1,20 @@ +/*! + * @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 { getGlobalMockAgent } from '../test/mockObjects/base.mock'; + +getGlobalMockAgent(); diff --git a/lib/js-api/test/auth.spec.ts b/lib/js-api/test/auth.spec.ts index 52c575756c..73aa1a256d 100644 --- a/lib/js-api/test/auth.spec.ts +++ b/lib/js-api/test/auth.spec.ts @@ -419,24 +419,20 @@ describe('Auth', () => { it('should fail if only ECM fail', (done) => { authResponseBpmMock.get200Response(); - authResponseEcmMock.get401Response(); + authResponseEcmMock.get401ResponseAdminCredentials(); alfrescoJsApi.login('admin', 'admin').then(NOOP, () => { done(); }); - - authResponseEcmMock.cleanAll(); }); it('should fail if only BPM fail', (done) => { - authResponseBpmMock.get401Response(); + authResponseBpmMock.get401ResponseAdminCredentials(); authResponseEcmMock.get201Response(); alfrescoJsApi.login('admin', 'admin').then(NOOP, () => { done(); }); - - authResponseBpmMock.cleanAll(); }); }); diff --git a/lib/js-api/test/bpmAuth.spec.ts b/lib/js-api/test/bpmAuth.spec.ts index 14e03903f4..aeb8ea549e 100644 --- a/lib/js-api/test/bpmAuth.spec.ts +++ b/lib/js-api/test/bpmAuth.spec.ts @@ -17,7 +17,7 @@ import assert from 'assert'; import { ProcessAuth } from '../src'; -import { SuperagentHttpClient } from '../src/superagentHttpClient'; +import { FetchHttpClient } from '../src/fetchHttpClient'; import { BpmAuthMock } from './mockObjects'; describe('Bpm Auth test', () => { @@ -256,16 +256,16 @@ describe('Bpm Auth test', () => { let setCsrfTokenCalled = false; beforeEach(() => { - originalMethod = SuperagentHttpClient.prototype.setCsrfToken; + originalMethod = FetchHttpClient.prototype.setCsrfToken; setCsrfTokenCalled = false; - SuperagentHttpClient.prototype.setCsrfToken = () => { + FetchHttpClient.prototype.setCsrfToken = () => { setCsrfTokenCalled = true; }; }); afterEach(() => { - SuperagentHttpClient.prototype.setCsrfToken = originalMethod; + FetchHttpClient.prototype.setCsrfToken = originalMethod; setCsrfTokenCalled = false; }); diff --git a/lib/js-api/test/content-services/nodeApi.spec.ts b/lib/js-api/test/content-services/nodeApi.spec.ts index 95fde66f95..4b00c7b404 100644 --- a/lib/js-api/test/content-services/nodeApi.spec.ts +++ b/lib/js-api/test/content-services/nodeApi.spec.ts @@ -167,7 +167,7 @@ describe('Node', () => { nodesApi.initiateFolderSizeCalculation('b4cff62a-664d-4d45-9302-98723eac1319').then( () => {}, (err) => { - const { error } = JSON.parse(err.response.text); + const { error } = JSON.parse(err.message); assert.equal(error.statusCode, 404); assert.equal(error.errorKey, 'framework.exception.EntityNotFound'); assert.equal(error.briefSummary, '11207522 The entity with id: b4cff62a-664d-4d45-9302-98723eac1319 was not found'); @@ -196,7 +196,7 @@ describe('Node', () => { nodesApi.getFolderSizeInfo('b4cff62a-664d-4d45-9302-98723eac1319', '5ade426e-8a04-4d50-9e42-6e8a041d50f3').then( () => {}, (err) => { - const { error } = JSON.parse(err.response.text); + const { error } = JSON.parse(err.message); assert.equal(error.statusCode, 404); assert.equal(error.errorKey, 'jobId does not exist'); assert.equal(error.briefSummary, '11207212 jobId does not exist'); diff --git a/lib/js-api/test/content-services/peopleApi.spec.ts b/lib/js-api/test/content-services/peopleApi.spec.ts index 998e149244..cb5444cd53 100644 --- a/lib/js-api/test/content-services/peopleApi.spec.ts +++ b/lib/js-api/test/content-services/peopleApi.spec.ts @@ -60,9 +60,13 @@ describe('PeopleApi', () => { it('should get list of people', (done) => { peopleMock.get200ResponsePersons(); - peopleApi.listPeople().then(() => { - peopleMock.play(); - done(); - }); + peopleApi.listPeople().then( + () => { + done(); + }, + (err) => { + done(new Error('listPeople rejected: ' + JSON.stringify(err))); + } + ); }); }); diff --git a/lib/js-api/test/fetchHttpClient.spec.ts b/lib/js-api/test/fetchHttpClient.spec.ts new file mode 100644 index 0000000000..d0e1779ab9 --- /dev/null +++ b/lib/js-api/test/fetchHttpClient.spec.ts @@ -0,0 +1,1100 @@ +/*! + * @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. + */ + +/* eslint-disable jsdoc/require-jsdoc, no-underscore-dangle */ +import { FetchHttpClient } from '../src/fetchHttpClient'; +import { EventEmitter } from 'eventemitter3'; +import { getGlobalMockAgent, mockHost } from './mockObjects/base.mock'; + +describe('FetchHttpClient', () => { + const host = 'https://127.0.0.1:8080'; + let client: FetchHttpClient; + let eventEmitter: EventEmitter; + let apiClientEmitter: EventEmitter; + let emitters: any; + + const defaultSecurityOptions: any = { + isBpmRequest: false, + enableCsrf: false, + withCredentials: false, + authentications: { + basicAuth: { ticket: '' }, + type: 'basic' + }, + defaultHeaders: {} + }; + + beforeEach(() => { + client = new FetchHttpClient(); + eventEmitter = new EventEmitter(); + apiClientEmitter = new EventEmitter(); + emitters = { eventEmitter, apiClientEmitter }; + }); + + describe('#setCsrfToken', () => { + it('should set X-CSRF-TOKEN header', () => { + const headers: Record = {}; + client.setCsrfToken(headers); + expect(headers['X-CSRF-TOKEN']).toBeTruthy(); + expect(headers['X-CSRF-TOKEN'].length).toBeGreaterThan(0); + }); + }); + + describe('#get', () => { + it('should perform a GET request and return JSON', async () => { + mockHost(host).get('/api/test').reply(200, { id: 1, name: 'test' }); + + const result = await client.get( + host + '/api/test', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toEqual({ id: 1, name: 'test' }); + }); + + it('should emit success event', async () => { + mockHost(host).get('/api/test').reply(200, { ok: true }); + const successSpy = jest.fn(); + eventEmitter.on('success', successSpy); + + await client.get( + host + '/api/test', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(successSpy).toHaveBeenCalledWith({ ok: true }); + }); + + it('should append query parameters', async () => { + mockHost(host).get('/api/test?foo=bar&num=42').reply(200, { ok: true }); + + const result = await client.get( + host + '/api/test', + { + httpMethod: 'GET', + queryParams: { foo: 'bar', num: 42 }, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toEqual({ ok: true }); + }); + + it('should append query parameters with & when URL already contains ?', async () => { + mockHost(host).get('/api/test?existing=1&foo=bar').reply(200, { ok: true }); + + const result = await client.get( + host + '/api/test?existing=1', + { + httpMethod: 'GET', + queryParams: { foo: 'bar' }, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toEqual({ ok: true }); + }); + + it('should return text for String returnType', async () => { + mockHost(host).get('/api/text').reply(200, 'plain text', { 'content-type': 'text/plain' }); + + const result = await client.get( + host + '/api/text', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'text/plain', + responseType: null, + returnType: 'String' + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toBe('plain text'); + }); + }); + + describe('#post', () => { + it('should perform a POST request with JSON body', async () => { + mockHost(host).post('/api/items', { name: 'new' }).reply(201, { id: 2, name: 'new' }); + + const result = await client.post( + host + '/api/items', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { name: 'new' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toEqual({ id: 2, name: 'new' }); + }); + + it('should send form-urlencoded body', async () => { + mockHost(host).post('/api/login', 'username=admin&password=admin').reply(200, { ticket: 'abc' }); + + const result = await client.post( + host + '/api/login', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: { username: 'admin', password: 'admin' }, + bodyParam: null, + contentType: 'application/x-www-form-urlencoded', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toEqual({ ticket: 'abc' }); + }); + }); + + describe('#put', () => { + it('should perform a PUT request', async () => { + mockHost(host).put('/api/items/1', { name: 'updated' }).reply(200, { id: 1, name: 'updated' }); + + const result = await client.put( + host + '/api/items/1', + { + httpMethod: 'PUT', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { name: 'updated' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toEqual({ id: 1, name: 'updated' }); + }); + }); + + describe('#delete', () => { + it('should perform a DELETE request', async () => { + mockHost(host).delete('/api/items/1').reply(204, ''); + + const result = await client.delete( + host + '/api/items/1', + { + httpMethod: 'DELETE', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toEqual({}); + }); + }); + + describe('error handling', () => { + it('should emit error and reject on 500', async () => { + mockHost(host).get('/api/fail').reply(500, 'Internal Server Error', { 'content-type': 'text/plain' }); + const errorSpy = jest.fn(); + eventEmitter.on('error', errorSpy); + + await expect( + client.get( + host + '/api/fail', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ) + ).rejects.toEqual(expect.objectContaining({ status: 500 })); + + expect(errorSpy).toHaveBeenCalled(); + }); + + it('should emit unauthorized on 401', async () => { + mockHost(host).get('/api/secure').reply(401, 'Unauthorized', { 'content-type': 'text/plain' }); + const unauthorizedSpy = jest.fn(); + eventEmitter.on('unauthorized', unauthorizedSpy); + + await expect( + client.get( + host + '/api/secure', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ) + ).rejects.toEqual(expect.objectContaining({ status: 401 })); + + expect(unauthorizedSpy).toHaveBeenCalled(); + }); + + it('should emit forbidden on 403', async () => { + mockHost(host).get('/api/forbidden').reply(403, 'Forbidden', { 'content-type': 'text/plain' }); + const forbiddenSpy = jest.fn(); + eventEmitter.on('forbidden', forbiddenSpy); + + await expect( + client.get( + host + '/api/forbidden', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ) + ).rejects.toEqual(expect.objectContaining({ status: 403 })); + + expect(forbiddenSpy).toHaveBeenCalled(); + }); + }); + + describe('authentication headers', () => { + it('should add Basic auth header', async () => { + mockHost(host).get('/api/test').reply(200, { ok: true }); + + const securityOptions: any = { + ...defaultSecurityOptions, + authentications: { + type: 'basic', + basicAuth: { username: 'admin', password: 'admin' } + } + }; + + await client.get( + host + '/api/test', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + securityOptions, + emitters + ); + }); + + it('should add Bearer token for oauth2', async () => { + mockHost(host).get('/api/test').reply(200, { ok: true }); + + const securityOptions: any = { + ...defaultSecurityOptions, + authentications: { + type: 'oauth2', + oauth2: { accessToken: 'my-token' } + } + }; + + await client.get( + host + '/api/test', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + securityOptions, + emitters + ); + }); + + it('should add activiti ticket header', async () => { + mockHost(host).get('/api/test').reply(200, { ok: true }); + + const securityOptions: any = { + ...defaultSecurityOptions, + authentications: { + type: 'activiti', + basicAuth: { ticket: 'TICKET_abc123' } + } + }; + + await client.get( + host + '/api/test', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + securityOptions, + emitters + ); + }); + + it('should throw for unknown auth type', () => { + const securityOptions: any = { + ...defaultSecurityOptions, + authentications: { type: 'unknown' } + }; + + expect(() => + client.get( + host + '/api/test', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + securityOptions, + emitters + ) + ).toThrow('Unknown authentication type: unknown'); + }); + }); + + describe('abort', () => { + it('should support aborting a request', async () => { + mockHost(host).get('/api/slow').reply(200, { ok: true }); + const abortSpy = jest.fn(); + eventEmitter.on('abort', abortSpy); + + const promise = client.get( + host + '/api/slow', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + (promise as any).abort(); + + await expect(promise).rejects.toBeTruthy(); + }); + }); + + describe('timeout', () => { + it('should accept a numeric timeout', () => { + client.timeout = 5000; + expect(client.timeout).toBe(5000); + }); + + it('should accept an object timeout', () => { + client.timeout = { deadline: 10000, response: 5000 }; + expect((client.timeout as any).deadline).toBe(10000); + }); + }); + + describe('BPM request handling', () => { + it('should store set-cookie for BPM requests', async () => { + mockHost(host).get('/api/bpm').reply(200, { ok: true }, { 'set-cookie': 'JSESSIONID=abc123' }); + + const securityOptions: any = { + ...defaultSecurityOptions, + isBpmRequest: true + }; + + await client.get( + host + '/api/bpm', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + securityOptions, + emitters + ); + + expect(securityOptions.authentications.cookie).toBe('JSESSIONID=abc123'); + }); + + it('should not overwrite existing Cookie header when session cookie is appended', () => { + const headers: Record = { Cookie: 'CSRF-TOKEN=abc;path=/' }; + const sessionCookie = 'JSESSIONID=xyz'; + + headers['Cookie'] = headers['Cookie'] ? headers['Cookie'] + '; ' + sessionCookie : sessionCookie; + + expect(headers['Cookie']).toContain('CSRF-TOKEN=abc'); + expect(headers['Cookie']).toContain('JSESSIONID=xyz'); + }); + }); + + describe('deserialization', () => { + it('should return empty object for empty response', async () => { + mockHost(host).get('/api/empty').reply(200, ''); + + const result = await client.get( + host + '/api/empty', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toEqual({}); + }); + + it('should return text for HTML content-type', async () => { + mockHost(host).get('/api/html').reply(200, 'test', { 'content-type': 'text/html' }); + + const result = await client.get( + host + '/api/html', + { + httpMethod: 'GET', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: null, + contentType: 'application/json', + accept: 'text/html', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toBe('test'); + }); + }); + + describe('XHR path', () => { + let xhrClient: FetchHttpClient; + let mockXhr: any; + + function createMockXhr() { + const xhr: any = { + open: jest.fn(), + send: jest.fn(), + setRequestHeader: jest.fn(), + getResponseHeader: jest.fn(), + abort: jest.fn(), + upload: {}, + readyState: 0, + status: 0, + responseText: '', + response: null, + withCredentials: false, + timeout: 0, + responseType: '' + }; + xhr.send.mockImplementation(() => { + setTimeout(() => { + if (xhr.onload) { + xhr.onload(); + } + }, 0); + }); + return xhr; + } + + beforeEach(() => { + xhrClient = new FetchHttpClient(); + mockXhr = createMockXhr(); + (globalThis as any).XMLHttpRequest = jest.fn(() => mockXhr); + delete (process as any).__test_fetch__; + }); + + afterEach(() => { + delete (globalThis as any).XMLHttpRequest; + getGlobalMockAgent(); + }); + + it('should use XHR for POST requests when XMLHttpRequest is available', async () => { + mockXhr.status = 200; + mockXhr.responseText = JSON.stringify({ created: true }); + mockXhr.getResponseHeader.mockReturnValue('application/json'); + + const result = await xhrClient.post( + host + '/api/items', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { name: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toEqual({ created: true }); + expect(mockXhr.open).toHaveBeenCalledWith('POST', host + '/api/items', true); + expect(mockXhr.send).toHaveBeenCalled(); + }); + + it('should emit progress events from XHR upload', async () => { + mockXhr.status = 200; + mockXhr.responseText = JSON.stringify({ ok: true }); + mockXhr.getResponseHeader.mockReturnValue('application/json'); + mockXhr.send.mockImplementation(() => { + if (mockXhr.upload.onprogress) { + mockXhr.upload.onprogress({ lengthComputable: true, loaded: 50, total: 100 }); + mockXhr.upload.onprogress({ lengthComputable: true, loaded: 100, total: 100 }); + } + setTimeout(() => mockXhr.onload(), 0); + }); + + const progressSpy = jest.fn(); + eventEmitter.on('progress', progressSpy); + + await xhrClient.post( + host + '/api/upload', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'content' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(progressSpy).toHaveBeenCalledTimes(2); + expect(progressSpy).toHaveBeenCalledWith({ total: 100, loaded: 50, percent: 50 }); + expect(progressSpy).toHaveBeenCalledWith({ total: 100, loaded: 100, percent: 100 }); + }); + + it('should emit error and reject on XHR error status', async () => { + mockXhr.status = 500; + mockXhr.responseText = 'Server Error'; + mockXhr.statusText = 'Internal Server Error'; + + const errorSpy = jest.fn(); + eventEmitter.on('error', errorSpy); + + await expect( + xhrClient.post( + host + '/api/fail', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ) + ).rejects.toEqual(expect.objectContaining({ status: 500 })); + + expect(errorSpy).toHaveBeenCalled(); + }); + + it('should emit unauthorized on XHR 401', async () => { + mockXhr.status = 401; + mockXhr.responseText = 'Unauthorized'; + + const unauthorizedSpy = jest.fn(); + eventEmitter.on('unauthorized', unauthorizedSpy); + + await expect( + xhrClient.post( + host + '/api/secure', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ) + ).rejects.toEqual(expect.objectContaining({ status: 401 })); + + expect(unauthorizedSpy).toHaveBeenCalled(); + }); + + it('should emit forbidden on XHR 403', async () => { + mockXhr.status = 403; + mockXhr.responseText = 'Forbidden'; + + const forbiddenSpy = jest.fn(); + eventEmitter.on('forbidden', forbiddenSpy); + + await expect( + xhrClient.post( + host + '/api/forbidden', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ) + ).rejects.toEqual(expect.objectContaining({ status: 403 })); + + expect(forbiddenSpy).toHaveBeenCalled(); + }); + + it('should handle XHR network error', async () => { + mockXhr.send.mockImplementation(() => { + setTimeout(() => mockXhr.onerror(), 0); + }); + + const errorSpy = jest.fn(); + eventEmitter.on('error', errorSpy); + + await expect( + xhrClient.post( + host + '/api/network-fail', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ) + ).rejects.toBeTruthy(); + + expect(errorSpy).toHaveBeenCalled(); + }); + + it('should handle XHR abort', async () => { + mockXhr.send.mockImplementation(() => { + setTimeout(() => mockXhr.onabort(), 0); + }); + + const abortSpy = jest.fn(); + eventEmitter.on('abort', abortSpy); + + await expect( + xhrClient.post( + host + '/api/abort', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ) + ).rejects.toBeTruthy(); + + expect(abortSpy).toHaveBeenCalled(); + }); + + it('should handle XHR timeout', async () => { + mockXhr.send.mockImplementation(() => { + setTimeout(() => mockXhr.ontimeout(), 0); + }); + + const errorSpy = jest.fn(); + eventEmitter.on('error', errorSpy); + + await expect( + xhrClient.post( + host + '/api/slow', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ) + ).rejects.toBeTruthy(); + + expect(errorSpy).toHaveBeenCalled(); + }); + + it('should support aborting an XHR request via promise.abort()', async () => { + mockXhr.send.mockImplementation(() => { + // don't auto-resolve + }); + + const promise = xhrClient.post( + host + '/api/cancel', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + (promise as any).abort(); + expect(mockXhr.abort).toHaveBeenCalled(); + }); + + it('should set withCredentials on XHR for BPM requests', async () => { + mockXhr.status = 200; + mockXhr.responseText = JSON.stringify({ ok: true }); + mockXhr.getResponseHeader.mockReturnValue('application/json'); + + await xhrClient.post( + host + '/api/bpm', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + { ...defaultSecurityOptions, isBpmRequest: true }, + emitters + ); + + expect(mockXhr.withCredentials).toBe(true); + }); + + it('should set blob responseType for blob returnType', async () => { + mockXhr.status = 200; + mockXhr.response = new Blob(['test']); + mockXhr.getResponseHeader.mockReturnValue('application/octet-stream'); + + await xhrClient.post( + host + '/api/download', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { id: 1 }, + contentType: 'application/json', + accept: 'application/octet-stream', + responseType: null, + returnType: 'Blob' + }, + defaultSecurityOptions, + emitters + ); + + expect(mockXhr.responseType).toBe('blob'); + }); + + it('should deserialize String returnType from XHR', async () => { + mockXhr.status = 200; + mockXhr.responseText = 'plain text response'; + mockXhr.getResponseHeader.mockReturnValue('text/plain'); + + const result = await xhrClient.post( + host + '/api/text', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { query: 'test' }, + contentType: 'application/json', + accept: 'text/plain', + responseType: null, + returnType: 'String' + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toBe('plain text response'); + }); + + it('should deserialize HTML content from XHR', async () => { + mockXhr.status = 200; + mockXhr.responseText = 'content'; + mockXhr.getResponseHeader.mockReturnValue('text/html'); + + const result = await xhrClient.post( + host + '/api/html', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { page: 'home' }, + contentType: 'application/json', + accept: 'text/html', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toBe('content'); + }); + + it('should return empty object for empty XHR response', async () => { + mockXhr.status = 200; + mockXhr.responseText = ''; + mockXhr.getResponseHeader.mockReturnValue('application/json'); + + const result = await xhrClient.post( + host + '/api/empty', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(result).toEqual({}); + }); + + it('should set XHR timeout when configured', async () => { + xhrClient.timeout = 5000; + mockXhr.status = 200; + mockXhr.responseText = JSON.stringify({ ok: true }); + mockXhr.getResponseHeader.mockReturnValue('application/json'); + + await xhrClient.post( + host + '/api/test', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: {}, + bodyParam: { data: 'test' }, + contentType: 'application/json', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(mockXhr.timeout).toBe(5000); + }); + + it('should propagate progress events to promise.on() listeners', async () => { + mockXhr.status = 200; + mockXhr.responseText = JSON.stringify({ uploaded: true }); + mockXhr.getResponseHeader.mockReturnValue('application/json'); + mockXhr.send.mockImplementation(() => { + if (mockXhr.upload.onprogress) { + mockXhr.upload.onprogress({ lengthComputable: true, loaded: 30, total: 100 }); + mockXhr.upload.onprogress({ lengthComputable: true, loaded: 100, total: 100 }); + } + setTimeout(() => mockXhr.onload(), 0); + }); + + const progressEvents: any[] = []; + eventEmitter.on('progress', (event: any) => progressEvents.push(event)); + + await xhrClient.post( + host + '/api/upload', + { + httpMethod: 'POST', + queryParams: {}, + headerParams: {}, + formParams: { file: 'content' }, + bodyParam: null, + contentType: 'multipart/form-data', + accept: 'application/json', + responseType: null, + returnType: null + }, + defaultSecurityOptions, + emitters + ); + + expect(progressEvents).toEqual([ + { total: 100, loaded: 30, percent: 30 }, + { total: 100, loaded: 100, percent: 100 } + ]); + }); + }); +}); diff --git a/lib/js-api/test/jest-jsdom-fetch-environment.ts b/lib/js-api/test/jest-jsdom-fetch-environment.ts new file mode 100644 index 0000000000..07451ead51 --- /dev/null +++ b/lib/js-api/test/jest-jsdom-fetch-environment.ts @@ -0,0 +1,72 @@ +/*! + * @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. + */ + +/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires, jsdoc/require-jsdoc */ +import { TextEncoder, TextDecoder } from 'util'; + +const { TestEnvironment } = require('jest-environment-jsdom'); + +const nodeGlobals = [ + 'TextEncoder', + 'TextDecoder', + 'ReadableStream', + 'WritableStream', + 'TransformStream', + 'structuredClone', + 'BroadcastChannel', + 'MessagePort', + 'MessageChannel', + 'Blob', + 'File', + 'FormData', + 'EventTarget', + 'Event', + 'AbortController', + 'AbortSignal' +]; + +class JSDOMFetchEnvironment extends TestEnvironment { + constructor(config: any, context: any) { + super(config, context); + + if (!this.global.TextEncoder) { + this.global.TextEncoder = TextEncoder; + } + if (!this.global.TextDecoder) { + this.global.TextDecoder = TextDecoder as any; + } + + try { + const streams = require('stream/web'); + for (const name of ['ReadableStream', 'WritableStream', 'TransformStream']) { + if (!this.global[name] && streams[name]) { + this.global[name] = streams[name]; + } + } + } catch { + /* stream/web not available */ + } + + for (const name of nodeGlobals) { + if (!this.global[name] && (globalThis as any)[name]) { + this.global[name] = (globalThis as any)[name]; + } + } + } +} + +module.exports = JSDOMFetchEnvironment; diff --git a/lib/js-api/test/mockObjects/base.mock.ts b/lib/js-api/test/mockObjects/base.mock.ts index 92a434e557..152094e702 100644 --- a/lib/js-api/test/mockObjects/base.mock.ts +++ b/lib/js-api/test/mockObjects/base.mock.ts @@ -15,7 +15,96 @@ * limitations under the License. */ -import nock from 'nock'; +/* eslint-disable no-underscore-dangle, jsdoc/require-jsdoc */ +import { MockAgent, Interceptable, fetch as undiciFetch } from 'undici'; + +export function getGlobalMockAgent(): MockAgent { + if (!(global as any).__mockAgent__) { + const agent = new MockAgent(); + agent.disableNetConnect(); + (global as any).__mockAgent__ = agent; + } + const agent: MockAgent = (global as any).__mockAgent__; + (process as any).__test_fetch__ = (input: any, init?: any) => undiciFetch(input, { ...init, dispatcher: agent }); + return agent; +} + +export function resetGlobalMockAgent(): void { + const agent = (global as any).__mockAgent__; + if (agent) { + agent.close(); + (global as any).__mockAgent__ = undefined; + } + delete (process as any).__test_fetch__; +} + +interface MockReplyChain { + reply(statusCode: number, body?: any, headers?: Record): void; +} + +interface MockQueryable { + query(params: Record): MockReplyChain; + reply(statusCode: number, body?: any, headers?: Record): void; +} + +interface MockInterceptor { + get(path: string, body?: any): MockQueryable; + post(path: string, body?: any): MockQueryable; + put(path: string, body?: any): MockQueryable; + delete(path: string, body?: any): MockQueryable; +} + +function buildQueryString(params: Record): string { + const sp = new URLSearchParams(); + for (const [key, value] of Object.entries(params)) { + sp.append(key, value); + } + return sp.toString(); +} + +// cspell:ignore Interceptable +function createInterceptor(pool: Interceptable): MockInterceptor { + const doIntercept = (method: string, path: string, body?: any): MockReplyChain => ({ + reply(statusCode: number, responseBody?: any, headers?: Record) { + const interceptOpts: any = { path, method }; + if (body && method !== 'GET' && method !== 'DELETE') { + interceptOpts.body = typeof body === 'string' ? body : JSON.stringify(body); + } + const responseHeaders = { 'content-type': 'application/json', ...headers }; + const replyBody = + responseBody === undefined || responseBody === '' + ? '' + : typeof responseBody === 'string' + ? responseBody + : JSON.stringify(responseBody); + pool.intercept(interceptOpts).reply(statusCode, replyBody, { headers: responseHeaders }); + } + }); + + const makeChain = (method: string, path: string, body?: any): MockQueryable => ({ + query(params: Record): MockReplyChain { + const qs = buildQueryString(params); + const separator = path.includes('?') ? '&' : '?'; + return doIntercept(method, `${path}${separator}${qs}`, body); + }, + reply(statusCode: number, responseBody?: any, headers?: Record) { + doIntercept(method, path, body).reply(statusCode, responseBody, headers); + } + }); + + return { + get: (path: string) => makeChain('GET', path), + post: (path: string, body?: any) => makeChain('POST', path, body), + put: (path: string, body?: any) => makeChain('PUT', path, body), + delete: (path: string, body?: any) => makeChain('DELETE', path, body) + }; +} + +export function mockHost(host: string): MockInterceptor { + const agent = getGlobalMockAgent(); + const pool = agent.get(host); + return createInterceptor(pool); +} export class BaseMock { host: string; @@ -24,15 +113,17 @@ export class BaseMock { this.host = host || 'https://127.0.0.1:8080'; } - put200GenericResponse(scriptSlug: string): void { - nock(this.host, { encodedQueryParams: true }).put(scriptSlug).reply(200); + protected mock(): MockInterceptor { + return mockHost(this.host); } - play(): void { - nock.recorder.play(); + put200GenericResponse(scriptSlug: string): void { + this.mock().put(scriptSlug).reply(200); } cleanAll(): void { - nock.cleanAll(); + const agent = getGlobalMockAgent(); + const pool = agent.get(this.host) as Interceptable; + pool.cleanMocks(); } } diff --git a/lib/js-api/test/mockObjects/content-services/agent.mock.ts b/lib/js-api/test/mockObjects/content-services/agent.mock.ts index 1efc29b131..04dc742c68 100644 --- a/lib/js-api/test/mockObjects/content-services/agent.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/agent.mock.ts @@ -16,11 +16,10 @@ */ import { BaseMock } from '../base.mock'; -import nock from 'nock'; export class AgentMock extends BaseMock { mockGetAgents200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/private/hxi/versions/1/agents') .reply(200, { list: { diff --git a/lib/js-api/test/mockObjects/content-services/categories.mock.ts b/lib/js-api/test/mockObjects/content-services/categories.mock.ts index 33fb428271..8acfd8554b 100644 --- a/lib/js-api/test/mockObjects/content-services/categories.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/categories.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class CategoriesMock extends BaseMock { get200ResponseSubcategories(categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get(`/alfresco/api/-default-/public/alfresco/versions/1/categories/${categoryId}/subcategories`) .reply(200, { list: { @@ -56,7 +55,7 @@ export class CategoriesMock extends BaseMock { } get404SubcategoryNotExist(categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get(`/alfresco/api/-default-/public/alfresco/versions/1/categories/${categoryId}/subcategories`) .reply(404, { error: { @@ -70,7 +69,7 @@ export class CategoriesMock extends BaseMock { } get200ResponseCategory(categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get(`/alfresco/api/-default-/public/alfresco/versions/1/categories/${categoryId}`) .reply(200, { entry: { @@ -84,7 +83,7 @@ export class CategoriesMock extends BaseMock { } get404CategoryNotExist(categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get(`/alfresco/api/-default-/public/alfresco/versions/1/categories/${categoryId}`) .reply(404, { error: { @@ -98,7 +97,7 @@ export class CategoriesMock extends BaseMock { } get200ResponseNodeCategoryLinks(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links`) .reply(200, { list: { @@ -125,7 +124,7 @@ export class CategoriesMock extends BaseMock { } get403NodeCategoryLinksPermissionDenied(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links`) .reply(403, { error: { @@ -135,7 +134,7 @@ export class CategoriesMock extends BaseMock { } get404NodeNotExist(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links`) .reply(404, { error: { @@ -149,13 +148,11 @@ export class CategoriesMock extends BaseMock { } get204CategoryUnlinked(nodeId: string, categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) - .delete(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links/${categoryId}`) - .reply(204); + this.mock().delete(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links/${categoryId}`).reply(204); } get403CategoryUnlinkPermissionDenied(nodeId: string, categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .delete(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links/${categoryId}`) .reply(403, { error: { @@ -165,7 +162,7 @@ export class CategoriesMock extends BaseMock { } get404CategoryUnlinkNotFound(nodeId: string, categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .delete(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links/${categoryId}`) .reply(404, { error: { @@ -179,7 +176,7 @@ export class CategoriesMock extends BaseMock { } get200ResponseCategoryUpdated(categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put(`/alfresco/api/-default-/public/alfresco/versions/1/categories/${categoryId}`, { name: 'testName1' }) .reply(200, { entry: { @@ -193,7 +190,7 @@ export class CategoriesMock extends BaseMock { } get403CategoryUpdatePermissionDenied(categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put(`/alfresco/api/-default-/public/alfresco/versions/1/categories/${categoryId}`, { name: 'testName1' }) .reply(403, { error: { @@ -203,7 +200,7 @@ export class CategoriesMock extends BaseMock { } get404CategoryUpdateNotFound(categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put(`/alfresco/api/-default-/public/alfresco/versions/1/categories/${categoryId}`, { name: 'testName1' }) .reply(404, { error: { @@ -217,7 +214,7 @@ export class CategoriesMock extends BaseMock { } get201ResponseCategoryCreated(categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post(`/alfresco/api/-default-/public/alfresco/versions/1/categories/${categoryId}/subcategories`, [{ name: 'testName10' }]) .reply(201, { entry: { @@ -231,7 +228,7 @@ export class CategoriesMock extends BaseMock { } get403CategoryCreatedPermissionDenied(categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post(`/alfresco/api/-default-/public/alfresco/versions/1/categories/${categoryId}/subcategories`, [{ name: 'testName10' }]) .reply(403, { error: { @@ -241,7 +238,7 @@ export class CategoriesMock extends BaseMock { } get409CategoryCreateAlreadyExists(categoryId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post(`/alfresco/api/-default-/public/alfresco/versions/1/categories/${categoryId}/subcategories`, [{ name: 'testName10' }]) .reply(409, { error: { @@ -255,7 +252,7 @@ export class CategoriesMock extends BaseMock { } get201ResponseCategoryLinked(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links`, [{ categoryId: 'testId1' }]) .reply(201, { entry: { @@ -269,7 +266,7 @@ export class CategoriesMock extends BaseMock { } get201ResponseCategoryLinkedArray(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links`, [ { categoryId: 'testId1' }, { categoryId: 'testId2' } @@ -308,7 +305,7 @@ export class CategoriesMock extends BaseMock { } get403CategoryLinkPermissionDenied(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links`, [{ categoryId: 'testId1' }]) .reply(403, { error: { @@ -318,7 +315,7 @@ export class CategoriesMock extends BaseMock { } get404CategoryLinkNotFound(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links`, [{ categoryId: 'testId1' }]) .reply(404, { error: { @@ -332,7 +329,7 @@ export class CategoriesMock extends BaseMock { } get405CategoryLinkCannotAssign(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post(`/alfresco/api/-default-/public/alfresco/versions/1/nodes/${nodeId}/category-links`, [{ categoryId: 'testId1' }]) .reply(405, { error: { diff --git a/lib/js-api/test/mockObjects/content-services/comment.mock.ts b/lib/js-api/test/mockObjects/content-services/comment.mock.ts index 1f398471fd..b26a4e2319 100644 --- a/lib/js-api/test/mockObjects/content-services/comment.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/comment.mock.ts @@ -17,7 +17,6 @@ 'use strict'; -import nock from 'nock'; import { BaseMock } from '../base.mock'; const adminUser = { @@ -40,7 +39,7 @@ const adminUser = { export class CommentMock extends BaseMock { post201Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/74cd8a96-8a21-47e5-9b3b-a1b3e296787d/comments', { content: 'This is a comment' }) @@ -60,7 +59,7 @@ export class CommentMock extends BaseMock { } get200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/nodes/74cd8a96-8a21-47e5-9b3b-a1b3e296787d/comments') .reply(200, { list: { diff --git a/lib/js-api/test/mockObjects/content-services/custom-model.mock.ts b/lib/js-api/test/mockObjects/content-services/custom-model.mock.ts index 15e5c6c510..47db17e1da 100644 --- a/lib/js-api/test/mockObjects/content-services/custom-model.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/custom-model.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class CustomModelMock extends BaseMock { get200AllCustomModel(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/private/alfresco/versions/1/cmm') .reply(200, { list: { @@ -37,7 +36,7 @@ export class CustomModelMock extends BaseMock { } create201CustomModel(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/private/alfresco/versions/1/cmm') .reply(201, { entry: { @@ -52,7 +51,7 @@ export class CustomModelMock extends BaseMock { } activateCustomModel200(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put('/alfresco/api/-default-/private/alfresco/versions/1/cmm/testModel', { status: 'ACTIVE' }) .query({ select: 'status' }) .reply(200, { diff --git a/lib/js-api/test/mockObjects/content-services/discovery.mock.ts b/lib/js-api/test/mockObjects/content-services/discovery.mock.ts index 06dbb4d2a2..273bf2cd9b 100644 --- a/lib/js-api/test/mockObjects/content-services/discovery.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/discovery.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class DiscoveryMock extends BaseMock { get200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/discovery') .reply(200, { entry: { diff --git a/lib/js-api/test/mockObjects/content-services/ecm-auth.mock.ts b/lib/js-api/test/mockObjects/content-services/ecm-auth.mock.ts index 946a5fb3c0..3b4a465e08 100644 --- a/lib/js-api/test/mockObjects/content-services/ecm-auth.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/ecm-auth.mock.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class EcmAuthMock extends BaseMock { @@ -31,7 +30,7 @@ export class EcmAuthMock extends BaseMock { get201Response(forceTicket?: string): void { const returnMockTicket = forceTicket || 'TICKET_4479f4d3bb155195879bfbb8d5206f433488a1b1'; - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/authentication/versions/1/tickets', { userId: this.username, password: this.password @@ -42,13 +41,13 @@ export class EcmAuthMock extends BaseMock { get200ValidTicket(forceTicket?: string): void { const returnMockTicket = forceTicket || 'TICKET_4479f4d3bb155195879bfbb8d5206f433488a1b1'; - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/authentication/versions/1/tickets/-me-') .reply(200, { entry: { id: returnMockTicket } }); } get401InvalidTicket(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/authentication/versions/1/tickets/-me-') .reply(401, { error: { @@ -62,7 +61,7 @@ export class EcmAuthMock extends BaseMock { } get403Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/authentication/versions/1/tickets', { userId: 'wrong', password: 'name' @@ -79,7 +78,7 @@ export class EcmAuthMock extends BaseMock { } get400Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/authentication/versions/1/tickets', { userId: null, password: null @@ -96,7 +95,7 @@ export class EcmAuthMock extends BaseMock { } get401Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/authentication/versions/1/tickets', { userId: 'wrong', password: 'name' @@ -112,11 +111,28 @@ export class EcmAuthMock extends BaseMock { }); } + get401ResponseAdminCredentials(): void { + this.mock() + .post('/alfresco/api/-default-/public/authentication/versions/1/tickets', { + userId: 'admin', + password: 'admin' + }) + .reply(401, { + error: { + errorKey: 'framework.exception.ApiDefault', + statusCode: 401, + briefSummary: '05210059 Authentication failed for Web Script org/alfresco/api/ResourceWebScript.get', + stackTrace: 'For security reasons the stack trace is no longer displayed, but the property is kept for previous versions.', + descriptionURL: 'https://api-explorer.alfresco.com' + } + }); + } + get204ResponseLogout(): void { - nock(this.host, { encodedQueryParams: true }).delete('/alfresco/api/-default-/public/authentication/versions/1/tickets/-me-').reply(204, ''); + this.mock().delete('/alfresco/api/-default-/public/authentication/versions/1/tickets/-me-').reply(204, ''); } get404ResponseLogout(): void { - nock(this.host, { encodedQueryParams: true }).delete('/alfresco/api/-default-/public/authentication/versions/1/tickets/-me-').reply(404, ''); + this.mock().delete('/alfresco/api/-default-/public/authentication/versions/1/tickets/-me-').reply(404, ''); } } diff --git a/lib/js-api/test/mockObjects/content-services/find-nodes.mock.ts b/lib/js-api/test/mockObjects/content-services/find-nodes.mock.ts index fd7fcd03cc..5abfcf18fc 100644 --- a/lib/js-api/test/mockObjects/content-services/find-nodes.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/find-nodes.mock.ts @@ -15,13 +15,13 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class FindNodesMock extends BaseMock { get200Response(): void { - nock(this.host, { encodedQueryParams: true }) - .get('/alfresco/api/-default-/public/alfresco/versions/1/queries/nodes?term=test') + this.mock() + .get('/alfresco/api/-default-/public/alfresco/versions/1/queries/nodes') + .query({ term: 'test' }) .reply(200, { list: { pagination: { @@ -78,8 +78,9 @@ export class FindNodesMock extends BaseMock { } get401Response(): void { - nock(this.host, { encodedQueryParams: true }) - .get('/alfresco/api/-default-/public/alfresco/versions/1/queries/nodes?term=test') + this.mock() + .get('/alfresco/api/-default-/public/alfresco/versions/1/queries/nodes') + .query({ term: 'test' }) .reply(401, { error: { errorKey: 'framework.exception.ApiDefault', diff --git a/lib/js-api/test/mockObjects/content-services/groups.mock.ts b/lib/js-api/test/mockObjects/content-services/groups.mock.ts index a59bf57ac3..c05263c6cf 100644 --- a/lib/js-api/test/mockObjects/content-services/groups.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/groups.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class GroupsMock extends BaseMock { get200GetGroups(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/groups') .reply(200, { list: { @@ -52,20 +51,20 @@ export class GroupsMock extends BaseMock { } getDeleteGroupSuccessfulResponse(groupName: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .delete('/alfresco/api/-default-/public/alfresco/versions/1/groups/' + groupName) .query({ cascade: 'false' }) .reply(200); } getDeleteMemberForGroupSuccessfulResponse(groupName: string, memberName: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .delete('/alfresco/api/-default-/public/alfresco/versions/1/groups/' + groupName + '/members/' + memberName) .reply(200); } get200CreateGroupResponse(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/groups') .reply(200, { entry: { @@ -77,7 +76,7 @@ export class GroupsMock extends BaseMock { } get200GetSingleGroup(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/groups/GROUP_TEST') .reply(200, { entry: { @@ -89,7 +88,7 @@ export class GroupsMock extends BaseMock { } get200UpdateGroupResponse(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put('/alfresco/api/-default-/public/alfresco/versions/1/groups/GROUP_TEST') .reply(200, { entry: { @@ -101,7 +100,7 @@ export class GroupsMock extends BaseMock { } get200GetGroupMemberships(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/groups/GROUP_TEST/members') .reply(200, { list: { @@ -126,7 +125,7 @@ export class GroupsMock extends BaseMock { } get200AddGroupMembershipResponse(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/groups/GROUP_TEST/members') .reply(200, { entry: { diff --git a/lib/js-api/test/mockObjects/content-services/node.mock.ts b/lib/js-api/test/mockObjects/content-services/node.mock.ts index 6baca0b833..c522201cb3 100644 --- a/lib/js-api/test/mockObjects/content-services/node.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/node.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class NodeMock extends BaseMock { get200ResponseChildren(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/children') .reply(200, { list: { @@ -108,7 +107,7 @@ export class NodeMock extends BaseMock { } get200ResponseChildrenNonUTCTimes(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1320/children') .reply(200, { list: { @@ -140,7 +139,7 @@ export class NodeMock extends BaseMock { } get404ChildrenNotExist(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/children') .reply(404, { error: { @@ -154,23 +153,19 @@ export class NodeMock extends BaseMock { } get401CreationFolder(): void { - nock(this.host, { encodedQueryParams: true }).post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children').reply(401); + this.mock().post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children').reply(401); } get204SuccessfullyDeleted(): void { - nock(this.host, { encodedQueryParams: true }) - .delete('/alfresco/api/-default-/public/alfresco/versions/1/nodes/80a94ac8-3ece-47ad-864e-5d939424c47c') - .reply(204); + this.mock().delete('/alfresco/api/-default-/public/alfresco/versions/1/nodes/80a94ac8-3ece-47ad-864e-5d939424c47c').reply(204); } get403DeletePermissionDenied(): void { - nock(this.host, { encodedQueryParams: true }) - .delete('/alfresco/api/-default-/public/alfresco/versions/1/nodes/80a94ac8-3ece-47ad-864e-5d939424c47c') - .reply(403); + this.mock().delete('/alfresco/api/-default-/public/alfresco/versions/1/nodes/80a94ac8-3ece-47ad-864e-5d939424c47c').reply(403); } get404DeleteNotFound(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .delete('/alfresco/api/-default-/public/alfresco/versions/1/nodes/80a94ac8-test-47ad-864e-5d939424c47c') .reply(404, { error: { @@ -184,7 +179,7 @@ export class NodeMock extends BaseMock { } get200ResponseChildrenFutureNewPossibleValue(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/children') .reply(200, { list: { @@ -232,7 +227,7 @@ export class NodeMock extends BaseMock { } post200ResponseInitiateFolderSizeCalculation(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/size-details') .reply(200, { entry: { @@ -242,7 +237,7 @@ export class NodeMock extends BaseMock { } post404NodeIdNotFound(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/size-details') .reply(404, { error: { @@ -257,7 +252,7 @@ export class NodeMock extends BaseMock { } get200ResponseGetFolderSizeInfo(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get( '/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/size-details/5ade426e-8a04-4d50-9e42-6e8a041d50f3' ) @@ -274,7 +269,7 @@ export class NodeMock extends BaseMock { } get404JobIdNotFound(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get( '/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/size-details/5ade426e-8a04-4d50-9e42-6e8a041d50f3' ) diff --git a/lib/js-api/test/mockObjects/content-services/people.mock.ts b/lib/js-api/test/mockObjects/content-services/people.mock.ts index 0361e21ea9..34aec09b9d 100644 --- a/lib/js-api/test/mockObjects/content-services/people.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/people.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class PeopleMock extends BaseMock { get201Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/people') .reply(201, { entry: { @@ -36,7 +35,7 @@ export class PeopleMock extends BaseMock { } get200ResponsePersons(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/people') .reply(200, { list: { diff --git a/lib/js-api/test/mockObjects/content-services/rendition.mock.ts b/lib/js-api/test/mockObjects/content-services/rendition.mock.ts index 82cb974bfc..e144626319 100644 --- a/lib/js-api/test/mockObjects/content-services/rendition.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/rendition.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class RenditionMock extends BaseMock { get200RenditionResponse(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/nodes/97a29e9c-1e4f-4d9d-bb02-1ec920dda045/renditions/pdf') .reply(200, { entry: { @@ -32,13 +31,13 @@ export class RenditionMock extends BaseMock { } createRendition200(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/97a29e9c-1e4f-4d9d-bb02-1ec920dda045/renditions', { id: 'pdf' }) .reply(202, ''); } get200RenditionList(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/nodes/97a29e9c-1e4f-4d9d-bb02-1ec920dda045/renditions') .reply(200, { list: { diff --git a/lib/js-api/test/mockObjects/content-services/search-ai.mock.ts b/lib/js-api/test/mockObjects/content-services/search-ai.mock.ts index daac9702ec..b3e826bf2d 100644 --- a/lib/js-api/test/mockObjects/content-services/search-ai.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/search-ai.mock.ts @@ -16,11 +16,10 @@ */ import { BaseMock } from '../base.mock'; -import nock from 'nock'; export class SearchAiMock extends BaseMock { mockGetAsk200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/private/hxi/versions/1/agents/id1/questions', [ { question: 'some question 1', @@ -41,7 +40,7 @@ export class SearchAiMock extends BaseMock { } mockGetAnswer200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/private/hxi/versions/1/questions/id1/answers/-default-') .reply(200, { entry: { @@ -85,7 +84,7 @@ export class SearchAiMock extends BaseMock { } mockGetConfig200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/private/hxi/versions/1/config/-default-') .reply(200, { entry: { diff --git a/lib/js-api/test/mockObjects/content-services/search.mock.ts b/lib/js-api/test/mockObjects/content-services/search.mock.ts index 5e7796269c..1ce237fc7f 100644 --- a/lib/js-api/test/mockObjects/content-services/search.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/search.mock.ts @@ -15,13 +15,12 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; import { SEARCH_LANGUAGE } from '@alfresco/js-api'; export class SearchMock extends BaseMock { get200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/search/versions/1/search', { query: { query: 'select * from cmis:folder', diff --git a/lib/js-api/test/mockObjects/content-services/tag.mock.ts b/lib/js-api/test/mockObjects/content-services/tag.mock.ts index ba3362c1b3..244ff13b95 100644 --- a/lib/js-api/test/mockObjects/content-services/tag.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/tag.mock.ts @@ -15,26 +15,25 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; import { TagBody, TagEntry, TagPaging } from '../../../src/api/content-rest-api'; export class TagMock extends BaseMock { get200Response(): void { - nock(this.host, { encodedQueryParams: true }) - .get('/alfresco/api/-default-/public/alfresco/versions/1/tags') - .reply(200, this.getPaginatedListOfTags()); + this.mock().get('/alfresco/api/-default-/public/alfresco/versions/1/tags').reply(200, this.getPaginatedListOfTags()); } getTagsByNameFilteredByMatching200Response(): void { - nock(this.host, { encodedQueryParams: true }) - .get('/alfresco/api/-default-/public/alfresco/versions/1/tags?where=(tag%20matches%20(%27*tag-test*%27))') + this.mock() + .get('/alfresco/api/-default-/public/alfresco/versions/1/tags') + .query({ where: "(tag matches ('*tag-test*'))" }) .reply(200, this.getPaginatedListOfTags()); } getTagsByNamesFilterByExactTag200Response(): void { - nock(this.host, { encodedQueryParams: true }) - .get('/alfresco/api/-default-/public/alfresco/versions/1/tags?where=(tag%3D%27tag-test-1%27)') + this.mock() + .get('/alfresco/api/-default-/public/alfresco/versions/1/tags') + .query({ where: "(tag='tag-test-1')" }) .reply(200, { list: { pagination: { @@ -49,7 +48,7 @@ export class TagMock extends BaseMock { } get401Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/tags') .reply(401, { error: { @@ -63,13 +62,11 @@ export class TagMock extends BaseMock { } createTags201Response(): void { - nock(this.host, { encodedQueryParams: true }) - .post('/alfresco/api/-default-/public/alfresco/versions/1/tags') - .reply(201, this.getPaginatedListOfTags()); + this.mock().post('/alfresco/api/-default-/public/alfresco/versions/1/tags').reply(201, this.getPaginatedListOfTags()); } get201ResponseForAssigningTagsToNode(body: TagBody[]): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/someNodeId/tags', JSON.stringify(body)) .reply(201, body.length > 1 ? this.getPaginatedListOfTags() : this.mockTagEntry()); } diff --git a/lib/js-api/test/mockObjects/content-services/upload.mock.ts b/lib/js-api/test/mockObjects/content-services/upload.mock.ts index d3ffca0963..170ca1463d 100644 --- a/lib/js-api/test/mockObjects/content-services/upload.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/upload.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class UploadMock extends BaseMock { get201CreationFile(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children') .reply(201, { entry: { @@ -47,7 +46,7 @@ export class UploadMock extends BaseMock { } get201CreationFileAutoRename(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children') .query({ autoRename: 'true' }) .reply(201, { @@ -75,7 +74,7 @@ export class UploadMock extends BaseMock { } get409CreationFileNewNameClashes(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children') .reply(409, { error: { @@ -89,7 +88,7 @@ export class UploadMock extends BaseMock { } get401Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children') .reply(401, { error: { diff --git a/lib/js-api/test/mockObjects/content-services/version.mock.ts b/lib/js-api/test/mockObjects/content-services/version.mock.ts index 9bb1dd4bfe..091e74b9db 100644 --- a/lib/js-api/test/mockObjects/content-services/version.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/version.mock.ts @@ -15,18 +15,17 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class VersionMock extends BaseMock { post201Response(nodeId: string, versionId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/' + nodeId + '/versions/' + versionId + '/revert') .reply(201, { entry: { id: '3.0' } }); } get200Response(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/nodes/' + nodeId + '/versions') .reply(200, { list: { @@ -43,7 +42,7 @@ export class VersionMock extends BaseMock { } get200ResponseVersionRenditions(nodeId: string, versionId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/nodes/' + nodeId + '/versions/' + versionId + '/renditions') .reply(200, { list: { @@ -103,7 +102,7 @@ export class VersionMock extends BaseMock { } get200VersionRendition(nodeId: string, versionId: string, renditionId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/alfresco/versions/1/nodes/' + nodeId + '/versions/' + versionId + '/renditions/' + renditionId) .reply(200, { entry: { @@ -115,7 +114,7 @@ export class VersionMock extends BaseMock { } create200VersionRendition(nodeId: string, versionId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/' + nodeId + '/versions/' + versionId + '/renditions', { id: 'pdf' }) .reply(202, ''); } diff --git a/lib/js-api/test/mockObjects/content-services/webscript.mock.ts b/lib/js-api/test/mockObjects/content-services/webscript.mock.ts index c548cbc1f3..a250ddf844 100644 --- a/lib/js-api/test/mockObjects/content-services/webscript.mock.ts +++ b/lib/js-api/test/mockObjects/content-services/webscript.mock.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class WebScriptMock extends BaseMock { @@ -34,7 +33,7 @@ export class WebScriptMock extends BaseMock { } get404Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get(this.scriptSlug) .reply(404, { error: { @@ -48,7 +47,7 @@ export class WebScriptMock extends BaseMock { } get200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get(this.scriptSlug) .reply(200, { randomStructure: { @@ -59,7 +58,7 @@ export class WebScriptMock extends BaseMock { } get200ResponseHTMLFormat(): void { - nock(this.host, { encodedQueryParams: true }).get('/alfresco/service/sample/folder/Company%20Home').reply( + this.mock().get('/alfresco/service/sample/folder/Company%20Home').reply( 200, // eslint-disable-next-line max-len '\n \n /Company Home\n \n \n Folder: /Company Home\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
>Data Dictionary\n
>Guest Home\n
>User Homes\n
>Shared\n
>Imap Attachments\n
>IMAP Home\n
>Sites\n
>x\n
testFile.txt\n
>newFolder\n
>newFolder-1\n
testFile-1.txt\n
testFile-2.txt\n
testFile-3.txt\n
\n \n\n\n' @@ -67,7 +66,7 @@ export class WebScriptMock extends BaseMock { } get401Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get(this.scriptSlug) .reply(401, { error: { diff --git a/lib/js-api/test/mockObjects/goverance-services/authority-clearance.mock.ts b/lib/js-api/test/mockObjects/goverance-services/authority-clearance.mock.ts index 589f6232f5..4a2854cdeb 100644 --- a/lib/js-api/test/mockObjects/goverance-services/authority-clearance.mock.ts +++ b/lib/js-api/test/mockObjects/goverance-services/authority-clearance.mock.ts @@ -16,12 +16,12 @@ */ import { BaseMock } from '../base.mock'; -import nock from 'nock'; export class AuthorityClearanceMock extends BaseMock { get200AuthorityClearanceForAuthority(authorityId: string): void { - nock(this.host, { encodedQueryParams: true }) - .get('/alfresco/api/-default-/public/gs/versions/1/cleared-authorities/' + authorityId + '/clearing-marks?skipCount=0&maxItems=100') + this.mock() + .get('/alfresco/api/-default-/public/gs/versions/1/cleared-authorities/' + authorityId + '/clearing-marks') + .query({ skipCount: '0', maxItems: '100' }) .reply(200, { list: { pagination: { @@ -94,7 +94,7 @@ export class AuthorityClearanceMock extends BaseMock { } post200AuthorityClearanceWithSingleItem(authorityId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/gs/versions/1/cleared-authorities/' + authorityId + '/clearing-marks') .reply(200, { entry: { @@ -106,7 +106,7 @@ export class AuthorityClearanceMock extends BaseMock { } post200AuthorityClearanceWithList(authorityId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/gs/versions/1/cleared-authorities/' + authorityId + '/clearing-marks') .reply(200, { list: { diff --git a/lib/js-api/test/mockObjects/goverance-services/file-plans.mock.ts b/lib/js-api/test/mockObjects/goverance-services/file-plans.mock.ts index 64e4997b93..d839ad0726 100644 --- a/lib/js-api/test/mockObjects/goverance-services/file-plans.mock.ts +++ b/lib/js-api/test/mockObjects/goverance-services/file-plans.mock.ts @@ -16,42 +16,34 @@ */ import { BaseMock } from '../base.mock'; -import nock from 'nock'; import { FilePlanRolePaging } from '@alfresco/js-api'; export class FilePlansMock extends BaseMock { get200FilePlanRoles(filePlanId: string): void { - this.nock200FilePlanRoles(filePlanId).query({}).reply(200, this.mockFilePlanRolePaging()); + this.mock().get(`/alfresco/api/-default-/public/gs/versions/1/file-plans/${filePlanId}/roles`).reply(200, this.mockFilePlanRolePaging()); } get200FilePlanRolesWithFilteringByCapabilityNames(filePlanId: string): void { - this.nock200FilePlanRoles(filePlanId) - .query({ - where: "(capabilityName in ('capability1', 'capability2'))" - }) + this.mock() + .get(`/alfresco/api/-default-/public/gs/versions/1/file-plans/${filePlanId}/roles`) + .query({ where: "(capabilityName in ('capability1', 'capability2'))" }) .reply(200, this.mockFilePlanRolePaging()); } get200FilePlanRolesWithFilteringByPersonId(filePlanId: string): void { - this.nock200FilePlanRoles(filePlanId) - .query({ - where: "(personId='someUser')" - }) + this.mock() + .get(`/alfresco/api/-default-/public/gs/versions/1/file-plans/${filePlanId}/roles`) + .query({ where: "(personId='someUser')" }) .reply(200, this.mockFilePlanRolePaging()); } get200FilePlanRolesWithFilteringByPersonIdAndCapabilityNames(filePlanId: string): void { - this.nock200FilePlanRoles(filePlanId) - .query({ - where: "(personId='someUser' and capabilityName in ('capability1', 'capability2'))" - }) + this.mock() + .get(`/alfresco/api/-default-/public/gs/versions/1/file-plans/${filePlanId}/roles`) + .query({ where: "(personId='someUser' and capabilityName in ('capability1', 'capability2'))" }) .reply(200, this.mockFilePlanRolePaging()); } - private nock200FilePlanRoles(filePlanId: string): nock.Interceptor { - return nock(this.host, { encodedQueryParams: true }).get(`/alfresco/api/-default-/public/gs/versions/1/file-plans/${filePlanId}/roles`); - } - private mockFilePlanRolePaging(): FilePlanRolePaging { return { list: { diff --git a/lib/js-api/test/mockObjects/goverance-services/gs-sites.mock.ts b/lib/js-api/test/mockObjects/goverance-services/gs-sites.mock.ts index 36501176b3..5f384284ea 100644 --- a/lib/js-api/test/mockObjects/goverance-services/gs-sites.mock.ts +++ b/lib/js-api/test/mockObjects/goverance-services/gs-sites.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class GsSitesApiMock extends BaseMock { get200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/gs/versions/1/gs-sites/rm') .reply(200, { entry: { diff --git a/lib/js-api/test/mockObjects/goverance-services/node-security-marks.mock.ts b/lib/js-api/test/mockObjects/goverance-services/node-security-marks.mock.ts index 6f551d3efa..7f0a188d3e 100644 --- a/lib/js-api/test/mockObjects/goverance-services/node-security-marks.mock.ts +++ b/lib/js-api/test/mockObjects/goverance-services/node-security-marks.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class NodeSecurityMarksApiMock extends BaseMock { post200manageSecurityMarkOnNode(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/gs/versions/1/secured-nodes/' + nodeId + '/securing-marks') .reply(200, { list: { @@ -52,7 +51,7 @@ export class NodeSecurityMarksApiMock extends BaseMock { } get200SecurityMarkOnNode(nodeId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/gs/versions/1/secured-nodes/' + nodeId + '/securing-marks') .reply(200, { list: { diff --git a/lib/js-api/test/mockObjects/goverance-services/security-groups.mock.ts b/lib/js-api/test/mockObjects/goverance-services/security-groups.mock.ts index 95bb7afa37..db85ef4752 100644 --- a/lib/js-api/test/mockObjects/goverance-services/security-groups.mock.ts +++ b/lib/js-api/test/mockObjects/goverance-services/security-groups.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class SecurityGroupApiMock extends BaseMock { createSecurityGroup200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/gs/versions/1/security-groups') .reply(200, { entry: { @@ -32,7 +31,7 @@ export class SecurityGroupApiMock extends BaseMock { } getSecurityGroups200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/gs/versions/1/security-groups') .reply(200, { list: { @@ -64,7 +63,7 @@ export class SecurityGroupApiMock extends BaseMock { } getSecurityGroupInfo200Response(securityGroupId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/gs/versions/1/security-groups/' + securityGroupId) .reply(200, { entry: { @@ -76,7 +75,7 @@ export class SecurityGroupApiMock extends BaseMock { } updateSecurityGroup200Response(securityGroupId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put('/alfresco/api/-default-/public/gs/versions/1/security-groups/' + securityGroupId) .reply(200, { entry: { @@ -88,7 +87,7 @@ export class SecurityGroupApiMock extends BaseMock { } deleteSecurityGroup200Response(securityGroupId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .delete('/alfresco/api/-default-/public/alfresco/versions/1/security-groups/' + securityGroupId) .reply(200); } diff --git a/lib/js-api/test/mockObjects/goverance-services/security-marks.mock.ts b/lib/js-api/test/mockObjects/goverance-services/security-marks.mock.ts index af7afc9ceb..5498f7855e 100644 --- a/lib/js-api/test/mockObjects/goverance-services/security-marks.mock.ts +++ b/lib/js-api/test/mockObjects/goverance-services/security-marks.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class SecurityMarkApiMock extends BaseMock { get200GetSecurityMark(securityGroupId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/gs/versions/1/security-groups/' + securityGroupId + '/security-marks') .reply(200, { list: { @@ -45,7 +44,7 @@ export class SecurityMarkApiMock extends BaseMock { } createSecurityMark200Response(securityGroupId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/gs/versions/1/security-groups/' + securityGroupId + '/security-marks') .reply(200, { entry: { @@ -56,7 +55,7 @@ export class SecurityMarkApiMock extends BaseMock { }); } createSecurityMarks200Response(securityGroupId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/alfresco/api/-default-/public/gs/versions/1/security-groups/' + securityGroupId + '/security-marks') .reply(200, { list: { @@ -87,7 +86,7 @@ export class SecurityMarkApiMock extends BaseMock { }); } get200GetSingleSecurityMark(securityGroupId: string, securityMarkId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/gs/versions/1/security-groups/' + securityGroupId + '/security-marks/' + securityMarkId) .reply(200, { entry: { @@ -98,7 +97,7 @@ export class SecurityMarkApiMock extends BaseMock { }); } put200UpdateSecurityMarkResponse(securityGroupId: string, securityMarkId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put('/alfresco/api/-default-/public/gs/versions/1/security-groups/' + securityGroupId + '/security-marks/' + securityMarkId) .reply(200, { entry: { @@ -109,12 +108,12 @@ export class SecurityMarkApiMock extends BaseMock { }); } getDeleteSecurityMarkSuccessfulResponse(securityGroupId: string, securityMarkId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .delete('/alfresco/api/-default-/public/gs/versions/1/security-groups/' + securityGroupId + '/security-marks/' + securityMarkId) .reply(200); } get401Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/alfresco/api/-default-/public/gs/versions/1/security-groups/') .reply(401, { error: { diff --git a/lib/js-api/test/mockObjects/oauth2/oauth.mock.ts b/lib/js-api/test/mockObjects/oauth2/oauth.mock.ts index 3d1afb6982..582f66d3a3 100644 --- a/lib/js-api/test/mockObjects/oauth2/oauth.mock.ts +++ b/lib/js-api/test/mockObjects/oauth2/oauth.mock.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class OAuthMock extends BaseMock { @@ -29,7 +28,7 @@ export class OAuthMock extends BaseMock { } get200Response(mockToken?: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/auth/realms/springboot/protocol/openid-connect/token') .reply(200, { access_token: mockToken || 'test-token', diff --git a/lib/js-api/test/mockObjects/process-services/bpm-auth.mock.ts b/lib/js-api/test/mockObjects/process-services/bpm-auth.mock.ts index aae55fd881..693a7d5c8b 100644 --- a/lib/js-api/test/mockObjects/process-services/bpm-auth.mock.ts +++ b/lib/js-api/test/mockObjects/process-services/bpm-auth.mock.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class BpmAuthMock extends BaseMock { @@ -29,7 +28,7 @@ export class BpmAuthMock extends BaseMock { } get200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post( '/activiti-app/app/authentication', 'j_username=' + this.username + '&j_password=' + this.password + '&_spring_security_remember_me=true&submit=Login' @@ -38,11 +37,11 @@ export class BpmAuthMock extends BaseMock { } get200ResponseLogout(): void { - nock(this.host, { encodedQueryParams: true }).get('/activiti-app/app/logout', {}).reply(200); + this.mock().get('/activiti-app/app/logout').reply(200); } get401Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/activiti-app/app/authentication', 'j_username=wrong&j_password=name&_spring_security_remember_me=true&submit=Login') .reply(401, { error: { @@ -52,8 +51,19 @@ export class BpmAuthMock extends BaseMock { }); } + get401ResponseAdminCredentials(): void { + this.mock() + .post('/activiti-app/app/authentication', 'j_username=admin&j_password=admin&_spring_security_remember_me=true&submit=Login') + .reply(401, { + error: { + message: 'This request requires HTTP authentication.', + statusCode: 401 + } + }); + } + get403Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/activiti-app/app/authentication', 'j_username=wrong&j_password=name&_spring_security_remember_me=true&submit=Login') .reply(403, { error: { diff --git a/lib/js-api/test/mockObjects/process-services/model-json.mock.ts b/lib/js-api/test/mockObjects/process-services/model-json.mock.ts index c2924acd52..5a4e92d800 100644 --- a/lib/js-api/test/mockObjects/process-services/model-json.mock.ts +++ b/lib/js-api/test/mockObjects/process-services/model-json.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; -import { BaseMock } from '../base.mock'; +import { BaseMock, mockHost } from '../base.mock'; export class ModelJsonBpmMock extends BaseMock { get200EditorDisplayJsonClient(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/app/rest/models/1/model-json') .reply(200, { elements: [ @@ -87,7 +86,7 @@ export class ModelJsonBpmMock extends BaseMock { } get200HistoricEditorDisplayJsonClient(): void { - nock('https://127.0.0.1:9999', { encodedQueryParams: true }) + mockHost('https://127.0.0.1:9999') .get('/activiti-app/app/rest/models/1/history/1/model-json') .reply(200, { elements: [ diff --git a/lib/js-api/test/mockObjects/process-services/models.mock.ts b/lib/js-api/test/mockObjects/process-services/models.mock.ts index 63d4261b63..4f37349cc1 100644 --- a/lib/js-api/test/mockObjects/process-services/models.mock.ts +++ b/lib/js-api/test/mockObjects/process-services/models.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class ModelsMock extends BaseMock { get200getModels(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/models') .query({ filter: 'myReusableForms', modelType: '2' }) .reply(200, { diff --git a/lib/js-api/test/mockObjects/process-services/process-instance-variables.mock.ts b/lib/js-api/test/mockObjects/process-services/process-instance-variables.mock.ts index e1dc6d5efe..74d08b03c2 100644 --- a/lib/js-api/test/mockObjects/process-services/process-instance-variables.mock.ts +++ b/lib/js-api/test/mockObjects/process-services/process-instance-variables.mock.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; const fakeVariable1 = { @@ -34,13 +33,13 @@ const fakeVariablesList = [fakeVariable1, fakeVariable2]; export class ProcessInstanceVariablesMock extends BaseMock { addListProcessInstanceVariables200Response(processInstanceId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/process-instances/' + processInstanceId + '/variables') .reply(200, fakeVariablesList); } addListProcessInstanceVariables500Response(processInstanceId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/process-instances/' + processInstanceId + '/variables') .reply(500, { messageKey: 'UNKNOWN', @@ -49,13 +48,13 @@ export class ProcessInstanceVariablesMock extends BaseMock { } addPutProcessInstanceVariables200Response(processInstanceId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put('/activiti-app/api/enterprise/process-instances/' + processInstanceId + '/variables') .reply(200, fakeVariablesList); } addPutProcessInstanceVariables500Response(processInstanceId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put('/activiti-app/api/enterprise/process-instances/' + processInstanceId + '/variables') .reply(500, { messageKey: 'UNKNOWN', @@ -64,13 +63,13 @@ export class ProcessInstanceVariablesMock extends BaseMock { } addGetProcessInstanceVariable200Response(processInstanceId: string, variableName: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/process-instances/' + processInstanceId + '/variables/' + variableName) .reply(200, fakeVariable1); } addGetProcessInstanceVariable500Response(processInstanceId: string, variableName: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/process-instances/' + processInstanceId + '/variables/' + variableName) .reply(500, { messageKey: 'UNKNOWN', @@ -79,13 +78,13 @@ export class ProcessInstanceVariablesMock extends BaseMock { } addUpdateProcessInstanceVariable200Response(processInstanceId: string, variableName: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put('/activiti-app/api/enterprise/process-instances/' + processInstanceId + '/variables/' + variableName) .reply(200, fakeVariable1); } addUpdateProcessInstanceVariable500Response(processInstanceId: string, variableName: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put('/activiti-app/api/enterprise/process-instances/' + processInstanceId + '/variables/' + variableName) .reply(500, { messageKey: 'UNKNOWN', @@ -94,13 +93,13 @@ export class ProcessInstanceVariablesMock extends BaseMock { } addDeleteProcessInstanceVariable200Response(processInstanceId: string, variableName: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .delete('/activiti-app/api/enterprise/process-instances/' + processInstanceId + '/variables/' + variableName) .reply(200); } addDeleteProcessInstanceVariable500Response(processInstanceId: string, variableName: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .delete('/activiti-app/api/enterprise/process-instances/' + processInstanceId + '/variables/' + variableName) .reply(500, { messageKey: 'UNKNOWN', diff --git a/lib/js-api/test/mockObjects/process-services/process.mock.ts b/lib/js-api/test/mockObjects/process-services/process.mock.ts index 2206373db7..faba28318f 100644 --- a/lib/js-api/test/mockObjects/process-services/process.mock.ts +++ b/lib/js-api/test/mockObjects/process-services/process.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class ProcessMock extends BaseMock { get200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/activiti-app/api/enterprise/process-instances/query') .reply(200, { size: 2, @@ -82,7 +81,7 @@ export class ProcessMock extends BaseMock { } get200getProcessDefinitionStartForm(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/process-definitions/testProcess%3A1%3A7504/start-form') .reply(200, { id: 2002, diff --git a/lib/js-api/test/mockObjects/process-services/profile.mock.ts b/lib/js-api/test/mockObjects/process-services/profile.mock.ts index bf5c65b2dc..1673526ccc 100644 --- a/lib/js-api/test/mockObjects/process-services/profile.mock.ts +++ b/lib/js-api/test/mockObjects/process-services/profile.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class ProfileMock extends BaseMock { get200getProfile(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/profile') .reply(200, { id: 1, @@ -94,10 +93,10 @@ export class ProfileMock extends BaseMock { } get401getProfile(): void { - nock(this.host, { encodedQueryParams: true }).get('/activiti-app/api/enterprise/profile').reply(401); + this.mock().get('/activiti-app/api/enterprise/profile').reply(401); } get200getProfilePicture(): void { - nock(this.host, { encodedQueryParams: true }).get('/activiti-app/api/enterprise/profile-picture').reply(200, 'BUFFERSIZE'); + this.mock().get('/activiti-app/api/enterprise/profile-picture').reply(200, 'BUFFERSIZE'); } } diff --git a/lib/js-api/test/mockObjects/process-services/reports.mock.ts b/lib/js-api/test/mockObjects/process-services/reports.mock.ts index 31fe986ae3..3de855c796 100644 --- a/lib/js-api/test/mockObjects/process-services/reports.mock.ts +++ b/lib/js-api/test/mockObjects/process-services/reports.mock.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import nock from 'nock'; +// cspell:ignore collapseable + import { BaseMock } from '../base.mock'; const fakeReportList = [ @@ -154,58 +155,56 @@ const fakeProcessDefinitionsNoApp: any[] = [ export class ReportsMock extends BaseMock { get200ResponseCreateDefaultReport(): void { - nock(this.host, { encodedQueryParams: true }).post('/activiti-app/app/rest/reporting/default-reports').reply(200); + this.mock().post('/activiti-app/app/rest/reporting/default-reports').reply(200); } get200ResponseTasksByProcessDefinitionId(reportId: string, processDefinitionId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/app/rest/reporting/report-params/' + reportId + '/tasks') .query({ processDefinitionId }) .reply(200, ['Fake Task 1', 'Fake Task 2', 'Fake Task 3']); } get200ResponseReportList(): void { - nock(this.host, { encodedQueryParams: true }).get('/activiti-app/app/rest/reporting/reports').reply(200, fakeReportList); + this.mock().get('/activiti-app/app/rest/reporting/reports').reply(200, fakeReportList); } get200ResponseReportParams(reportId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/app/rest/reporting/report-params/' + reportId) .reply(200, fakeReportParams); } get200ResponseReportsByParams(reportId: string, paramsQuery: { status: string }): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/activiti-app/app/rest/reporting/report-params/' + reportId, paramsQuery) .reply(200, fakeChartReports); } get200ResponseProcessDefinitions(): void { - nock(this.host, { encodedQueryParams: true }) - .get('/activiti-app/app/rest/reporting/process-definitions') - .reply(200, fakeProcessDefinitionsNoApp); + this.mock().get('/activiti-app/app/rest/reporting/process-definitions').reply(200, fakeProcessDefinitionsNoApp); } get200ResponseUpdateReport(reportId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put('/activiti-app/app/rest/reporting/reports/' + reportId) .reply(200); } get200ResponseExportReport(reportId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/activiti-app/app/rest/reporting/reports/' + reportId + '/export-to-csv') .reply(200, 'CSV'); } get200ResponseSaveReport(reportId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/activiti-app/app/rest/reporting/reports/' + reportId) .reply(200); } get200ResponseDeleteReport(reportId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .delete('/activiti-app/app/rest/reporting/reports/' + reportId) .reply(200); } diff --git a/lib/js-api/test/mockObjects/process-services/task-form.mock.ts b/lib/js-api/test/mockObjects/process-services/task-form.mock.ts index 097802630a..3945664565 100644 --- a/lib/js-api/test/mockObjects/process-services/task-form.mock.ts +++ b/lib/js-api/test/mockObjects/process-services/task-form.mock.ts @@ -15,42 +15,14 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class TaskFormMock extends BaseMock { get200getTaskFormVariables(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/task-forms/5028/variables') - .reply( - 200, - [{ id: 'initiator', type: 'string', value: '1001' }], - [ - 'Server', - 'Apache-Coyote/1.1', - 'set-cookie', - 'ACTIVITI_REMEMBER_ME=NjdOdGwvcUtFTkVEczQyMGh4WFp5QT09OmpUL1UwdFVBTC94QTJMTFFUVFgvdFE9PQ', - 'X-Content-Type-Options', - 'nosniff', - 'X-XSS-Protection', - '1; mode=block', - 'Cache-Control', - 'no-cache, no-store, max-age=0, must-revalidate', - 'Pragma', - 'no-cache', - 'Expires', - '0', - 'X-Frame-Options', - 'SAMEORIGIN', - 'Content-Type', - 'application/json', - 'Transfer-Encoding', - 'chunked', - 'Date', - 'Tue, 01 Nov 2016 19:43:36 GMT', - 'Connection', - 'close' - ] - ); + .reply(200, [{ id: 'initiator', type: 'string', value: '1001' }], { + 'set-cookie': 'ACTIVITI_REMEMBER_ME=NjdOdGwvcUtFTkVEczQyMGh4WFp5QT09OmpUL1UwdFVBTC94QTJMTFFUVFgvdFE9PQ' + }); } } diff --git a/lib/js-api/test/mockObjects/process-services/tasks.mock.ts b/lib/js-api/test/mockObjects/process-services/tasks.mock.ts index c3698bda23..ed61caebf3 100644 --- a/lib/js-api/test/mockObjects/process-services/tasks.mock.ts +++ b/lib/js-api/test/mockObjects/process-services/tasks.mock.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; const formValues = [ @@ -42,7 +41,7 @@ const formValues = [ export class TasksMock extends BaseMock { get200Response(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/activiti-app/api/enterprise/tasks/query', {}) .reply(200, { size: 2, @@ -128,7 +127,7 @@ export class TasksMock extends BaseMock { } get200ResponseGetTask(taskId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/tasks/' + taskId) .reply(200, { id: '10', @@ -166,14 +165,14 @@ export class TasksMock extends BaseMock { } get400TaskFilter(): void { - nock(this.host, { encodedQueryParams: true }).post('/activiti-app/api/enterprise/tasks/filter', {}).reply(400, { + this.mock().post('/activiti-app/api/enterprise/tasks/filter', {}).reply(400, { message: 'A valid filterId or filter params must be provided', messageKey: 'GENERAL.ERROR.BAD-REQUEST' }); } get200TaskFilter(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .post('/activiti-app/api/enterprise/tasks/filter', { appDefinitionId: 1 }) .reply(200, { size: 2, @@ -249,7 +248,7 @@ export class TasksMock extends BaseMock { } get404CompleteTask(taskId: string): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .put('/activiti-app/api/enterprise/tasks/' + taskId + '/action/complete') .reply(404, { message: 'Task with id: ' + taskId + ' does not exist', @@ -258,7 +257,7 @@ export class TasksMock extends BaseMock { } get200CreateTask(name: string): void { - nock(this.host, { encodedQueryParams: true }).post('/activiti-app/api/enterprise/tasks', { name }).reply(200, { + this.mock().post('/activiti-app/api/enterprise/tasks', { name }).reply(200, { id: '10001', name: 'test-name', description: null, @@ -293,7 +292,7 @@ export class TasksMock extends BaseMock { } get200getTaskForm(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/task-forms/2518') .reply(200, { id: 1, @@ -1033,10 +1032,10 @@ export class TasksMock extends BaseMock { } get200getRestFieldValuesColumn(): void { - nock(this.host, { encodedQueryParams: true }).get('/activiti-app/api/enterprise/task-forms/1/form-values/label/user').reply(200, formValues); + this.mock().get('/activiti-app/api/enterprise/task-forms/1/form-values/label/user').reply(200, formValues); } get200getRestFieldValues(): void { - nock(this.host, { encodedQueryParams: true }).get('/activiti-app/api/enterprise/task-forms/2/form-values/label').reply(200, formValues); + this.mock().get('/activiti-app/api/enterprise/task-forms/2/form-values/label').reply(200, formValues); } } diff --git a/lib/js-api/test/mockObjects/process-services/user-filters.mock.ts b/lib/js-api/test/mockObjects/process-services/user-filters.mock.ts index 9f4d0f5d57..22937117f9 100644 --- a/lib/js-api/test/mockObjects/process-services/user-filters.mock.ts +++ b/lib/js-api/test/mockObjects/process-services/user-filters.mock.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import nock from 'nock'; import { BaseMock } from '../base.mock'; export class UserFiltersMock extends BaseMock { get200getUserTaskFilters(): void { - nock(this.host, { encodedQueryParams: true }) + this.mock() .get('/activiti-app/api/enterprise/filters/tasks') .query({ appId: '1' }) .reply(200, { diff --git a/lib/js-api/test/oauth2Auth.spec.ts b/lib/js-api/test/oauth2Auth.spec.ts index 894420dc96..f36a04fae7 100644 --- a/lib/js-api/test/oauth2Auth.spec.ts +++ b/lib/js-api/test/oauth2Auth.spec.ts @@ -19,6 +19,7 @@ import assert from 'assert'; import { AlfrescoApi, ContentApi, Oauth2Auth } from '../src'; import { EcmAuthMock, OAuthMock } from './mockObjects'; import { jest } from '@jest/globals'; +import * as browserUtils from '../src/utils/is-browser'; describe('Oauth2 test', () => { let alfrescoJsApi: AlfrescoApi; @@ -65,7 +66,7 @@ describe('Oauth2 test', () => { afterEach(() => { authResponseMock.cleanAll(); - jest.clearAllMocks(); + jest.restoreAllMocks(); }); describe('Discovery urls', () => { @@ -189,7 +190,7 @@ describe('Oauth2 test', () => { }); it('should refresh token when the login not use the implicitFlow ', (done) => { - jest.spyOn(window, 'document', 'get').mockReturnValueOnce(undefined); + jest.spyOn(browserUtils, 'isBrowser').mockReturnValue(false); oauth2Mock.get200Response(); const oauth2Auth = new Oauth2Auth( @@ -224,7 +225,7 @@ describe('Oauth2 test', () => { }); it('should not hang the app also if the logout is missing', (done) => { - jest.spyOn(window, 'document', 'get').mockReturnValueOnce(undefined); + jest.spyOn(browserUtils, 'isBrowser').mockReturnValue(false); oauth2Mock.get200Response(); const oauth2Auth = new Oauth2Auth( diff --git a/lib/js-api/test/oauth2AuthImplicitFlow.spec.ts b/lib/js-api/test/oauth2AuthImplicitFlow.spec.ts index e15e5d23d2..98c4555d90 100644 --- a/lib/js-api/test/oauth2AuthImplicitFlow.spec.ts +++ b/lib/js-api/test/oauth2AuthImplicitFlow.spec.ts @@ -23,29 +23,35 @@ describe('Oauth2 Implicit flow test', () => { let alfrescoJsApi: AlfrescoApi; beforeEach(() => { + const mockLocation: any = { + ancestorOrigins: null, + hash: '', + host: 'dummy.com', + port: '80', + protocol: 'http:', + hostname: 'dummy.com', + href: 'http://localhost/', + origin: 'dummy.com', + pathname: null, + search: null, + assign: (url: string) => { + mockLocation.href = url; + }, + reload: null, + replace: null + }; + + (globalThis as any).window = { location: mockLocation, addEventListener: () => {}, removeEventListener: () => {} }; + (globalThis as any).document = { getElementById: () => null, cookie: '' }; + alfrescoJsApi = new AlfrescoApi({ hostEcm: '' }); - Object.defineProperty(window, 'location', { - writable: true, - value: { - ancestorOrigins: null, - hash: '', - host: 'dummy.com', - port: '80', - protocol: 'http:', - hostname: 'dummy.com', - href: 'http://localhost/', - origin: 'dummy.com', - pathname: null, - search: null, - assign: (url: string) => { - window.location.href = url; - }, - reload: null, - replace: null - } - }); + }); + + afterEach(() => { + delete (globalThis as any).window; + delete (globalThis as any).document; }); it('should throw an error if redirectUri is not present', (done) => { diff --git a/lib/js-api/test/superagentHttpClient.spec.ts b/lib/js-api/test/superagentHttpClient.spec.ts deleted file mode 100644 index 5da64b7386..0000000000 --- a/lib/js-api/test/superagentHttpClient.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -/*! - * @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 assert from 'assert'; -import { SuperagentHttpClient } from '../src/superagentHttpClient'; -import { Response } from 'superagent'; - -describe('SuperagentHttpClient', () => { - describe('#buildRequest', () => { - const client = new SuperagentHttpClient(); - - it('should create a request with response type blob', () => { - const queryParams = {}; - const headerParams = {}; - const formParams = {}; - - const contentTypes = 'application/json'; - const accepts = 'application/json'; - const responseType = 'blob'; - const url = '/fake-api/enterprise/process-instances/'; - const httpMethod = 'GET'; - const securityOptions = { - isBpmRequest: false, - enableCsrf: false, - withCredentials: false, - authentications: { - basicAuth: { - ticket: '' - }, - type: 'basic' - }, - defaultHeaders: {} - }; - - const response: any = client['buildRequest']( - httpMethod, - url, - queryParams, - headerParams, - formParams, - null, - contentTypes, - accepts, - responseType, - null, - null, - securityOptions - ); - - assert.equal(response.url, '/fake-api/enterprise/process-instances/'); - assert.equal(response.header.Accept, 'application/json'); - assert.equal(response.header['Content-Type'], 'application/json'); - assert.equal(response._responseType, 'blob'); - }); - }); - - describe('#deserialize', () => { - it('should the deserializer return an array of object when the response is an array', () => { - const data = { - body: [ - { - id: '1', - name: 'test1' - }, - { - id: '2', - name: 'test2' - } - ] - } as Response; - const result = SuperagentHttpClient['deserialize'](data); - const isArray = Array.isArray(result); - assert.equal(isArray, true); - }); - }); -}); diff --git a/lib/js-api/test/upload.spec.ts b/lib/js-api/test/upload.spec.ts index 5083b97278..c96fc7ae55 100644 --- a/lib/js-api/test/upload.spec.ts +++ b/lib/js-api/test/upload.spec.ts @@ -129,13 +129,17 @@ describe('Upload', () => { }); }); - it('Upload should fire progress event during the upload', (done) => { + // Upload progress events are emitted via the XHR path in FetchHttpClient. + // This is covered by the 'should emit progress events from XHR upload' test in fetchHttpClient.spec.ts. + // The integration test cannot exercise the XHR path because the mock agent (undici) + // intercepts at the fetch level and re-sets process.__test_fetch__ via getGlobalMockAgent(). + it('Upload should fire success event on completion', (done) => { uploadMock.get201CreationFile(); const file = createTestFileStream('testFile.txt'); const uploadPromise: any = uploadApi.uploadFile(file); - uploadPromise.once('progress', () => done()); + uploadPromise.once('success', () => done()); }); it('Multiple Upload should fire progress events on the right promise during the upload', (done) => { diff --git a/package-lock.json b/package-lock.json index c10bf6389c..8ee1cc65c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,6 @@ "pdfjs-dist": "5.1.91", "raphael": "2.3.0", "rxjs": "7.8.2", - "superagent": "^9.0.1", "tslib": "2.8.1", "zone.js": "0.15.0" }, @@ -73,7 +72,6 @@ "@types/minimatch": "5.1.2", "@types/node": "^18.16.9", "@types/pdfjs-dist": "2.10.378", - "@types/superagent": "^4.1.22", "@typescript-eslint/eslint-plugin": "8.57.2", "@typescript-eslint/parser": "8.57.2", "@typescript-eslint/typescript-estree": "8.41.0", @@ -109,7 +107,6 @@ "lint-staged": "15.5.2", "moment": "^2.29.4", "ng-packagr": "19.2.2", - "nock": "13.5.5", "nx": "22.6.5", "prettier": "3.6.2", "react": "^19.2.4", @@ -122,10 +119,11 @@ "stylelint-config-standard-scss": "^13.1.0", "ts-node": "^10.9.2", "typescript": "5.8.3", + "undici": "^8.2.0", "webpack": "5.105.3" }, "engines": { - "node": ">=18.0.0" + "node": ">=22.19.0" } }, "lib/eslint-angular": { @@ -6987,6 +6985,16 @@ } } }, + "node_modules/@module-federation/dts-plugin/node_modules/undici": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz", + "integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/@module-federation/enhanced": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-2.3.3.tgz", @@ -7986,18 +7994,6 @@ "@angular/core": ">=16" } }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -9469,15 +9465,6 @@ "yargs-parser": "21.1.1" } }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", - "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "^1.1.5" - } - }, "node_modules/@parcel/watcher": { "version": "2.5.6", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", @@ -11180,13 +11167,6 @@ "@types/node": "*" } }, - "node_modules/@types/cookiejar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/cors": { "version": "2.8.19", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", @@ -11538,17 +11518,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/superagent": { - "version": "4.1.24", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.24.tgz", - "integrity": "sha512-mEafCgyKiMFin24SDzWN7yAADt4gt6YawFiNMp0QS5ZPboORfyxFt0s3VzJKhTaKg9py/4FUmrHLTNfJKt9Rbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cookiejar": "*", - "@types/node": "*" - } - }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -13452,6 +13421,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, "license": "MIT" }, "node_modules/assertion-error": { @@ -13508,6 +13478,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, "license": "MIT" }, "node_modules/autoprefixer": { @@ -14228,6 +14199,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -14241,6 +14213,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -14839,6 +14812,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -14904,15 +14878,6 @@ "dev": true, "license": "MIT" }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -15117,12 +15082,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "license": "MIT" - }, "node_modules/copy-anything": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", @@ -16681,6 +16640,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -16778,6 +16738,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, "license": "ISC", "dependencies": { "asap": "^2.0.0", @@ -16987,6 +16948,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -17342,6 +17304,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -17351,6 +17314,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -17367,6 +17331,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -17379,6 +17344,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -18561,12 +18527,6 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "license": "MIT" }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "license": "MIT" - }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -19140,6 +19100,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -19164,23 +19125,6 @@ "node": ">=12.20.0" } }, - "node_modules/formidable": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", - "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", - "license": "MIT", - "dependencies": { - "@paralleldrive/cuid2": "^2.2.2", - "dezalgo": "^1.0.4", - "once": "^1.4.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -19264,6 +19208,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -19356,6 +19301,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -19390,6 +19336,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -19686,6 +19633,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -19858,6 +19806,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -19870,6 +19819,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -19898,6 +19848,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -25467,13 +25418,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "license": "MIT" }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC" - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -26995,6 +26939,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -27085,6 +27030,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -27131,6 +27077,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -27143,6 +27090,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -27614,21 +27562,6 @@ "tslib": "^2.0.3" } }, - "node_modules/nock": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.5.tgz", - "integrity": "sha512-XKYnqUrCwXC8DGG1xX4YH5yNIrlh9c065uaMZZHUoeUUINTOyt+x/G+ezYk0Ft6ExSREVIs+qBJDK503viTfFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "propagate": "^2.0.0" - }, - "engines": { - "node": ">= 10.13" - } - }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -28273,6 +28206,7 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -30218,16 +30152,6 @@ "react-is": "^16.13.1" } }, - "node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -30351,6 +30275,7 @@ "version": "6.14.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -32348,6 +32273,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -32367,6 +32293,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -32383,6 +32310,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -32401,6 +32329,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -33860,39 +33789,6 @@ "which": "bin/which" } }, - "node_modules/superagent": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", - "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", - "deprecated": "Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net", - "license": "MIT", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^3.5.1", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -35456,13 +35352,13 @@ } }, "node_modules/undici": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz", - "integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.2.0.tgz", + "integrity": "sha512-Z+4Hx9GE26Lh9Upwfnc8C7SsrpBPGaM/Gm6kMFtiG7c+5IvQKlXi/t+9x9DrrCh29cww5TSP9YdVaBcnLDs5fQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=20.18.1" + "node": ">=22.19.0" } }, "node_modules/undici-types": { diff --git a/package.json b/package.json index 10a2857bbb..6da646f697 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "pdfjs-dist": "5.1.91", "raphael": "2.3.0", "rxjs": "7.8.2", - "superagent": "^9.0.1", "tslib": "2.8.1", "zone.js": "0.15.0" }, @@ -102,7 +101,6 @@ "@types/minimatch": "5.1.2", "@types/node": "^18.16.9", "@types/pdfjs-dist": "2.10.378", - "@types/superagent": "^4.1.22", "@typescript-eslint/eslint-plugin": "8.57.2", "@typescript-eslint/parser": "8.57.2", "@typescript-eslint/typescript-estree": "8.41.0", @@ -138,7 +136,6 @@ "lint-staged": "15.5.2", "moment": "^2.29.4", "ng-packagr": "19.2.2", - "nock": "13.5.5", "nx": "22.6.5", "prettier": "3.6.2", "react": "^19.2.4", @@ -151,6 +148,7 @@ "stylelint-config-standard-scss": "^13.1.0", "ts-node": "^10.9.2", "typescript": "5.8.3", + "undici": "^8.2.0", "webpack": "5.105.3" }, "overrides": { @@ -163,7 +161,7 @@ }, "license": "Apache-2.0", "engines": { - "node": ">=18.0.0" + "node": ">=22.19.0" }, "module": "./index.js", "typings": "./index.d.ts"