From 90a2f9ac853f6a154d2c31dd0f6d081e8d832016 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 1 Dec 2025 16:28:34 +0100 Subject: [PATCH 01/58] wip --- .../service-binding-to-destination.spec.ts | 91 ++++++++- .../service-binding-to-destination.ts | 20 +- .../src/scp-cf/identity-service.spec.ts | 182 +++++++++++++++++- .../src/scp-cf/identity-service.ts | 129 +++++++++++++ 4 files changed, 411 insertions(+), 11 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index 5fb5251973..c6e75ed06d 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -1,12 +1,38 @@ -import { serviceToken } from '../token-accessor'; import { resolveServiceBinding } from '../environment-accessor/service-bindings'; +import { getIasClientCredentialsToken } from '../identity-service'; import { decodeJwt } from '../jwt'; +import { serviceToken } from '../token-accessor'; import { transformServiceBindingToClientCredentialsDestination, transformServiceBindingToDestination } from './service-binding-to-destination'; +jest.mock('../identity-service', () => ({ + getIasClientCredentialsToken: jest.fn() +})); + +jest.mock('../token-accessor', () => ({ + serviceToken: jest.fn() +})); + +jest.mock('../jwt', () => ({ + decodeJwt: jest.fn() +})); + const services = { + identity: [ + { + name: 'my-identity-service', + label: 'identity', + tags: ['identity'], + credentials: { + url: 'https://tenant.accounts.ondemand.com', + clientid: 'identity-clientid', + certificate: '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----', + key: '-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----' + } + } + ], destination: [ { name: 'my-destination-service1', @@ -97,17 +123,16 @@ const services = { ] }; -jest.mock('../token-accessor', () => ({ - serviceToken: jest.fn() -})); - -jest.mock('../jwt', () => ({ - decodeJwt: jest.fn() -})); - describe('service binding to destination', () => { beforeAll(() => { (serviceToken as jest.Mock).mockResolvedValue('access-token'); + (getIasClientCredentialsToken as jest.Mock).mockResolvedValue({ + access_token: 'ias-access-token', + token_type: 'Bearer', + expires_in: 3600, + scope: 'openid', + jti: 'mock-jti' + }); (decodeJwt as jest.Mock).mockReturnValue({ exp: 1596549600 }); process.env.VCAP_SERVICES = JSON.stringify(services); }); @@ -255,4 +280,52 @@ describe('service binding to destination', () => { }) ); }); + + it('transforms identity (IAS) service binding', async () => { + const destination = await transformServiceBindingToDestination( + resolveServiceBinding('identity') + ); + expect(destination).toEqual( + expect.objectContaining({ + url: 'https://tenant.accounts.ondemand.com', + name: 'my-identity-service', + authentication: 'OAuth2ClientCredentials', + authTokens: expect.arrayContaining([ + expect.objectContaining({ + value: 'ias-access-token', + type: 'bearer' + }) + ]) + }) + ); + expect(getIasClientCredentialsToken).toHaveBeenCalledWith( + expect.objectContaining({ + label: 'identity', + name: 'my-identity-service' + }), + expect.objectContaining({}) + ); + }); + + it('transforms identity (IAS) service binding with resource parameter', async () => { + const destination = await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { resource: 'my-app' } as Parameters[1] + ); + expect(destination).toEqual( + expect.objectContaining({ + url: 'https://tenant.accounts.ondemand.com', + name: 'my-identity-service', + authentication: 'OAuth2ClientCredentials' + }) + ); + expect(getIasClientCredentialsToken).toHaveBeenCalledWith( + expect.objectContaining({ + label: 'identity' + }), + expect.objectContaining({ + resource: 'my-app' + }) + ); + }); }); diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index 38ae7109b8..9769285f12 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -1,5 +1,6 @@ import { serviceToken } from '../token-accessor'; import { decodeJwt } from '../jwt'; +import { getIasClientCredentialsToken } from '../identity-service'; import type { Service } from '../environment-accessor'; import type { ServiceBindingTransformFunction, @@ -21,7 +22,8 @@ export const serviceToDestinationTransformers: Record< workflow: workflowBindingToDestination, 'service-manager': serviceManagerBindingToDestination, xsuaa: xsuaaToDestination, - aicore: aicoreToDestination + aicore: aicoreToDestination, + identity: iasBindingToDestination }; /** @@ -36,6 +38,7 @@ export const serviceToDestinationTransformers: Record< * - service-manager (OAuth2ClientCredentials) * - xsuaa (OAuth2ClientCredentials) * - aicore (OAuth2ClientCredentials) + * - identity (OAuth2ClientCredentials with mTLS or client secret) * Throws an error if the provided service binding is not supported. * @param serviceBinding - The service binding to transform. * @param options - Options used for fetching the destination. @@ -179,6 +182,21 @@ async function xfS4hanaCloudBindingToDestination( }; } +async function iasBindingToDestination( + service: Service, + options?: ServiceBindingTransformOptions & { resource?: string; appTenantId?: string } +): Promise { + const { access_token } = await getIasClientCredentialsToken(service, { + resource: options?.resource, + appTenantId: options?.appTenantId + }); + return buildClientCredentialsDestination( + access_token, + service.credentials.url, + service.name + ); +} + function buildClientCredentialsDestination( token: string, url: string, diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 2332035546..beb030a544 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -1,5 +1,13 @@ +import axios from 'axios'; import { signedJwt } from '../../../../test-resources/test/test-util'; -import { shouldExchangeToken } from './identity-service'; +import { + getIasClientCredentialsToken, + shouldExchangeToken +} from './identity-service'; +import type { Service } from './environment-accessor'; + +jest.mock('axios'); +const mockedAxios = axios as jest.Mocked; describe('shouldExchangeToken', () => { it('should not exchange token from XSUAA', async () => { @@ -37,3 +45,175 @@ describe('shouldExchangeToken', () => { ).toBe(false); }); }); + +describe('getIasClientCredentialsToken', () => { + const mockIasService: Service = { + name: 'my-identity-service', + label: 'identity', + tags: ['identity'], + credentials: { + url: 'https://tenant.accounts.ondemand.com', + clientid: 'test-client-id', + certificate: '-----BEGIN CERTIFICATE-----\ntest-cert\n-----END CERTIFICATE-----', + key: '-----BEGIN RSA PRIVATE KEY-----\ntest-key\n-----END RSA PRIVATE KEY-----' + } + }; + + const mockTokenResponse = { + access_token: 'mock-ias-access-token', + token_type: 'Bearer', + expires_in: 3600, + scope: 'openid', + jti: 'mock-jti' + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('fetches IAS token with mTLS authentication', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + const result = await getIasClientCredentialsToken(mockIasService); + + expect(result).toEqual(mockTokenResponse); + expect(mockedAxios.request).toHaveBeenCalledWith( + expect.objectContaining({ + method: 'post', + url: 'https://tenant.accounts.ondemand.com/oauth2/token', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + data: 'grant_type=client_credentials&client_id=test-client-id', + httpsAgent: expect.any(Object) + }) + ); + }); + + it('fetches IAS token with client secret authentication', async () => { + const serviceWithSecret: Service = { + ...mockIasService, + credentials: { + url: 'https://tenant.accounts.ondemand.com', + clientid: 'test-client-id', + clientsecret: 'test-client-secret' + } + }; + + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + const result = await getIasClientCredentialsToken(serviceWithSecret); + + expect(result).toEqual(mockTokenResponse); + expect(mockedAxios.request).toHaveBeenCalledWith( + expect.objectContaining({ + method: 'post', + url: 'https://tenant.accounts.ondemand.com/oauth2/token', + headers: expect.objectContaining({ + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: expect.stringContaining('Basic ') + }), + data: 'grant_type=client_credentials&client_id=test-client-id' + }) + ); + }); + + it('includes resource parameter for app2app flow', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + await getIasClientCredentialsToken(mockIasService, { + resource: 'my-app' + }); + + expect(mockedAxios.request).toHaveBeenCalledWith( + expect.objectContaining({ + data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app' + }) + ); + }); + + it('includes appTenantId parameter for multi-tenant scenarios', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + await getIasClientCredentialsToken(mockIasService, { + appTenantId: 'tenant-123' + }); + + expect(mockedAxios.request).toHaveBeenCalledWith( + expect.objectContaining({ + data: 'grant_type=client_credentials&client_id=test-client-id&app_tid=tenant-123' + }) + ); + }); + + it('includes both resource and appTenantId parameters', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + await getIasClientCredentialsToken(mockIasService, { + resource: 'my-app', + appTenantId: 'tenant-123' + }); + + expect(mockedAxios.request).toHaveBeenCalledWith( + expect.objectContaining({ + data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app&app_tid=tenant-123' + }) + ); + }); + + it('throws error when url is missing', async () => { + const invalidService: Service = { + ...mockIasService, + credentials: { + clientid: 'test-client-id', + certificate: 'cert', + key: 'key' + } as any + }; + + await expect( + getIasClientCredentialsToken(invalidService) + ).rejects.toThrow('IAS credentials must contain "url" and "clientid"'); + }); + + it('throws error when clientid is missing', async () => { + const invalidService: Service = { + ...mockIasService, + credentials: { + url: 'https://tenant.accounts.ondemand.com', + certificate: 'cert', + key: 'key' + } as any + }; + + await expect( + getIasClientCredentialsToken(invalidService) + ).rejects.toThrow('IAS credentials must contain "url" and "clientid"'); + }); + + it('throws error when neither certificate/key nor clientsecret is provided', async () => { + const invalidService: Service = { + ...mockIasService, + credentials: { + url: 'https://tenant.accounts.ondemand.com', + clientid: 'test-client-id' + } as any + }; + + await expect( + getIasClientCredentialsToken(invalidService) + ).rejects.toThrow( + 'IAS credentials must contain either "certificate" and "key" for mTLS, or "clientsecret" for client secret authentication' + ); + }); + + it('handles token fetch errors gracefully', async () => { + mockedAxios.request.mockRejectedValue(new Error('Network error')); + + await expect( + getIasClientCredentialsToken(mockIasService) + ).rejects.toThrow( + 'Could not fetch IAS client credentials token for service of type identity' + ); + }); +}); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 68b5d6a33d..eb6d806a09 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -1,5 +1,20 @@ +import https from 'https'; +import { executeWithMiddleware } from '@sap-cloud-sdk/resilience/internal'; +import { resilience } from '@sap-cloud-sdk/resilience'; +import { createLogger } from '@sap-cloud-sdk/util'; +import axios from 'axios'; import { decodeJwt, isXsuaaToken } from './jwt'; +import { resolveServiceBinding } from './environment-accessor'; import type { DestinationOptions } from './destination'; +import type { MiddlewareContext } from '@sap-cloud-sdk/resilience'; +import type { Service, ServiceCredentials } from './environment-accessor'; +import type { RawAxiosRequestConfig } from 'axios'; +import type { ClientCredentialsResponse } from './xsuaa-service-types'; + +const logger = createLogger({ + package: 'connectivity', + messageContext: 'identity-service' +}); /** * @internal @@ -15,3 +30,117 @@ export function shouldExchangeToken(options: DestinationOptions): boolean { !isXsuaaToken(decodeJwt(options.jwt)) ); } + +interface IasParameters { + serviceCredentials: ServiceCredentials; + resource?: string; + appTenantId?: string; +} + +/** + * Make a client credentials request against the IAS OAuth2 endpoint. + * Supports both certificate-based (mTLS) and client secret authentication. + * @param service - Service as it is defined in the environment variable. + * @param options - Options for token fetching. + * @returns Client credentials token response. + * @internal + */ +export async function getIasClientCredentialsToken( + service: string | Service, + options?: { resource?: string; appTenantId?: string } +): Promise { + const resolvedService = resolveServiceBinding(service); + + const fnArgument: IasParameters = { + serviceCredentials: resolvedService.credentials, + resource: options?.resource, + appTenantId: options?.appTenantId + }; + + const iasPromise = async function ( + arg: IasParameters + ): Promise { + const { url, clientid, certificate, key, clientsecret } = + arg.serviceCredentials; + + if (!url || !clientid) { + throw new Error( + 'IAS credentials must contain "url" and "clientid" properties.' + ); + } + + // Build form data + const params = new URLSearchParams({ + grant_type: 'client_credentials', + client_id: clientid + }); + + if (arg.resource) { + const fullResource = `urn:sap:identity:application:provider:name:${arg.resource}`; + params.append('resource', fullResource); + logger.debug( + `Fetching IAS token with resource parameter: ${fullResource}` + ); + } + + if (arg.appTenantId) { + params.append('app_tid', arg.appTenantId); + logger.debug( + `Fetching IAS token with app_tid parameter: ${arg.appTenantId}` + ); + } + + const tokenUrl = `${url}/oauth2/token`; + const headers: Record = { + 'Content-Type': 'application/x-www-form-urlencoded' + }; + + const requestConfig: RawAxiosRequestConfig = { + method: 'post', + url: tokenUrl, + headers, + data: params.toString() + }; + + // Determine authentication method + if (certificate && key) { + // mTLS authentication + logger.debug('Using certificate-based authentication for IAS token.'); + requestConfig.httpsAgent = new https.Agent({ + cert: certificate, + key + }); + } else if (clientsecret) { + // Client secret authentication + logger.debug('Using client secret authentication for IAS token.'); + const credentials = Buffer.from(`${clientid}:${clientsecret}`).toString( + 'base64' + ); + headers['Authorization'] = `Basic ${credentials}`; + } else { + throw new Error( + 'IAS credentials must contain either "certificate" and "key" for mTLS, or "clientsecret" for client secret authentication.' + ); + } + + const response = await axios.request(requestConfig); + return response.data; + }; + + return executeWithMiddleware< + IasParameters, + ClientCredentialsResponse, + MiddlewareContext + >(resilience(), { + fn: iasPromise, + fnArgument, + context: { + uri: fnArgument.serviceCredentials.url, + tenantId: fnArgument.serviceCredentials.tenantid + } + }).catch(err => { + throw new Error( + `Could not fetch IAS client credentials token for service of type ${resolvedService.label}: ${err.message}` + ); + }); +} From b2d80f42d642718af3932e4187a9be6b55e65e58 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 1 Dec 2025 16:43:43 +0100 Subject: [PATCH 02/58] support reading user_uuid from jwtPayload --- .../connectivity/src/scp-cf/jwt/jwt.spec.ts | 28 ++++++++++++++++++- packages/connectivity/src/scp-cf/jwt/jwt.ts | 12 ++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts b/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts index 258f1f92f6..cdf299f413 100644 --- a/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts +++ b/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts @@ -4,9 +4,35 @@ import { mockServiceBindings, signedJwtForVerification } from '../../../../../test-resources/test/test-util'; -import { audiences, retrieveJwt, isXsuaaToken, decodeJwt } from './jwt'; +import { + audiences, + decodeJwt, + isXsuaaToken, + retrieveJwt, + userId +} from './jwt'; describe('jwt', () => { + describe('userId', () => { + it('extracts user_id from XSUAA tokens', () => { + const xsuaaPayload = { user_id: 'xsuaa-user-123' }; + expect(userId(xsuaaPayload)).toBe('xsuaa-user-123'); + }); + + it('extracts user_uuid from IAS tokens', () => { + const iasPayload = { user_uuid: 'ias-user-uuid-456' }; + expect(userId(iasPayload)).toBe('ias-user-uuid-456'); + }); + + it('prefers user_uuid over user_id when both are present', () => { + const mixedPayload = { + user_uuid: 'ias-user-uuid-456', + user_id: 'xsuaa-user-123' + }; + expect(userId(mixedPayload)).toBe('ias-user-uuid-456'); + }); + }); + describe('isXsuaaToken()', () => { it('returns true if the token was issued by XSUAA', () => { const jwt = decodeJwt( diff --git a/packages/connectivity/src/scp-cf/jwt/jwt.ts b/packages/connectivity/src/scp-cf/jwt/jwt.ts index 92271fe043..43cd8f5024 100644 --- a/packages/connectivity/src/scp-cf/jwt/jwt.ts +++ b/packages/connectivity/src/scp-cf/jwt/jwt.ts @@ -27,12 +27,18 @@ function makeArray(val: string | string[] | undefined): string[] { /** * @internal * Get the user ID from the JWT payload. + * For XSUAA tokens, this is `user_id`. + * For IAS tokens, this is `user_uuid`. * @param jwtPayload - Token payload to read the user ID from. * @returns The user ID, if available. */ -export function userId({ user_id }: JwtPayload): string { - logger.debug(`JWT user_id is: ${user_id}.`); - return user_id; +export function userId(jwtPayload: JwtPayload): string { + // IAS tokens use user_uuid, XSUAA tokens use user_id + const id = jwtPayload.user_uuid || jwtPayload.user_id; + logger.debug( + `JWT user identifier is: ${id} (from ${jwtPayload.user_uuid ? 'user_uuid (IAS)' : 'user_id (XSUAA)'}).` + ); + return id; } /** From 0ed94aaff15aa0771a1b155e78641f0c06501450 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 1 Dec 2025 16:59:27 +0100 Subject: [PATCH 03/58] add extraParams option field --- .../service-binding-to-destination.ts | 9 ++++++-- .../src/scp-cf/identity-service.spec.ts | 22 +++++++++++++++++++ .../src/scp-cf/identity-service.ts | 16 +++++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index 9769285f12..a729e387d7 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -184,11 +184,16 @@ async function xfS4hanaCloudBindingToDestination( async function iasBindingToDestination( service: Service, - options?: ServiceBindingTransformOptions & { resource?: string; appTenantId?: string } + options?: ServiceBindingTransformOptions & { + resource?: string; + appTenantId?: string; + extraParams?: Record; + } ): Promise { const { access_token } = await getIasClientCredentialsToken(service, { resource: options?.resource, - appTenantId: options?.appTenantId + appTenantId: options?.appTenantId, + extraParams: options?.extraParams }); return buildClientCredentialsDestination( access_token, diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index beb030a544..7737ebe9e9 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -161,6 +161,28 @@ describe('getIasClientCredentialsToken', () => { ); }); + it('includes extraParams for additional OAuth2 parameters', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + await getIasClientCredentialsToken(mockIasService, { + extraParams: { + custom_param: 'custom_value', + another_param: 'another_value' + } + }); + + expect(mockedAxios.request).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.stringContaining('custom_param=custom_value') + }) + ); + expect(mockedAxios.request).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.stringContaining('another_param=another_value') + }) + ); + }); + it('throws error when url is missing', async () => { const invalidService: Service = { ...mockIasService, diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index eb6d806a09..bffc52c06d 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -35,26 +35,32 @@ interface IasParameters { serviceCredentials: ServiceCredentials; resource?: string; appTenantId?: string; + extraParams?: Record; } /** * Make a client credentials request against the IAS OAuth2 endpoint. * Supports both certificate-based (mTLS) and client secret authentication. * @param service - Service as it is defined in the environment variable. - * @param options - Options for token fetching. + * @param options - Options for token fetching, including optional resource parameter for app2app, appTenantId for multi-tenant scenarios, and extraParams for additional OAuth2 parameters. * @returns Client credentials token response. * @internal */ export async function getIasClientCredentialsToken( service: string | Service, - options?: { resource?: string; appTenantId?: string } + options?: { + resource?: string; + appTenantId?: string; + extraParams?: Record; + } ): Promise { const resolvedService = resolveServiceBinding(service); const fnArgument: IasParameters = { serviceCredentials: resolvedService.credentials, resource: options?.resource, - appTenantId: options?.appTenantId + appTenantId: options?.appTenantId, + extraParams: options?.extraParams }; const iasPromise = async function ( @@ -90,6 +96,10 @@ export async function getIasClientCredentialsToken( ); } + for (const [paramKey, paramValue] of Object.entries(arg.extraParams || {})) { + params.append(paramKey, paramValue); + } + const tokenUrl = `${url}/oauth2/token`; const headers: Record = { 'Content-Type': 'application/x-www-form-urlencoded' From 5ec465795f0c23b978fb58fa52e238bfb004dc26 Mon Sep 17 00:00:00 2001 From: cloud-sdk-js Date: Tue, 2 Dec 2025 14:30:35 +0000 Subject: [PATCH 04/58] Changes from lint:fix --- .../service-binding-to-destination.spec.ts | 7 ++++-- .../src/scp-cf/identity-service.spec.ts | 23 ++++++++----------- .../src/scp-cf/identity-service.ts | 7 ++++-- .../connectivity/src/scp-cf/jwt/jwt.spec.ts | 8 +------ 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index c6e75ed06d..0f23299c0d 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -28,7 +28,8 @@ const services = { credentials: { url: 'https://tenant.accounts.ondemand.com', clientid: 'identity-clientid', - certificate: '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----', + certificate: + '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----', key: '-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----' } } @@ -310,7 +311,9 @@ describe('service binding to destination', () => { it('transforms identity (IAS) service binding with resource parameter', async () => { const destination = await transformServiceBindingToDestination( resolveServiceBinding('identity'), - { resource: 'my-app' } as Parameters[1] + { resource: 'my-app' } as Parameters< + typeof transformServiceBindingToDestination + >[1] ); expect(destination).toEqual( expect.objectContaining({ diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 7737ebe9e9..4980b6a8ce 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -54,7 +54,8 @@ describe('getIasClientCredentialsToken', () => { credentials: { url: 'https://tenant.accounts.ondemand.com', clientid: 'test-client-id', - certificate: '-----BEGIN CERTIFICATE-----\ntest-cert\n-----END CERTIFICATE-----', + certificate: + '-----BEGIN CERTIFICATE-----\ntest-cert\n-----END CERTIFICATE-----', key: '-----BEGIN RSA PRIVATE KEY-----\ntest-key\n-----END RSA PRIVATE KEY-----' } }; @@ -193,9 +194,9 @@ describe('getIasClientCredentialsToken', () => { } as any }; - await expect( - getIasClientCredentialsToken(invalidService) - ).rejects.toThrow('IAS credentials must contain "url" and "clientid"'); + await expect(getIasClientCredentialsToken(invalidService)).rejects.toThrow( + 'IAS credentials must contain "url" and "clientid"' + ); }); it('throws error when clientid is missing', async () => { @@ -208,9 +209,9 @@ describe('getIasClientCredentialsToken', () => { } as any }; - await expect( - getIasClientCredentialsToken(invalidService) - ).rejects.toThrow('IAS credentials must contain "url" and "clientid"'); + await expect(getIasClientCredentialsToken(invalidService)).rejects.toThrow( + 'IAS credentials must contain "url" and "clientid"' + ); }); it('throws error when neither certificate/key nor clientsecret is provided', async () => { @@ -222,9 +223,7 @@ describe('getIasClientCredentialsToken', () => { } as any }; - await expect( - getIasClientCredentialsToken(invalidService) - ).rejects.toThrow( + await expect(getIasClientCredentialsToken(invalidService)).rejects.toThrow( 'IAS credentials must contain either "certificate" and "key" for mTLS, or "clientsecret" for client secret authentication' ); }); @@ -232,9 +231,7 @@ describe('getIasClientCredentialsToken', () => { it('handles token fetch errors gracefully', async () => { mockedAxios.request.mockRejectedValue(new Error('Network error')); - await expect( - getIasClientCredentialsToken(mockIasService) - ).rejects.toThrow( + await expect(getIasClientCredentialsToken(mockIasService)).rejects.toThrow( 'Could not fetch IAS client credentials token for service of type identity' ); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index bffc52c06d..de98cb8f98 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -96,7 +96,9 @@ export async function getIasClientCredentialsToken( ); } - for (const [paramKey, paramValue] of Object.entries(arg.extraParams || {})) { + for (const [paramKey, paramValue] of Object.entries( + arg.extraParams || {} + )) { params.append(paramKey, paramValue); } @@ -133,7 +135,8 @@ export async function getIasClientCredentialsToken( ); } - const response = await axios.request(requestConfig); + const response = + await axios.request(requestConfig); return response.data; }; diff --git a/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts b/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts index cdf299f413..71716767b9 100644 --- a/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts +++ b/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts @@ -4,13 +4,7 @@ import { mockServiceBindings, signedJwtForVerification } from '../../../../../test-resources/test/test-util'; -import { - audiences, - decodeJwt, - isXsuaaToken, - retrieveJwt, - userId -} from './jwt'; +import { audiences, decodeJwt, isXsuaaToken, retrieveJwt, userId } from './jwt'; describe('jwt', () => { describe('userId', () => { From a541e186496c8e0f59dc0c76fcc1c283d117f16a Mon Sep 17 00:00:00 2001 From: David Knaack Date: Thu, 4 Dec 2025 09:40:36 +0100 Subject: [PATCH 05/58] add workaround --- packages/connectivity/src/scp-cf/identity-service.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index de98cb8f98..cba4eb8810 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -96,6 +96,11 @@ export async function getIasClientCredentialsToken( ); } + // Workaround for IAS issue + // if( onBehalfOf == OnBehalfOf.NAMED_USER_CURRENT_TENANT ) { + params.append('refresh_token', '0'); + // }; + for (const [paramKey, paramValue] of Object.entries( arg.extraParams || {} )) { From 24cd0a716fc9c4e1a6839bd3abdb7bfadc38eaf7 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Thu, 4 Dec 2025 16:14:05 +0100 Subject: [PATCH 06/58] chore: add targetURL option to `iasBindingToDestination` --- .../destination/service-binding-to-destination.ts | 13 +++++++------ .../connectivity/src/scp-cf/identity-service.ts | 14 +++++--------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index a729e387d7..ae80bfb53c 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -184,20 +184,21 @@ async function xfS4hanaCloudBindingToDestination( async function iasBindingToDestination( service: Service, - options?: ServiceBindingTransformOptions & { - resource?: string; + options: ServiceBindingTransformOptions & { + targetURL: string; + resource: string; appTenantId?: string; extraParams?: Record; } ): Promise { const { access_token } = await getIasClientCredentialsToken(service, { - resource: options?.resource, - appTenantId: options?.appTenantId, - extraParams: options?.extraParams + resource: options.resource, + appTenantId: options.appTenantId, + extraParams: options.extraParams }); return buildClientCredentialsDestination( access_token, - service.credentials.url, + options.targetURL, service.name ); } diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index cba4eb8810..0500bb672a 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -48,8 +48,8 @@ interface IasParameters { */ export async function getIasClientCredentialsToken( service: string | Service, - options?: { - resource?: string; + options: { + resource: string; appTenantId?: string; extraParams?: Record; } @@ -81,13 +81,9 @@ export async function getIasClientCredentialsToken( client_id: clientid }); - if (arg.resource) { - const fullResource = `urn:sap:identity:application:provider:name:${arg.resource}`; - params.append('resource', fullResource); - logger.debug( - `Fetching IAS token with resource parameter: ${fullResource}` - ); - } + const fullResource = `urn:sap:identity:application:provider:name:${arg.resource}`; + params.append('resource', fullResource); + logger.debug(`Fetching IAS token with resource parameter: ${fullResource}`); if (arg.appTenantId) { params.append('app_tid', arg.appTenantId); From 1348182d293b1b1ea13d75b4d47df182d88753ef Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 5 Dec 2025 12:17:13 +0100 Subject: [PATCH 07/58] chore: fix clientsecret auth --- packages/connectivity/src/scp-cf/identity-service.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 0500bb672a..76e2751fe1 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -124,12 +124,8 @@ export async function getIasClientCredentialsToken( key }); } else if (clientsecret) { - // Client secret authentication logger.debug('Using client secret authentication for IAS token.'); - const credentials = Buffer.from(`${clientid}:${clientsecret}`).toString( - 'base64' - ); - headers['Authorization'] = `Basic ${credentials}`; + params.append('client_secret', clientsecret); } else { throw new Error( 'IAS credentials must contain either "certificate" and "key" for mTLS, or "clientsecret" for client secret authentication.' From 2f2cbf6c4f7171ee1858b79eae4886730af263df Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 8 Dec 2025 13:42:19 +0100 Subject: [PATCH 08/58] rename options & forward mtls creds to destination --- .../connectivity/src/http-agent/http-agent.ts | 11 ++++-- .../destination/destination-service-types.ts | 7 ++++ .../service-binding-to-destination.spec.ts | 29 ++++++++++++-- .../service-binding-to-destination.ts | 26 +++++++++---- .../src/scp-cf/identity-service.spec.ts | 19 +++++----- .../src/scp-cf/identity-service.ts | 38 +++++++++++++++---- 6 files changed, 98 insertions(+), 32 deletions(-) diff --git a/packages/connectivity/src/http-agent/http-agent.ts b/packages/connectivity/src/http-agent/http-agent.ts index 7e2a7e93c6..9c4ec2b6bd 100644 --- a/packages/connectivity/src/http-agent/http-agent.ts +++ b/packages/connectivity/src/http-agent/http-agent.ts @@ -182,6 +182,10 @@ async function getMtlsOptions( ); } if (mtlsIsEnabled(destination)) { + if (destination.mtlsKeyPair) { + return destination.mtlsKeyPair; + } + if (registerDestinationCache.mtls.useMtlsCache) { return registerDestinationCache.mtls.getMtlsOptions(); } @@ -198,9 +202,10 @@ async function getMtlsOptions( function mtlsIsEnabled(destination: Destination) { return ( - destination.mtls && - process.env.CF_INSTANCE_CERT && - process.env.CF_INSTANCE_KEY + (destination.mtls && + process.env.CF_INSTANCE_CERT && + process.env.CF_INSTANCE_KEY) || + destination.mtlsKeyPair ); } diff --git a/packages/connectivity/src/scp-cf/destination/destination-service-types.ts b/packages/connectivity/src/scp-cf/destination/destination-service-types.ts index 71ca05f594..6993d46df8 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-service-types.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-service-types.ts @@ -1,3 +1,4 @@ +import type { MtlsOptions } from '../../internal'; import type { CachingOptions } from '../cache'; import type { ProxyConfiguration } from '../connectivity-service-types'; import type { IsolationStrategy } from './destination-cache'; @@ -169,6 +170,12 @@ export interface Destination { * will be automatically used for TLS secured HTTP requests. */ mtls?: boolean; + + /** + * MTLS key pair consisting of certificate and private key in PEM format. + * This field is used to authenticate the destination using mTLS. + */ + mtlsKeyPair?: MtlsOptions; } /** diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index 0f23299c0d..8a65547f15 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -308,10 +308,10 @@ describe('service binding to destination', () => { ); }); - it('transforms identity (IAS) service binding with resource parameter', async () => { + it('transforms identity (IAS) service binding with appName parameter', async () => { const destination = await transformServiceBindingToDestination( resolveServiceBinding('identity'), - { resource: 'my-app' } as Parameters< + { appName: 'my-app' } as Parameters< typeof transformServiceBindingToDestination >[1] ); @@ -327,7 +327,30 @@ describe('service binding to destination', () => { label: 'identity' }), expect.objectContaining({ - resource: 'my-app' + appName: 'my-app' + }) + ); + }); + + it('transforms identity (IAS) service binding and includes mTLS cert/key in destination', async () => { + const destination = await transformServiceBindingToDestination( + resolveServiceBinding('identity') + ); + expect(destination).toEqual( + expect.objectContaining({ + url: 'https://tenant.accounts.ondemand.com', + name: 'my-identity-service', + authentication: 'OAuth2ClientCredentials', + mtlsKeyPair: { + cert: '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----', + key: '-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----' + }, + authTokens: expect.arrayContaining([ + expect.objectContaining({ + value: 'ias-access-token', + type: 'bearer' + }) + ]) }) ); }); diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index ae80bfb53c..2731e45868 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -184,23 +184,33 @@ async function xfS4hanaCloudBindingToDestination( async function iasBindingToDestination( service: Service, - options: ServiceBindingTransformOptions & { - targetURL: string; - resource: string; + options?: ServiceBindingTransformOptions & { + targetUrl?: string; + appName?: string; appTenantId?: string; extraParams?: Record; } ): Promise { const { access_token } = await getIasClientCredentialsToken(service, { - resource: options.resource, - appTenantId: options.appTenantId, - extraParams: options.extraParams + appName: options?.appName, + appTenantId: options?.appTenantId, + extraParams: options?.extraParams }); - return buildClientCredentialsDestination( + const destination = buildClientCredentialsDestination( access_token, - options.targetURL, + options?.targetUrl ?? service.credentials.url, service.name ); + + // Add mTLS key pair if available + if (service.credentials.certificate && service.credentials.key) { + destination.mtlsKeyPair = { + cert: service.credentials.certificate, + key: service.credentials.key + }; + } + + return destination; } function buildClientCredentialsDestination( diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 4980b6a8ce..46244e065f 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -85,7 +85,7 @@ describe('getIasClientCredentialsToken', () => { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - data: 'grant_type=client_credentials&client_id=test-client-id', + data: 'grant_type=client_credentials&client_id=test-client-id&refresh_token=0', httpsAgent: expect.any(Object) }) ); @@ -111,10 +111,9 @@ describe('getIasClientCredentialsToken', () => { method: 'post', url: 'https://tenant.accounts.ondemand.com/oauth2/token', headers: expect.objectContaining({ - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: expect.stringContaining('Basic ') + 'Content-Type': 'application/x-www-form-urlencoded' }), - data: 'grant_type=client_credentials&client_id=test-client-id' + data: 'grant_type=client_credentials&client_id=test-client-id&refresh_token=0&client_secret=test-client-secret' }) ); }); @@ -123,12 +122,12 @@ describe('getIasClientCredentialsToken', () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); await getIasClientCredentialsToken(mockIasService, { - resource: 'my-app' + appName: 'my-app' }); expect(mockedAxios.request).toHaveBeenCalledWith( expect.objectContaining({ - data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app' + data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app&refresh_token=0' }) ); }); @@ -142,22 +141,22 @@ describe('getIasClientCredentialsToken', () => { expect(mockedAxios.request).toHaveBeenCalledWith( expect.objectContaining({ - data: 'grant_type=client_credentials&client_id=test-client-id&app_tid=tenant-123' + data: 'grant_type=client_credentials&client_id=test-client-id&app_tid=tenant-123&refresh_token=0' }) ); }); - it('includes both resource and appTenantId parameters', async () => { + it('includes both appName and appTenantId parameters', async () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); await getIasClientCredentialsToken(mockIasService, { - resource: 'my-app', + appName: 'my-app', appTenantId: 'tenant-123' }); expect(mockedAxios.request).toHaveBeenCalledWith( expect.objectContaining({ - data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app&app_tid=tenant-123' + data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app&app_tid=tenant-123&refresh_token=0' }) ); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 76e2751fe1..c22fb6379d 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -16,6 +16,21 @@ const logger = createLogger({ messageContext: 'identity-service' }); +// export type OnBehalfOf = +// /** +// * A technical user for the provider account. +// */ +// | 'TECHNICAL_USER_PROVIDER' +// // TECHNICAL_USER_SUBSCRIBER, +// /** +// * A technical user based on tenant set in the current context. +// */ +// | 'TECHNICAL_USER_CURRENT_TENANT' +// /** +// * A named user based on the auth token set in the current context. +// */ +// | 'NAMED_USER_CURRENT_TENANT' + /** * @internal * Checks whether the IAS token to XSUAA token exchange should be applied. @@ -33,7 +48,7 @@ export function shouldExchangeToken(options: DestinationOptions): boolean { interface IasParameters { serviceCredentials: ServiceCredentials; - resource?: string; + appName?: string; appTenantId?: string; extraParams?: Record; } @@ -49,7 +64,7 @@ interface IasParameters { export async function getIasClientCredentialsToken( service: string | Service, options: { - resource: string; + appName?: string; appTenantId?: string; extraParams?: Record; } @@ -58,7 +73,7 @@ export async function getIasClientCredentialsToken( const fnArgument: IasParameters = { serviceCredentials: resolvedService.credentials, - resource: options?.resource, + appName: options?.appName, appTenantId: options?.appTenantId, extraParams: options?.extraParams }; @@ -81,9 +96,13 @@ export async function getIasClientCredentialsToken( client_id: clientid }); - const fullResource = `urn:sap:identity:application:provider:name:${arg.resource}`; - params.append('resource', fullResource); - logger.debug(`Fetching IAS token with resource parameter: ${fullResource}`); + if (arg.appName) { + const fullResource = `urn:sap:identity:application:provider:name:${arg.appName}`; + params.append('resource', fullResource); + logger.debug( + `Fetching IAS token with resource parameter: ${fullResource}` + ); + } if (arg.appTenantId) { params.append('app_tid', arg.appTenantId); @@ -111,8 +130,7 @@ export async function getIasClientCredentialsToken( const requestConfig: RawAxiosRequestConfig = { method: 'post', url: tokenUrl, - headers, - data: params.toString() + headers }; // Determine authentication method @@ -132,6 +150,10 @@ export async function getIasClientCredentialsToken( ); } + requestConfig.data = params.toString(); + + logger.info(params.toString()); + const response = await axios.request(requestConfig); return response.data; From f9ce3a53cf0d10a0e3c8bc6ba354ffe34182697f Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 8 Dec 2025 13:46:15 +0100 Subject: [PATCH 09/58] chore: add changeset --- .changeset/slow-cars-lie.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/slow-cars-lie.md diff --git a/.changeset/slow-cars-lie.md b/.changeset/slow-cars-lie.md new file mode 100644 index 0000000000..f909e19c6e --- /dev/null +++ b/.changeset/slow-cars-lie.md @@ -0,0 +1,5 @@ +--- +'@sap-cloud-sdk/connectivity': minor +--- + +[feat] Support IAS (App-to-App) authentication From 9990c6feec44953646df10920cfa1ee1f7ada8c5 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 8 Dec 2025 14:00:59 +0100 Subject: [PATCH 10/58] chore: update changeset msg --- .changeset/slow-cars-lie.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/slow-cars-lie.md b/.changeset/slow-cars-lie.md index f909e19c6e..a15e6cf801 100644 --- a/.changeset/slow-cars-lie.md +++ b/.changeset/slow-cars-lie.md @@ -2,4 +2,4 @@ '@sap-cloud-sdk/connectivity': minor --- -[feat] Support IAS (App-to-App) authentication +[New Functionality] Support IAS (App-to-App) authentication From a146af24f9b14241f8567815c8897b848dd571db Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 9 Dec 2025 11:52:26 +0100 Subject: [PATCH 11/58] chore: print warning if both `mtls` and `mtlsKeyPair` provided --- .../src/http-agent/http-agent.spec.ts | 29 +++++++++++++++++++ .../connectivity/src/http-agent/http-agent.ts | 22 +++++++++----- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/packages/connectivity/src/http-agent/http-agent.spec.ts b/packages/connectivity/src/http-agent/http-agent.spec.ts index fb3c08b1e1..9907169b88 100644 --- a/packages/connectivity/src/http-agent/http-agent.spec.ts +++ b/packages/connectivity/src/http-agent/http-agent.spec.ts @@ -316,6 +316,35 @@ describe('getAgentConfig', () => { expect(actual.passphrase).not.toBeDefined(); expect(cacheSpy).toHaveBeenCalledTimes(1); }); + + it('logs a warning when both mtls is enabled and mtlsKeyPair is provided', async () => { + process.env.CF_INSTANCE_CERT = 'cf-crypto/cf-cert'; + process.env.CF_INSTANCE_KEY = 'cf-crypto/cf-key'; + + const destination: HttpDestination = { + url: 'https://example.com', + name: 'test-destination', + mtls: true, + mtlsKeyPair: { + cert: 'ias-cert', + key: 'ias-key' + } + }; + + const logger = createLogger('http-agent'); + const warnSpy = jest.spyOn(logger, 'warn'); + + const actual = (await getAgentConfig(destination))['httpsAgent'] + .options; + + expect(warnSpy).toHaveBeenCalledWith( + "Destination test-destination has both 'mtlsKeyPair' (used by IAS) and 'mtls' (to use certs from cf) enabled. The 'mtlsKeyPair' will be used." + ); + expect(actual.cert).toEqual('ias-cert'); + expect(actual.key).toEqual('ias-key'); + + warnSpy.mockRestore(); + }); }); it('returns an object with key "httpsAgent" which is missing mTLS options when mtls is set to true but env variables do not include cert & key', async () => { diff --git a/packages/connectivity/src/http-agent/http-agent.ts b/packages/connectivity/src/http-agent/http-agent.ts index 9c4ec2b6bd..2ae9b4c518 100644 --- a/packages/connectivity/src/http-agent/http-agent.ts +++ b/packages/connectivity/src/http-agent/http-agent.ts @@ -116,7 +116,7 @@ function getKeyStoreOptions(destination: Destination): if ( // Only add certificates, when using ClientCertificateAuthentication (https://github.com/SAP/cloud-sdk-js/issues/3544) destination.authentication === 'ClientCertificateAuthentication' && - !mtlsIsEnabled(destination) && + !(mtlsIsEnabled(destination) || destination.mtlsKeyPair) && destination.keyStoreName ) { const certificate = selectCertificate(destination); @@ -181,11 +181,18 @@ async function getMtlsOptions( } has mTLS enabled, but the required Cloud Foundry environment variables (CF_INSTANCE_CERT and CF_INSTANCE_KEY) are not defined. Note that 'inferMtls' only works on Cloud Foundry.` ); } - if (mtlsIsEnabled(destination)) { - if (destination.mtlsKeyPair) { - return destination.mtlsKeyPair; + if (destination.mtlsKeyPair) { + if (mtlsIsEnabled(destination)) { + logger.warn( + `Destination ${ + destination.name ? destination.name : '' + } has both 'mtlsKeyPair' (used by IAS) and 'mtls' (to use certs from cf) enabled. The 'mtlsKeyPair' will be used.` + ); } + return destination.mtlsKeyPair; + } + if (mtlsIsEnabled(destination)) { if (registerDestinationCache.mtls.useMtlsCache) { return registerDestinationCache.mtls.getMtlsOptions(); } @@ -202,10 +209,9 @@ async function getMtlsOptions( function mtlsIsEnabled(destination: Destination) { return ( - (destination.mtls && - process.env.CF_INSTANCE_CERT && - process.env.CF_INSTANCE_KEY) || - destination.mtlsKeyPair + destination.mtls && + process.env.CF_INSTANCE_CERT && + process.env.CF_INSTANCE_KEY ); } From e5e67eb761aec2201dd312c7cbaf745c14810f28 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Wed, 10 Dec 2025 12:23:48 +0100 Subject: [PATCH 12/58] move ias-specific opts to iasOptions & refactor parameter handling --- .../destination/destination-from-vcap.ts | 23 ++++++++++++++++++ .../service-binding-to-destination.spec.ts | 18 +++++++++++--- .../service-binding-to-destination.ts | 18 +++++--------- .../src/scp-cf/identity-service.spec.ts | 10 ++++---- .../src/scp-cf/identity-service.ts | 24 ++++++++++--------- 5 files changed, 62 insertions(+), 31 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index 6549608b59..8109011d51 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -91,6 +91,29 @@ export type ServiceBindingTransformOptions = { * The JWT payload used to fetch destinations. */ jwt?: JwtPayload; + /** + * The options for IAS token retrieval. + */ + iasOptions?: { + /** + * The target URL of the destination that the IAS token is requested for. + */ + targetUrl?: string; + /** + * The application name registered in IAS for App-to-App communication. + * The token will only be usable to call the requested application. + */ + appName?: string; + /** + * The BTP tenant ID the token should be requested for. + * Required for cross-tenant communication. + */ + appTenantId?: string; + /** + * Additional parameters to be sent along with the token request. + */ + extraParams?: Record; + }; } & CachingOptions; /** diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index 8a65547f15..facb49e082 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -311,9 +311,7 @@ describe('service binding to destination', () => { it('transforms identity (IAS) service binding with appName parameter', async () => { const destination = await transformServiceBindingToDestination( resolveServiceBinding('identity'), - { appName: 'my-app' } as Parameters< - typeof transformServiceBindingToDestination - >[1] + { iasOptions: { appName: 'my-app' } } ); expect(destination).toEqual( expect.objectContaining({ @@ -332,6 +330,20 @@ describe('service binding to destination', () => { ); }); + it('transforms identity (IAS) service binding with custom targetUrl', async () => { + const destination = await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { iasOptions: { targetUrl: 'https://custom-target.example.com' } } + ); + expect(destination).toEqual( + expect.objectContaining({ + url: 'https://custom-target.example.com', + name: 'my-identity-service', + authentication: 'OAuth2ClientCredentials' + }) + ); + }); + it('transforms identity (IAS) service binding and includes mTLS cert/key in destination', async () => { const destination = await transformServiceBindingToDestination( resolveServiceBinding('identity') diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index 2731e45868..744566bffb 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -184,21 +184,15 @@ async function xfS4hanaCloudBindingToDestination( async function iasBindingToDestination( service: Service, - options?: ServiceBindingTransformOptions & { - targetUrl?: string; - appName?: string; - appTenantId?: string; - extraParams?: Record; - } + options?: ServiceBindingTransformOptions ): Promise { - const { access_token } = await getIasClientCredentialsToken(service, { - appName: options?.appName, - appTenantId: options?.appTenantId, - extraParams: options?.extraParams - }); + const { access_token } = await getIasClientCredentialsToken( + service, + options?.iasOptions ?? {} + ); const destination = buildClientCredentialsDestination( access_token, - options?.targetUrl ?? service.credentials.url, + options?.iasOptions?.targetUrl ?? service.credentials.url, service.name ); diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 46244e065f..9b454abb56 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -85,7 +85,7 @@ describe('getIasClientCredentialsToken', () => { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - data: 'grant_type=client_credentials&client_id=test-client-id&refresh_token=0', + data: 'grant_type=client_credentials&client_id=test-client-id&refresh_token=0&token_format=jwt', httpsAgent: expect.any(Object) }) ); @@ -113,7 +113,7 @@ describe('getIasClientCredentialsToken', () => { headers: expect.objectContaining({ 'Content-Type': 'application/x-www-form-urlencoded' }), - data: 'grant_type=client_credentials&client_id=test-client-id&refresh_token=0&client_secret=test-client-secret' + data: 'grant_type=client_credentials&client_id=test-client-id&refresh_token=0&token_format=jwt&client_secret=test-client-secret' }) ); }); @@ -127,7 +127,7 @@ describe('getIasClientCredentialsToken', () => { expect(mockedAxios.request).toHaveBeenCalledWith( expect.objectContaining({ - data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app&refresh_token=0' + data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app&refresh_token=0&token_format=jwt' }) ); }); @@ -141,7 +141,7 @@ describe('getIasClientCredentialsToken', () => { expect(mockedAxios.request).toHaveBeenCalledWith( expect.objectContaining({ - data: 'grant_type=client_credentials&client_id=test-client-id&app_tid=tenant-123&refresh_token=0' + data: 'grant_type=client_credentials&client_id=test-client-id&app_tid=tenant-123&refresh_token=0&token_format=jwt' }) ); }); @@ -156,7 +156,7 @@ describe('getIasClientCredentialsToken', () => { expect(mockedAxios.request).toHaveBeenCalledWith( expect.objectContaining({ - data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app&app_tid=tenant-123&refresh_token=0' + data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app&app_tid=tenant-123&refresh_token=0&token_format=jwt' }) ); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index c22fb6379d..ef06529c44 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -5,7 +5,10 @@ import { createLogger } from '@sap-cloud-sdk/util'; import axios from 'axios'; import { decodeJwt, isXsuaaToken } from './jwt'; import { resolveServiceBinding } from './environment-accessor'; -import type { DestinationOptions } from './destination'; +import type { + DestinationOptions, + ServiceBindingTransformOptions +} from './destination'; import type { MiddlewareContext } from '@sap-cloud-sdk/resilience'; import type { Service, ServiceCredentials } from './environment-accessor'; import type { RawAxiosRequestConfig } from 'axios'; @@ -63,11 +66,7 @@ interface IasParameters { */ export async function getIasClientCredentialsToken( service: string | Service, - options: { - appName?: string; - appTenantId?: string; - extraParams?: Record; - } + options: ServiceBindingTransformOptions['iasOptions'] = {} ): Promise { const resolvedService = resolveServiceBinding(service); @@ -116,11 +115,8 @@ export async function getIasClientCredentialsToken( params.append('refresh_token', '0'); // }; - for (const [paramKey, paramValue] of Object.entries( - arg.extraParams || {} - )) { - params.append(paramKey, paramValue); - } + // Ensure JWT token format + params.append('token_format', 'jwt'); const tokenUrl = `${url}/oauth2/token`; const headers: Record = { @@ -150,6 +146,12 @@ export async function getIasClientCredentialsToken( ); } + for (const [paramKey, paramValue] of Object.entries( + arg.extraParams || {} + )) { + params.append(paramKey, paramValue); + } + requestConfig.data = params.toString(); logger.info(params.toString()); From cd8e736f7928a15603a49204fec0d9a08f9a2fc5 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Wed, 10 Dec 2025 15:21:34 +0100 Subject: [PATCH 13/58] increase resource name flexibility --- .../destination/destination-from-vcap.ts | 20 ++++++++++++---- .../service-binding-to-destination.spec.ts | 4 ++-- .../src/scp-cf/identity-service.spec.ts | 4 ++-- .../src/scp-cf/identity-service.ts | 23 +++++++++++-------- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index 8109011d51..a7ddba4c4a 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -8,6 +8,7 @@ import { import { isHttpDestination } from './destination-service-types'; import { serviceToDestinationTransformers } from './service-binding-to-destination'; import { setForwardedAuthTokenIfNeeded } from './forward-auth-token'; +import type { Xor } from '@sap-cloud-sdk/util'; import type { DestinationFetchOptions } from './destination-accessor-types'; import type { Destination } from './destination-service-types'; import type { CachingOptions } from '../cache'; @@ -97,16 +98,27 @@ export type ServiceBindingTransformOptions = { iasOptions?: { /** * The target URL of the destination that the IAS token is requested for. + * @defaults to the (identity service) URL from the service binding. */ targetUrl?: string; /** - * The application name registered in IAS for App-to-App communication. + * The application resource for which the token is requested. * The token will only be usable to call the requested application. + * Either provide the app name (common case) or the provider client ID + * and tenant ID (optional). */ - appName?: string; + resource?: Xor< + { + name: string; + }, + { + clientId: string; + tenantId?: string; + } + >; /** - * The BTP tenant ID the token should be requested for. - * Required for cross-tenant communication. + * The source (BTP)tenant ID of the application. + * May be required for cross-tenant communication. */ appTenantId?: string; /** diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index facb49e082..1a68fe2bf3 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -311,7 +311,7 @@ describe('service binding to destination', () => { it('transforms identity (IAS) service binding with appName parameter', async () => { const destination = await transformServiceBindingToDestination( resolveServiceBinding('identity'), - { iasOptions: { appName: 'my-app' } } + { iasOptions: { resource: { name: 'my-app' } } } ); expect(destination).toEqual( expect.objectContaining({ @@ -325,7 +325,7 @@ describe('service binding to destination', () => { label: 'identity' }), expect.objectContaining({ - appName: 'my-app' + resource: { name: 'my-app' } }) ); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 9b454abb56..bc64762708 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -122,7 +122,7 @@ describe('getIasClientCredentialsToken', () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); await getIasClientCredentialsToken(mockIasService, { - appName: 'my-app' + resource: { name: 'my-app' } }); expect(mockedAxios.request).toHaveBeenCalledWith( @@ -150,7 +150,7 @@ describe('getIasClientCredentialsToken', () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); await getIasClientCredentialsToken(mockIasService, { - appName: 'my-app', + resource: { name: 'my-app' }, appTenantId: 'tenant-123' }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index ef06529c44..717f0274d9 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -49,12 +49,9 @@ export function shouldExchangeToken(options: DestinationOptions): boolean { ); } -interface IasParameters { +type IasParameters = { serviceCredentials: ServiceCredentials; - appName?: string; - appTenantId?: string; - extraParams?: Record; -} +} & ServiceBindingTransformOptions['iasOptions']; /** * Make a client credentials request against the IAS OAuth2 endpoint. @@ -72,9 +69,7 @@ export async function getIasClientCredentialsToken( const fnArgument: IasParameters = { serviceCredentials: resolvedService.credentials, - appName: options?.appName, - appTenantId: options?.appTenantId, - extraParams: options?.extraParams + ...options }; const iasPromise = async function ( @@ -95,8 +90,16 @@ export async function getIasClientCredentialsToken( client_id: clientid }); - if (arg.appName) { - const fullResource = `urn:sap:identity:application:provider:name:${arg.appName}`; + if (arg.resource) { + let fullResource = ''; + if ('name' in arg.resource) { + fullResource = `urn:sap:identity:application:provider:name:${arg.resource.name}`; + } else if (arg.resource.clientId && arg.resource.tenantId) { + fullResource = `urn:sap:identity:application:provider:clientid:${arg.resource.clientId}:tenantid:${arg.resource.tenantId}`; + } else if (arg.resource.clientId) { + fullResource = `urn:sap:identity:application:provider:clientid:${arg.resource.clientId}`; + } + params.append('resource', fullResource); logger.debug( `Fetching IAS token with resource parameter: ${fullResource}` From 34b83be03d9d5b0dc8bdd8bc9627445cc749d6c7 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Wed, 10 Dec 2025 16:35:10 +0100 Subject: [PATCH 14/58] chore: try to fix lint error --- .../src/scp-cf/destination/destination-from-vcap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index a7ddba4c4a..30997f5e09 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -98,7 +98,7 @@ export type ServiceBindingTransformOptions = { iasOptions?: { /** * The target URL of the destination that the IAS token is requested for. - * @defaults to the (identity service) URL from the service binding. + * @default to the (identity service) URL from the service binding. */ targetUrl?: string; /** From fdb3d3543fd1a4a0a8471bb23b28be0856633551 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 12 Dec 2025 15:07:03 +0100 Subject: [PATCH 15/58] support user-token exchange --- packages/connectivity/src/index.ts | 4 +- .../destination/destination-accessor-types.ts | 1 + .../destination/destination-from-vcap.ts | 113 ++++++++++---- .../src/scp-cf/identity-service.spec.ts | 138 ++++++++++++++++-- .../src/scp-cf/identity-service.ts | 75 ++++++---- 5 files changed, 253 insertions(+), 78 deletions(-) diff --git a/packages/connectivity/src/index.ts b/packages/connectivity/src/index.ts index 013286cb89..f28fc185d9 100644 --- a/packages/connectivity/src/index.ts +++ b/packages/connectivity/src/index.ts @@ -58,7 +58,9 @@ export type { DestinationJson, DestinationsByType, DestinationFromServiceBindingOptions, - ServiceBindingTransformOptions + ServiceBindingTransformOptions, + ActAs, + IasOptions } from './scp-cf'; export type { diff --git a/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts b/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts index a24b8df8c5..4c4479dc81 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts @@ -51,6 +51,7 @@ export interface DestinationAccessorOptions { * ATTENTION: The property is mandatory in the following cases: * - User-dependent authentication flow is used, e.g., `OAuth2UserTokenExchange`, `OAuth2JWTBearer`, `OAuth2SAMLBearerAssertion`, `SAMLAssertion` or `PrincipalPropagation`. * - Multi-tenant scenarios with destinations maintained in the subscriber account. This case is implied if the `selectionStrategy` is set to `alwaysSubscriber`. + * - IAS token exchange with `iasOptions.actAs` set to `'business-user'`. In this case, the JWT is automatically used as the assertion for IAS token exchange. */ jwt?: string; diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index 30997f5e09..4bcb31f9eb 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -40,7 +40,17 @@ export async function getDestinationFromServiceBinding( ? decodeJwt(options.jwt) : undefined; - const retrievalOptions = { ...options, jwt: decodedJwt }; + // If using business user authentication with IAS and no assertion provided, use the JWT from options + let iasOptions = options.iasOptions; + if ( + iasOptions?.actAs === 'business-user' && + options.jwt && + !iasOptions.assertion + ) { + iasOptions = { ...iasOptions, assertion: options.jwt }; + } + + const retrievalOptions = { ...options, jwt: decodedJwt, iasOptions }; const destination = await retrieveDestination(retrievalOptions); const destWithProxy = @@ -82,8 +92,77 @@ export interface DestinationFromServiceBindingOptions { * Custom transformation function to control how a {@link Destination} is built from the given {@link Service}. */ serviceBindingTransformFn?: ServiceBindingTransformFunction; + /** + * Options for IAS token retrieval. + */ + iasOptions?: IasOptions; +} + +/** + * Base options shared by all IAS authentication modes. + */ +interface IasOptionsBase { + /** + * The target URL of the destination that the IAS token is requested for. + * @default to the (identity service) URL from the service binding. + */ + targetUrl?: string; + /** + * The application resource for which the token is requested. + * The token will only be usable to call the requested application. + * Either provide the app name (common case) or the provider client ID + * and tenant ID (optional). + */ + resource?: Xor< + { + name: string; + }, + { + clientId: string; + tenantId?: string; + } + >; + /** + * The consumer (BTP) tenant ID of the application. + * May be required for multi-tenant communication. + */ + appTenantId?: string; + /** + * Additional parameters to be sent along with the token request. + */ + extraParams?: Record; } +/** + * IAS options for technical user authentication. + */ +type IasOptionsTechnical = IasOptionsBase & { + actAs?: 'technical-user'; + /** + * Assertion not used for technical user authentication. + */ + assertion?: never; +}; + +/** + * IAS options for business user authentication. + */ +type IasOptionsBusinessUser = IasOptionsBase & { + /** + * Specifies business user authentication. + */ + actAs: 'business-user'; + /** + * The JWT assertion string to use for business user authentication (required). + */ + assertion: string; +}; + +/** + * Options for IAS token retrieval with type-safe actAs/jwt relationship. + */ +export type IasOptions = IasOptionsTechnical | IasOptionsBusinessUser; + /** * Represents options passed to the service binding transform function. */ @@ -95,37 +174,7 @@ export type ServiceBindingTransformOptions = { /** * The options for IAS token retrieval. */ - iasOptions?: { - /** - * The target URL of the destination that the IAS token is requested for. - * @default to the (identity service) URL from the service binding. - */ - targetUrl?: string; - /** - * The application resource for which the token is requested. - * The token will only be usable to call the requested application. - * Either provide the app name (common case) or the provider client ID - * and tenant ID (optional). - */ - resource?: Xor< - { - name: string; - }, - { - clientId: string; - tenantId?: string; - } - >; - /** - * The source (BTP)tenant ID of the application. - * May be required for cross-tenant communication. - */ - appTenantId?: string; - /** - * Additional parameters to be sent along with the token request. - */ - extraParams?: Record; - }; + iasOptions?: IasOptions; } & CachingOptions; /** diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index bc64762708..95164ed9af 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -85,10 +85,15 @@ describe('getIasClientCredentialsToken', () => { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - data: 'grant_type=client_credentials&client_id=test-client-id&refresh_token=0&token_format=jwt', httpsAgent: expect.any(Object) }) ); + // Verify the data contains required parameters (order may vary) + const callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).toContain('grant_type=client_credentials'); + expect(callData).toContain('client_id=test-client-id'); + expect(callData).toContain('token_format=jwt'); + expect(callData).not.toContain('refresh_token=0'); // Should not appear for technical users }); it('fetches IAS token with client secret authentication', async () => { @@ -112,10 +117,16 @@ describe('getIasClientCredentialsToken', () => { url: 'https://tenant.accounts.ondemand.com/oauth2/token', headers: expect.objectContaining({ 'Content-Type': 'application/x-www-form-urlencoded' - }), - data: 'grant_type=client_credentials&client_id=test-client-id&refresh_token=0&token_format=jwt&client_secret=test-client-secret' + }) }) ); + // Verify the data contains required parameters (order may vary) + const callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).toContain('grant_type=client_credentials'); + expect(callData).toContain('client_id=test-client-id'); + expect(callData).toContain('token_format=jwt'); + expect(callData).toContain('client_secret=test-client-secret'); + expect(callData).not.toContain('refresh_token=0'); // Should not appear for technical users }); it('includes resource parameter for app2app flow', async () => { @@ -125,11 +136,12 @@ describe('getIasClientCredentialsToken', () => { resource: { name: 'my-app' } }); - expect(mockedAxios.request).toHaveBeenCalledWith( - expect.objectContaining({ - data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app&refresh_token=0&token_format=jwt' - }) + const callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).toContain('grant_type=client_credentials'); + expect(callData).toContain( + 'resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app' ); + expect(callData).not.toContain('refresh_token=0'); }); it('includes appTenantId parameter for multi-tenant scenarios', async () => { @@ -139,11 +151,10 @@ describe('getIasClientCredentialsToken', () => { appTenantId: 'tenant-123' }); - expect(mockedAxios.request).toHaveBeenCalledWith( - expect.objectContaining({ - data: 'grant_type=client_credentials&client_id=test-client-id&app_tid=tenant-123&refresh_token=0&token_format=jwt' - }) - ); + const callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).toContain('grant_type=client_credentials'); + expect(callData).toContain('app_tid=tenant-123'); + expect(callData).not.toContain('refresh_token=0'); }); it('includes both appName and appTenantId parameters', async () => { @@ -154,11 +165,13 @@ describe('getIasClientCredentialsToken', () => { appTenantId: 'tenant-123' }); - expect(mockedAxios.request).toHaveBeenCalledWith( - expect.objectContaining({ - data: 'grant_type=client_credentials&client_id=test-client-id&resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app&app_tid=tenant-123&refresh_token=0&token_format=jwt' - }) + const callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).toContain('grant_type=client_credentials'); + expect(callData).toContain( + 'resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app' ); + expect(callData).toContain('app_tid=tenant-123'); + expect(callData).not.toContain('refresh_token=0'); }); it('includes extraParams for additional OAuth2 parameters', async () => { @@ -234,4 +247,97 @@ describe('getIasClientCredentialsToken', () => { 'Could not fetch IAS client credentials token for service of type identity' ); }); + + describe('actAs parameter', () => { + it('uses technical-user by default', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + await getIasClientCredentialsToken(mockIasService, {}); + + const callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).toContain('grant_type=client_credentials'); + expect(callData).not.toContain('assertion='); + expect(callData).not.toContain('refresh_token=0'); + }); + + it('uses client credentials for technical-user', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + await getIasClientCredentialsToken(mockIasService, { + actAs: 'technical-user' + }); + + const callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).toContain('grant_type=client_credentials'); + expect(callData).not.toContain('assertion='); + expect(callData).not.toContain('refresh_token=0'); + }); + + it('uses JWT bearer grant for business-user with assertion', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + await getIasClientCredentialsToken(mockIasService, { + actAs: 'business-user', + assertion: 'user-jwt-token' + }); + + const callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).toContain( + 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer' + ); + expect(callData).toContain('assertion=user-jwt-token'); + expect(callData).toContain('refresh_token=0'); // Workaround applied for business users + }); + + it('throws error for business-user without assertion', async () => { + await expect( + getIasClientCredentialsToken(mockIasService, { + actAs: 'business-user' + } as any) + ).rejects.toThrow('JWT assertion required for actAs: "business-user"'); + }); + + it('includes refresh_token workaround only for business-user', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + // Technical user - no refresh_token + await getIasClientCredentialsToken(mockIasService, { + actAs: 'technical-user' + }); + let callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).not.toContain('refresh_token=0'); + + jest.clearAllMocks(); + + // Business user - has refresh_token + await getIasClientCredentialsToken(mockIasService, { + actAs: 'business-user', + assertion: 'user-jwt' + }); + callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).toContain('refresh_token=0'); + }); + + it('supports business-user with resource and appTenantId', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + await getIasClientCredentialsToken(mockIasService, { + actAs: 'business-user', + assertion: 'user-jwt-token', + resource: { name: 'my-app' }, + appTenantId: 'tenant-123' + }); + + const callData = mockedAxios.request.mock.calls[0][0].data; + expect(callData).toContain( + 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer' + ); + expect(callData).toContain('assertion=user-jwt-token'); + expect(callData).toContain( + 'resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app' + ); + expect(callData).toContain('app_tid=tenant-123'); + expect(callData).toContain('refresh_token=0'); + }); + }); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 717f0274d9..660b988e35 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -19,20 +19,19 @@ const logger = createLogger({ messageContext: 'identity-service' }); -// export type OnBehalfOf = -// /** -// * A technical user for the provider account. -// */ -// | 'TECHNICAL_USER_PROVIDER' -// // TECHNICAL_USER_SUBSCRIBER, -// /** -// * A technical user based on tenant set in the current context. -// */ -// | 'TECHNICAL_USER_CURRENT_TENANT' -// /** -// * A named user based on the auth token set in the current context. -// */ -// | 'NAMED_USER_CURRENT_TENANT' +/** + * Specifies which user identity should be used for authentication. + * Determines whether to use technical client credentials or propagate a business user's identity. + */ +export type ActAs = + /** + * Technical user from the service binding (default). + */ + | 'technical-user' + /** + * Business user from the current request context (requires JWT). + */ + | 'business-user'; /** * @internal @@ -57,7 +56,7 @@ type IasParameters = { * Make a client credentials request against the IAS OAuth2 endpoint. * Supports both certificate-based (mTLS) and client secret authentication. * @param service - Service as it is defined in the environment variable. - * @param options - Options for token fetching, including optional resource parameter for app2app, appTenantId for multi-tenant scenarios, and extraParams for additional OAuth2 parameters. + * @param options - Options for token fetching, including actAs to specify authentication mode, optional resource parameter for app2app, appTenantId for multi-tenant scenarios, and extraParams for additional OAuth2 parameters. * @returns Client credentials token response. * @internal */ @@ -86,18 +85,40 @@ export async function getIasClientCredentialsToken( // Build form data const params = new URLSearchParams({ - grant_type: 'client_credentials', client_id: clientid }); + // Determine grant type based on actAs parameter + const actAs = arg.actAs || 'technical-user'; + + if (actAs === 'business-user') { + // JWT bearer grant for business user propagation + if (!arg.assertion) { + throw new Error( + 'JWT assertion required for actAs: "business-user". Provide iasOptions.assertion.' + ); + } + params.append('assertion', arg.assertion); + params.append( + 'grant_type', + 'urn:ietf:params:oauth:grant-type:jwt-bearer' + ); + // Workaround for an IAS issue + params.append('refresh_token', '0'); + } else { + // Client credentials for technical users + params.append('grant_type', 'client_credentials'); + } + if (arg.resource) { let fullResource = ''; if ('name' in arg.resource) { fullResource = `urn:sap:identity:application:provider:name:${arg.resource.name}`; - } else if (arg.resource.clientId && arg.resource.tenantId) { - fullResource = `urn:sap:identity:application:provider:clientid:${arg.resource.clientId}:tenantid:${arg.resource.tenantId}`; - } else if (arg.resource.clientId) { + } else { fullResource = `urn:sap:identity:application:provider:clientid:${arg.resource.clientId}`; + if (arg.resource.tenantId) { + fullResource += `:tenantid:${arg.resource.tenantId}`; + } } params.append('resource', fullResource); @@ -113,12 +134,8 @@ export async function getIasClientCredentialsToken( ); } - // Workaround for IAS issue - // if( onBehalfOf == OnBehalfOf.NAMED_USER_CURRENT_TENANT ) { - params.append('refresh_token', '0'); - // }; - - // Ensure JWT token format + // Ensure JWT token format, not mandatory but we expect JWTs + // and the docs mention in some cases we may get opaque tokens otherwise params.append('token_format', 'jwt'); const tokenUrl = `${url}/oauth2/token`; @@ -149,10 +166,10 @@ export async function getIasClientCredentialsToken( ); } - for (const [paramKey, paramValue] of Object.entries( - arg.extraParams || {} - )) { - params.append(paramKey, paramValue); + if (arg.extraParams) { + for (const [paramKey, paramValue] of Object.entries(arg.extraParams)) { + params.append(paramKey, paramValue); + } } requestConfig.data = params.toString(); From af1d0bd8fa7ac8bf81692d3afd3e6d96f258ebd9 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 12 Dec 2025 15:11:20 +0100 Subject: [PATCH 16/58] add experimental to changelog note --- .changeset/slow-cars-lie.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/slow-cars-lie.md b/.changeset/slow-cars-lie.md index a15e6cf801..2e6ea4225c 100644 --- a/.changeset/slow-cars-lie.md +++ b/.changeset/slow-cars-lie.md @@ -2,4 +2,4 @@ '@sap-cloud-sdk/connectivity': minor --- -[New Functionality] Support IAS (App-to-App) authentication +[New Functionality] Support IAS (App-to-App) authentication (experimental) From 62d4023e59ae692a133d6393d1b7f025a43af1b2 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 12 Dec 2025 17:06:28 +0100 Subject: [PATCH 17/58] add initial cache implementation --- .../src/scp-cf/ias-token-cache.spec.ts | 265 ++++++++++++++++++ .../src/scp-cf/ias-token-cache.ts | 134 +++++++++ .../src/scp-cf/identity-service.spec.ts | 129 ++++++++- .../src/scp-cf/identity-service.ts | 223 ++++++++------- 4 files changed, 640 insertions(+), 111 deletions(-) create mode 100644 packages/connectivity/src/scp-cf/ias-token-cache.spec.ts create mode 100644 packages/connectivity/src/scp-cf/ias-token-cache.ts diff --git a/packages/connectivity/src/scp-cf/ias-token-cache.spec.ts b/packages/connectivity/src/scp-cf/ias-token-cache.spec.ts new file mode 100644 index 0000000000..496d869758 --- /dev/null +++ b/packages/connectivity/src/scp-cf/ias-token-cache.spec.ts @@ -0,0 +1,265 @@ +import { createLogger } from '@sap-cloud-sdk/util'; +import { signedJwt } from '../../../../test-resources/test/test-util'; +import { iasTokenCache, getCacheKey } from './ias-token-cache'; +import type { ClientCredentialsResponse } from './xsuaa-service-types'; + +const logger = createLogger({ + package: 'connectivity', + messageContext: 'ias-token-cache' +}); + +describe('ias-token-cache', () => { + const mockToken: ClientCredentialsResponse = { + access_token: 'mock-access-token', + token_type: 'Bearer', + expires_in: 3600, + scope: 'openid', + jti: 'mock-jti' + }; + + afterEach(() => { + iasTokenCache.clear(); + jest.restoreAllMocks(); + }); + + describe('getToken', () => { + it('returns cached token if valid', () => { + iasTokenCache.cacheToken('test-client-id', {}, mockToken); + + const cachedToken = iasTokenCache.getToken('test-client-id', {}); + expect(cachedToken).toEqual(mockToken); + }); + + it('returns undefined for expired token', () => { + const expiredToken = { + ...mockToken, + expires_in: -1 + }; + iasTokenCache.cacheToken('test-client-id', {}, expiredToken); + + // Wait for token to expire + jest.useFakeTimers(); + jest.advanceTimersByTime(2000); + + const cachedToken = iasTokenCache.getToken('test-client-id', {}); + expect(cachedToken).toBeUndefined(); + + jest.useRealTimers(); + }); + + it('returns undefined if cache key cannot be created', () => { + jest.spyOn(logger, 'warn'); + + const cachedToken = iasTokenCache.getToken('', {}); + expect(cachedToken).toBeUndefined(); + expect(logger.warn).toHaveBeenCalledWith( + 'Cannot create cache key for IAS token cache. The given client ID is undefined.' + ); + }); + }); + + describe('cacheToken', () => { + it('caches token with expiration', () => { + iasTokenCache.cacheToken('test-client-id', {}, mockToken); + + const cachedToken = iasTokenCache.getToken('test-client-id', {}); + expect(cachedToken).toEqual(mockToken); + }); + + it('does not cache if cache key cannot be created', () => { + jest.spyOn(logger, 'warn'); + + iasTokenCache.cacheToken('', {}, mockToken); + + expect(logger.warn).toHaveBeenCalledWith( + 'Cannot create cache key for IAS token cache. The given client ID is undefined.' + ); + }); + }); + + describe('getCacheKey', () => { + describe('technical-user flow', () => { + it('generates cache key with default tenant (provider-tenant)', () => { + const cacheKey = getCacheKey('test-client-id', {}); + expect(cacheKey).toBe('provider-tenant:test-client-id:'); + }); + + it('generates cache key with appTenantId', () => { + const cacheKey = getCacheKey('test-client-id', { + appTenantId: 'tenant-123' + }); + expect(cacheKey).toBe('tenant-123:test-client-id:'); + }); + + it('includes resource name in cache key', () => { + const cacheKey = getCacheKey('test-client-id', { + resource: { name: 'my-app' } + }); + expect(cacheKey).toBe('provider-tenant:test-client-id:name=my-app'); + }); + + it('includes resource clientId in cache key', () => { + const cacheKey = getCacheKey('test-client-id', { + resource: { clientId: 'resource-client-123' } + }); + expect(cacheKey).toBe( + 'provider-tenant:test-client-id:clientid=resource-client-123' + ); + }); + + it('includes resource clientId and tenantId in cache key', () => { + const cacheKey = getCacheKey('test-client-id', { + resource: { clientId: 'resource-client-123', tenantId: 'tenant-456' } + }); + expect(cacheKey).toBe( + 'provider-tenant:test-client-id:clientid=resource-client-123:tenantid=tenant-456' + ); + }); + + it('returns undefined if clientId is missing', () => { + jest.spyOn(logger, 'warn'); + + const cacheKey = getCacheKey('', {}); + expect(cacheKey).toBeUndefined(); + expect(logger.warn).toHaveBeenCalledWith( + 'Cannot create cache key for IAS token cache. The given client ID is undefined.' + ); + }); + }); + + describe('business-user flow', () => { + it('generates cache key with user and tenant from assertion', () => { + const assertion = signedJwt({ + user_uuid: 'user-123', + app_tid: 'tenant-456' + }); + + const cacheKey = getCacheKey('test-client-id', { + actAs: 'business-user', + assertion + }); + expect(cacheKey).toBe('user-123:tenant-456:test-client-id:'); + }); + + it('generates cache key with resource', () => { + const assertion = signedJwt({ + user_uuid: 'user-123', + app_tid: 'tenant-456' + }); + + const cacheKey = getCacheKey('test-client-id', { + actAs: 'business-user', + assertion, + resource: { name: 'my-app' } + }); + expect(cacheKey).toBe('user-123:tenant-456:test-client-id:name=my-app'); + }); + + it('returns undefined if assertion is missing', () => { + jest.spyOn(logger, 'warn'); + + const cacheKey = getCacheKey('test-client-id', { + actAs: 'business-user' + } as any); + expect(cacheKey).toBeUndefined(); + expect(logger.warn).toHaveBeenCalledWith( + 'Cannot create cache key for IAS token cache. Business-user flow requires assertion JWT.' + ); + }); + + it('returns undefined if user ID cannot be extracted', () => { + jest.spyOn(logger, 'warn'); + const assertion = signedJwt({ + app_tid: 'tenant-456' + // Missing user_uuid + }); + + const cacheKey = getCacheKey('test-client-id', { + actAs: 'business-user', + assertion + }); + expect(cacheKey).toBeUndefined(); + expect(logger.warn).toHaveBeenCalledWith( + 'Cannot create cache key for IAS token cache. User ID could not be extracted from assertion JWT.' + ); + }); + + it('returns undefined if tenant ID cannot be extracted', () => { + jest.spyOn(logger, 'warn'); + const assertion = signedJwt({ + user_uuid: 'user-123' + // Missing app_tid or zid + }); + + const cacheKey = getCacheKey('test-client-id', { + actAs: 'business-user', + assertion + }); + expect(cacheKey).toBeUndefined(); + expect(logger.warn).toHaveBeenCalledWith( + 'Cannot create cache key for IAS token cache. Tenant ID could not be extracted from assertion JWT.' + ); + }); + }); + + describe('cache isolation', () => { + it('isolates cache by tenant for technical-user', () => { + const key1 = getCacheKey('test-client-id', { + appTenantId: 'tenant-1' + }); + const key2 = getCacheKey('test-client-id', { + appTenantId: 'tenant-2' + }); + + expect(key1).not.toBe(key2); + expect(key1).toBe('tenant-1:test-client-id:'); + expect(key2).toBe('tenant-2:test-client-id:'); + }); + + it('isolates cache by resource', () => { + const key1 = getCacheKey('test-client-id', { + resource: { name: 'app-1' } + }); + const key2 = getCacheKey('test-client-id', { + resource: { name: 'app-2' } + }); + + expect(key1).not.toBe(key2); + }); + + it('isolates cache by user for business-user', () => { + const assertion1 = signedJwt({ + user_uuid: 'user-1', + app_tid: 'tenant-123' + }); + const assertion2 = signedJwt({ + user_uuid: 'user-2', + app_tid: 'tenant-123' + }); + + const key1 = getCacheKey('test-client-id', { + actAs: 'business-user', + assertion: assertion1 + }); + const key2 = getCacheKey('test-client-id', { + actAs: 'business-user', + assertion: assertion2 + }); + + expect(key1).not.toBe(key2); + expect(key1).toBe('user-1:tenant-123:test-client-id:'); + expect(key2).toBe('user-2:tenant-123:test-client-id:'); + }); + }); + }); + + describe('clear', () => { + it('clears all cached tokens', () => { + iasTokenCache.cacheToken('test-client-id', {}, mockToken); + expect(iasTokenCache.getToken('test-client-id', {})).toEqual(mockToken); + + iasTokenCache.clear(); + expect(iasTokenCache.getToken('test-client-id', {})).toBeUndefined(); + }); + }); +}); diff --git a/packages/connectivity/src/scp-cf/ias-token-cache.ts b/packages/connectivity/src/scp-cf/ias-token-cache.ts new file mode 100644 index 0000000000..75411f2483 --- /dev/null +++ b/packages/connectivity/src/scp-cf/ias-token-cache.ts @@ -0,0 +1,134 @@ +import { createLogger } from '@sap-cloud-sdk/util'; +import { + decodeJwt, + getTenantId, + userId, + getTenantIdFromBinding, + getDefaultTenantId +} from './jwt'; +import { Cache } from './cache'; +import type { ClientCredentialsResponse } from './xsuaa-service-types'; +import type { IasOptions, ServiceBindingTransformOptions } from './destination'; + +const logger = createLogger({ + package: 'connectivity', + messageContext: 'ias-token-cache' +}); + +const IasTokenCache = (cache: Cache) => ({ + getToken: ( + clientId: string, + options: IasOptions = {} + ): ClientCredentialsResponse | undefined => { + const cacheKey = getCacheKey(clientId, options); + return cacheKey ? cache.get(cacheKey) : undefined; + }, + + cacheToken: ( + clientId: string, + options: IasOptions = {}, + token: ClientCredentialsResponse + ): void => { + const cacheKey = getCacheKey(clientId, options); + if (cacheKey) { + cache.set(cacheKey, { + entry: token, + expires: token.expires_in + ? Date.now() + token.expires_in * 1000 + : undefined + }); + } + }, + + clear: (): void => { + cache.clear(); + }, + + getCacheInstance: () => cache +}); + +/** + * Normalizes the resource parameter to a consistent string format for cache key. + * @param resource - The resource parameter from iasOptions. + * @returns Normalized resource string or empty string if not provided. + * @internal + */ +function normalizeResource(resource?: IasOptions['resource']): string { + if (!resource) { + return ''; + } + + if ('name' in resource) { + return `name=${resource.name}`; + } + + let normalized = `clientid=${resource.clientId}`; + if (resource.tenantId) { + normalized += `:tenantid=${resource.tenantId}`; + } + return normalized; +} + +/** + * Generates a cache key for IAS tokens based on actAs mode, user/tenant context, and resource. + * @param clientId - The client ID from service credentials. + * @param options - IAS options containing actAs, assertion, resource, and appTenantId. + * @returns Cache key string or undefined if required components are missing. + * @internal + */ +export function getCacheKey( + clientId: string, + options: ServiceBindingTransformOptions['iasOptions'] = {} +): string | undefined { + const actAs = options.actAs || 'technical-user'; + const resourceStr = normalizeResource(options.resource); + + if (!clientId) { + logger.warn( + 'Cannot create cache key for IAS token cache. The given client ID is undefined.' + ); + return undefined; + } + + if (actAs === 'business-user') { + if (!options.assertion) { + logger.warn( + 'Cannot create cache key for IAS token cache. Business-user flow requires assertion JWT.' + ); + return undefined; + } + + const decodedAssertion = decodeJwt(options.assertion); + const user = userId(decodedAssertion); + + if (!user) { + logger.warn( + 'Cannot create cache key for IAS token cache. User ID could not be extracted from assertion JWT.' + ); + return undefined; + } + + const assertionTenant = getTenantId(decodedAssertion); + + if (!assertionTenant) { + logger.warn( + 'Cannot create cache key for IAS token cache. Tenant ID could not be extracted from assertion JWT.' + ); + return undefined; + } + + return [user, assertionTenant, clientId, resourceStr].join(':'); + } + + const tenant = + options.appTenantId || getTenantIdFromBinding() || getDefaultTenantId(); + + return [tenant, clientId, resourceStr].join(':'); +} + +/** + * @internal + */ +export const iasTokenCache = IasTokenCache( + new Cache(5 * 60 * 1000 /* 5 minutes in ms */) +); diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 95164ed9af..8b7ea50d89 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -4,6 +4,7 @@ import { getIasClientCredentialsToken, shouldExchangeToken } from './identity-service'; +import { iasTokenCache } from './ias-token-cache'; import type { Service } from './environment-accessor'; jest.mock('axios'); @@ -72,6 +73,10 @@ describe('getIasClientCredentialsToken', () => { jest.clearAllMocks(); }); + afterEach(() => { + iasTokenCache.clear(); + }); + it('fetches IAS token with mTLS authentication', async () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); @@ -276,16 +281,23 @@ describe('getIasClientCredentialsToken', () => { it('uses JWT bearer grant for business-user with assertion', async () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + const userAssertion = signedJwt({ + user_uuid: 'test-user', + app_tid: 'test-tenant' + }); + await getIasClientCredentialsToken(mockIasService, { actAs: 'business-user', - assertion: 'user-jwt-token' + assertion: userAssertion }); const callData = mockedAxios.request.mock.calls[0][0].data; expect(callData).toContain( 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer' ); - expect(callData).toContain('assertion=user-jwt-token'); + expect(callData).toContain( + `assertion=${encodeURIComponent(userAssertion)}` + ); expect(callData).toContain('refresh_token=0'); // Workaround applied for business users }); @@ -308,11 +320,16 @@ describe('getIasClientCredentialsToken', () => { expect(callData).not.toContain('refresh_token=0'); jest.clearAllMocks(); + iasTokenCache.clear(); // Business user - has refresh_token + const userAssertion = signedJwt({ + user_uuid: 'test-user', + app_tid: 'test-tenant' + }); await getIasClientCredentialsToken(mockIasService, { actAs: 'business-user', - assertion: 'user-jwt' + assertion: userAssertion }); callData = mockedAxios.request.mock.calls[0][0].data; expect(callData).toContain('refresh_token=0'); @@ -321,9 +338,14 @@ describe('getIasClientCredentialsToken', () => { it('supports business-user with resource and appTenantId', async () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + const userAssertion = signedJwt({ + user_uuid: 'test-user', + app_tid: 'test-tenant' + }); + await getIasClientCredentialsToken(mockIasService, { actAs: 'business-user', - assertion: 'user-jwt-token', + assertion: userAssertion, resource: { name: 'my-app' }, appTenantId: 'tenant-123' }); @@ -332,7 +354,9 @@ describe('getIasClientCredentialsToken', () => { expect(callData).toContain( 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer' ); - expect(callData).toContain('assertion=user-jwt-token'); + expect(callData).toContain( + `assertion=${encodeURIComponent(userAssertion)}` + ); expect(callData).toContain( 'resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app' ); @@ -340,4 +364,99 @@ describe('getIasClientCredentialsToken', () => { expect(callData).toContain('refresh_token=0'); }); }); + + describe('token caching', () => { + it('caches technical-user tokens and returns from cache on second call', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + const first = await getIasClientCredentialsToken(mockIasService, {}); + const second = await getIasClientCredentialsToken(mockIasService, {}); + + expect(first).toEqual(mockTokenResponse); + expect(second).toEqual(mockTokenResponse); + expect(mockedAxios.request).toHaveBeenCalledTimes(1); + }); + + it('caches business-user tokens per user', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + const assertion1 = signedJwt({ + user_uuid: 'user-1', + app_tid: 'tenant-1' + }); + const assertion2 = signedJwt({ + user_uuid: 'user-2', + app_tid: 'tenant-1' + }); + + await getIasClientCredentialsToken(mockIasService, { + actAs: 'business-user', + assertion: assertion1 + }); + await getIasClientCredentialsToken(mockIasService, { + actAs: 'business-user', + assertion: assertion2 + }); + + // Different users should result in 2 network calls + expect(mockedAxios.request).toHaveBeenCalledTimes(2); + }); + + it('isolates cache by tenant for technical-user', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + await getIasClientCredentialsToken(mockIasService, { + appTenantId: 'tenant-1' + }); + await getIasClientCredentialsToken(mockIasService, { + appTenantId: 'tenant-2' + }); + + // Different tenants should result in 2 network calls + expect(mockedAxios.request).toHaveBeenCalledTimes(2); + }); + + it('isolates cache by resource parameter', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + await getIasClientCredentialsToken(mockIasService, { + resource: { name: 'app-1' } + }); + await getIasClientCredentialsToken(mockIasService, { + resource: { name: 'app-2' } + }); + + // Different resources should result in 2 network calls + expect(mockedAxios.request).toHaveBeenCalledTimes(2); + }); + + it('reuses cache for same resource across calls', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + const first = await getIasClientCredentialsToken(mockIasService, { + resource: { name: 'my-app' } + }); + const second = await getIasClientCredentialsToken(mockIasService, { + resource: { name: 'my-app' } + }); + + expect(first).toEqual(mockTokenResponse); + expect(second).toEqual(mockTokenResponse); + expect(mockedAxios.request).toHaveBeenCalledTimes(1); + }); + + it('does not cache if token fetch fails', async () => { + mockedAxios.request.mockRejectedValue(new Error('Network error')); + + await expect( + getIasClientCredentialsToken(mockIasService, {}) + ).rejects.toThrow(); + + // Second call should also make a network request + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + await getIasClientCredentialsToken(mockIasService, {}); + + expect(mockedAxios.request).toHaveBeenCalledTimes(2); + }); + }); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 660b988e35..5c5145e42f 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -5,6 +5,7 @@ import { createLogger } from '@sap-cloud-sdk/util'; import axios from 'axios'; import { decodeJwt, isXsuaaToken } from './jwt'; import { resolveServiceBinding } from './environment-accessor'; +import { iasTokenCache } from './ias-token-cache'; import type { DestinationOptions, ServiceBindingTransformOptions @@ -65,136 +66,146 @@ export async function getIasClientCredentialsToken( options: ServiceBindingTransformOptions['iasOptions'] = {} ): Promise { const resolvedService = resolveServiceBinding(service); + const { clientid } = resolvedService.credentials; + + const cachedToken = iasTokenCache.getToken(clientid, options); + if (cachedToken) { + return cachedToken; + } const fnArgument: IasParameters = { serviceCredentials: resolvedService.credentials, ...options }; - const iasPromise = async function ( - arg: IasParameters - ): Promise { - const { url, clientid, certificate, key, clientsecret } = - arg.serviceCredentials; - - if (!url || !clientid) { - throw new Error( - 'IAS credentials must contain "url" and "clientid" properties.' - ); + const token = await executeWithMiddleware< + IasParameters, + ClientCredentialsResponse, + MiddlewareContext + >(resilience(), { + fn: getIasClientCredentialsTokenImpl, + fnArgument, + context: { + uri: fnArgument.serviceCredentials.url, + tenantId: fnArgument.serviceCredentials.tenantid } + }).catch(err => { + throw new Error( + `Could not fetch IAS client credentials token for service of type ${resolvedService.label}: ${err.message}` + ); + }); - // Build form data - const params = new URLSearchParams({ - client_id: clientid - }); + // Cache the token + iasTokenCache.cacheToken(clientid, options, token); - // Determine grant type based on actAs parameter - const actAs = arg.actAs || 'technical-user'; + return token; +} - if (actAs === 'business-user') { - // JWT bearer grant for business user propagation - if (!arg.assertion) { - throw new Error( - 'JWT assertion required for actAs: "business-user". Provide iasOptions.assertion.' - ); - } - params.append('assertion', arg.assertion); - params.append( - 'grant_type', - 'urn:ietf:params:oauth:grant-type:jwt-bearer' - ); - // Workaround for an IAS issue - params.append('refresh_token', '0'); - } else { - // Client credentials for technical users - params.append('grant_type', 'client_credentials'); - } +/** + * Implementation of the IAS client credentials token retrieval. + * @param arg - The parameters for IAS token retrieval. + * @returns A promise resolving to the client credentials response. + * @internal + */ +async function getIasClientCredentialsTokenImpl( + arg: IasParameters +): Promise { + const { url, clientid, certificate, key, clientsecret } = + arg.serviceCredentials; - if (arg.resource) { - let fullResource = ''; - if ('name' in arg.resource) { - fullResource = `urn:sap:identity:application:provider:name:${arg.resource.name}`; - } else { - fullResource = `urn:sap:identity:application:provider:clientid:${arg.resource.clientId}`; - if (arg.resource.tenantId) { - fullResource += `:tenantid:${arg.resource.tenantId}`; - } - } + if (!url || !clientid) { + throw new Error( + 'IAS credentials must contain "url" and "clientid" properties.' + ); + } - params.append('resource', fullResource); - logger.debug( - `Fetching IAS token with resource parameter: ${fullResource}` - ); - } + // Build form data + const params = new URLSearchParams({ + client_id: clientid + }); - if (arg.appTenantId) { - params.append('app_tid', arg.appTenantId); - logger.debug( - `Fetching IAS token with app_tid parameter: ${arg.appTenantId}` - ); - } + // Determine grant type based on actAs parameter + const actAs = arg.actAs || 'technical-user'; - // Ensure JWT token format, not mandatory but we expect JWTs - // and the docs mention in some cases we may get opaque tokens otherwise - params.append('token_format', 'jwt'); - - const tokenUrl = `${url}/oauth2/token`; - const headers: Record = { - 'Content-Type': 'application/x-www-form-urlencoded' - }; - - const requestConfig: RawAxiosRequestConfig = { - method: 'post', - url: tokenUrl, - headers - }; - - // Determine authentication method - if (certificate && key) { - // mTLS authentication - logger.debug('Using certificate-based authentication for IAS token.'); - requestConfig.httpsAgent = new https.Agent({ - cert: certificate, - key - }); - } else if (clientsecret) { - logger.debug('Using client secret authentication for IAS token.'); - params.append('client_secret', clientsecret); - } else { + if (actAs === 'business-user') { + // JWT bearer grant for business user propagation + if (!arg.assertion) { throw new Error( - 'IAS credentials must contain either "certificate" and "key" for mTLS, or "clientsecret" for client secret authentication.' + 'JWT assertion required for actAs: "business-user". Provide iasOptions.assertion.' ); } - - if (arg.extraParams) { - for (const [paramKey, paramValue] of Object.entries(arg.extraParams)) { - params.append(paramKey, paramValue); + params.append('assertion', arg.assertion); + params.append('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer'); + // Workaround for an IAS issue + params.append('refresh_token', '0'); + } else { + // Client credentials for technical users + params.append('grant_type', 'client_credentials'); + } + + if (arg.resource) { + let fullResource = ''; + if ('name' in arg.resource) { + fullResource = `urn:sap:identity:application:provider:name:${arg.resource.name}`; + } else { + fullResource = `urn:sap:identity:application:provider:clientid:${arg.resource.clientId}`; + if (arg.resource.tenantId) { + fullResource += `:tenantid:${arg.resource.tenantId}`; } } - requestConfig.data = params.toString(); + params.append('resource', fullResource); + logger.debug(`Fetching IAS token with resource parameter: ${fullResource}`); + } - logger.info(params.toString()); + if (arg.appTenantId) { + params.append('app_tid', arg.appTenantId); + logger.debug( + `Fetching IAS token with app_tid parameter: ${arg.appTenantId}` + ); + } + + // Ensure JWT token format, not mandatory but we expect JWTs + // and the docs mention in some cases we may get opaque tokens otherwise + params.append('token_format', 'jwt'); - const response = - await axios.request(requestConfig); - return response.data; + const tokenUrl = `${url}/oauth2/token`; + const headers: Record = { + 'Content-Type': 'application/x-www-form-urlencoded' }; - return executeWithMiddleware< - IasParameters, - ClientCredentialsResponse, - MiddlewareContext - >(resilience(), { - fn: iasPromise, - fnArgument, - context: { - uri: fnArgument.serviceCredentials.url, - tenantId: fnArgument.serviceCredentials.tenantid - } - }).catch(err => { + const requestConfig: RawAxiosRequestConfig = { + method: 'post', + url: tokenUrl, + headers + }; + + // Determine authentication method + if (certificate && key) { + // mTLS authentication + logger.debug('Using certificate-based authentication for IAS token.'); + requestConfig.httpsAgent = new https.Agent({ + cert: certificate, + key + }); + } else if (clientsecret) { + logger.debug('Using client secret authentication for IAS token.'); + params.append('client_secret', clientsecret); + } else { throw new Error( - `Could not fetch IAS client credentials token for service of type ${resolvedService.label}: ${err.message}` + 'IAS credentials must contain either "certificate" and "key" for mTLS, or "clientsecret" for client secret authentication.' ); - }); + } + + if (arg.extraParams) { + for (const [paramKey, paramValue] of Object.entries(arg.extraParams)) { + params.append(paramKey, paramValue); + } + } + + requestConfig.data = params.toString(); + + const response = + await axios.request(requestConfig); + return response.data; } From 437fd877f4cd48cfba92ca43a24900dd097418a3 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 15 Dec 2025 11:10:00 +0100 Subject: [PATCH 18/58] harmonize towards `AuthenticationType` and allow disabling cache --- .../destination/destination-accessor-types.ts | 2 +- .../destination/destination-from-vcap.ts | 27 +++++--- .../src/scp-cf/ias-token-cache.spec.ts | 16 ++--- .../src/scp-cf/ias-token-cache.ts | 11 ++-- .../src/scp-cf/identity-service.spec.ts | 63 ++++++++++++++++--- .../src/scp-cf/identity-service.ts | 40 +++++------- 6 files changed, 104 insertions(+), 55 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts b/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts index 4c4479dc81..4dc555bdec 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts @@ -51,7 +51,7 @@ export interface DestinationAccessorOptions { * ATTENTION: The property is mandatory in the following cases: * - User-dependent authentication flow is used, e.g., `OAuth2UserTokenExchange`, `OAuth2JWTBearer`, `OAuth2SAMLBearerAssertion`, `SAMLAssertion` or `PrincipalPropagation`. * - Multi-tenant scenarios with destinations maintained in the subscriber account. This case is implied if the `selectionStrategy` is set to `alwaysSubscriber`. - * - IAS token exchange with `iasOptions.actAs` set to `'business-user'`. In this case, the JWT is automatically used as the assertion for IAS token exchange. + * - IAS token exchange with `iasOptions.authenticationType` set to `'OAuth2JWTBearer'`. In this case, the JWT is automatically used as the assertion for IAS token exchange. */ jwt?: string; diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index 4bcb31f9eb..d1f0a43a8f 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -10,7 +10,10 @@ import { serviceToDestinationTransformers } from './service-binding-to-destinati import { setForwardedAuthTokenIfNeeded } from './forward-auth-token'; import type { Xor } from '@sap-cloud-sdk/util'; import type { DestinationFetchOptions } from './destination-accessor-types'; -import type { Destination } from './destination-service-types'; +import type { + AuthenticationType, + Destination +} from './destination-service-types'; import type { CachingOptions } from '../cache'; import type { Service } from '../environment-accessor'; import type { JwtPayload } from '../jsonwebtoken-type'; @@ -43,7 +46,7 @@ export async function getDestinationFromServiceBinding( // If using business user authentication with IAS and no assertion provided, use the JWT from options let iasOptions = options.iasOptions; if ( - iasOptions?.actAs === 'business-user' && + iasOptions?.authenticationType === 'OAuth2JWTBearer' && options.jwt && !iasOptions.assertion ) { @@ -131,13 +134,21 @@ interface IasOptionsBase { * Additional parameters to be sent along with the token request. */ extraParams?: Record; + /** + * Whether to use the cache for IAS tokens. + * @default true + */ + useCache?: boolean; } /** - * IAS options for technical user authentication. + * IAS options for technical user authentication (client credentials). */ type IasOptionsTechnical = IasOptionsBase & { - actAs?: 'technical-user'; + /** + * Authentication type. Use 'OAuth2ClientCredentials' for technical user (default). + */ + authenticationType?: Extract; /** * Assertion not used for technical user authentication. */ @@ -145,13 +156,13 @@ type IasOptionsTechnical = IasOptionsBase & { }; /** - * IAS options for business user authentication. + * IAS options for business user authentication (JWT bearer). */ type IasOptionsBusinessUser = IasOptionsBase & { /** - * Specifies business user authentication. + * Authentication type. Use 'OAuth2JWTBearer' for business user authentication. */ - actAs: 'business-user'; + authenticationType: Extract; /** * The JWT assertion string to use for business user authentication (required). */ @@ -159,7 +170,7 @@ type IasOptionsBusinessUser = IasOptionsBase & { }; /** - * Options for IAS token retrieval with type-safe actAs/jwt relationship. + * Options for IAS token retrieval with type-safe authenticationType/assertion relationship. */ export type IasOptions = IasOptionsTechnical | IasOptionsBusinessUser; diff --git a/packages/connectivity/src/scp-cf/ias-token-cache.spec.ts b/packages/connectivity/src/scp-cf/ias-token-cache.spec.ts index 496d869758..d547aea6aa 100644 --- a/packages/connectivity/src/scp-cf/ias-token-cache.spec.ts +++ b/packages/connectivity/src/scp-cf/ias-token-cache.spec.ts @@ -135,7 +135,7 @@ describe('ias-token-cache', () => { }); const cacheKey = getCacheKey('test-client-id', { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion }); expect(cacheKey).toBe('user-123:tenant-456:test-client-id:'); @@ -148,7 +148,7 @@ describe('ias-token-cache', () => { }); const cacheKey = getCacheKey('test-client-id', { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion, resource: { name: 'my-app' } }); @@ -159,11 +159,11 @@ describe('ias-token-cache', () => { jest.spyOn(logger, 'warn'); const cacheKey = getCacheKey('test-client-id', { - actAs: 'business-user' + authenticationType: 'OAuth2JWTBearer' } as any); expect(cacheKey).toBeUndefined(); expect(logger.warn).toHaveBeenCalledWith( - 'Cannot create cache key for IAS token cache. Business-user flow requires assertion JWT.' + 'Cannot create cache key for IAS token cache. OAuth2JWTBearer flow requires assertion JWT.' ); }); @@ -175,7 +175,7 @@ describe('ias-token-cache', () => { }); const cacheKey = getCacheKey('test-client-id', { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion }); expect(cacheKey).toBeUndefined(); @@ -192,7 +192,7 @@ describe('ias-token-cache', () => { }); const cacheKey = getCacheKey('test-client-id', { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion }); expect(cacheKey).toBeUndefined(); @@ -238,11 +238,11 @@ describe('ias-token-cache', () => { }); const key1 = getCacheKey('test-client-id', { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion: assertion1 }); const key2 = getCacheKey('test-client-id', { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion: assertion2 }); diff --git a/packages/connectivity/src/scp-cf/ias-token-cache.ts b/packages/connectivity/src/scp-cf/ias-token-cache.ts index 75411f2483..e0c169abae 100644 --- a/packages/connectivity/src/scp-cf/ias-token-cache.ts +++ b/packages/connectivity/src/scp-cf/ias-token-cache.ts @@ -70,9 +70,9 @@ function normalizeResource(resource?: IasOptions['resource']): string { } /** - * Generates a cache key for IAS tokens based on actAs mode, user/tenant context, and resource. + * Generates a cache key for IAS tokens based on authenticationType, user/tenant context, and resource. * @param clientId - The client ID from service credentials. - * @param options - IAS options containing actAs, assertion, resource, and appTenantId. + * @param options - IAS options containing authenticationType, assertion, resource, and appTenantId. * @returns Cache key string or undefined if required components are missing. * @internal */ @@ -80,7 +80,8 @@ export function getCacheKey( clientId: string, options: ServiceBindingTransformOptions['iasOptions'] = {} ): string | undefined { - const actAs = options.actAs || 'technical-user'; + const authenticationType = + options.authenticationType || 'OAuth2ClientCredentials'; const resourceStr = normalizeResource(options.resource); if (!clientId) { @@ -90,10 +91,10 @@ export function getCacheKey( return undefined; } - if (actAs === 'business-user') { + if (authenticationType === 'OAuth2JWTBearer') { if (!options.assertion) { logger.warn( - 'Cannot create cache key for IAS token cache. Business-user flow requires assertion JWT.' + 'Cannot create cache key for IAS token cache. OAuth2JWTBearer flow requires assertion JWT.' ); return undefined; } diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 8b7ea50d89..2034c21641 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -269,7 +269,7 @@ describe('getIasClientCredentialsToken', () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); await getIasClientCredentialsToken(mockIasService, { - actAs: 'technical-user' + authenticationType: 'OAuth2ClientCredentials' }); const callData = mockedAxios.request.mock.calls[0][0].data; @@ -287,7 +287,7 @@ describe('getIasClientCredentialsToken', () => { }); await getIasClientCredentialsToken(mockIasService, { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion: userAssertion }); @@ -304,9 +304,11 @@ describe('getIasClientCredentialsToken', () => { it('throws error for business-user without assertion', async () => { await expect( getIasClientCredentialsToken(mockIasService, { - actAs: 'business-user' + authenticationType: 'OAuth2JWTBearer' } as any) - ).rejects.toThrow('JWT assertion required for actAs: "business-user"'); + ).rejects.toThrow( + 'JWT assertion required for authenticationType: "OAuth2JWTBearer"' + ); }); it('includes refresh_token workaround only for business-user', async () => { @@ -314,7 +316,7 @@ describe('getIasClientCredentialsToken', () => { // Technical user - no refresh_token await getIasClientCredentialsToken(mockIasService, { - actAs: 'technical-user' + authenticationType: 'OAuth2ClientCredentials' }); let callData = mockedAxios.request.mock.calls[0][0].data; expect(callData).not.toContain('refresh_token=0'); @@ -328,7 +330,7 @@ describe('getIasClientCredentialsToken', () => { app_tid: 'test-tenant' }); await getIasClientCredentialsToken(mockIasService, { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion: userAssertion }); callData = mockedAxios.request.mock.calls[0][0].data; @@ -344,7 +346,7 @@ describe('getIasClientCredentialsToken', () => { }); await getIasClientCredentialsToken(mockIasService, { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion: userAssertion, resource: { name: 'my-app' }, appTenantId: 'tenant-123' @@ -390,11 +392,11 @@ describe('getIasClientCredentialsToken', () => { }); await getIasClientCredentialsToken(mockIasService, { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion: assertion1 }); await getIasClientCredentialsToken(mockIasService, { - actAs: 'business-user', + authenticationType: 'OAuth2JWTBearer', assertion: assertion2 }); @@ -458,5 +460,48 @@ describe('getIasClientCredentialsToken', () => { expect(mockedAxios.request).toHaveBeenCalledTimes(2); }); + + it('does not use cache when useCache is false', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + const first = await getIasClientCredentialsToken(mockIasService, { + useCache: false + }); + const second = await getIasClientCredentialsToken(mockIasService, { + useCache: false + }); + + expect(first).toEqual(mockTokenResponse); + expect(second).toEqual(mockTokenResponse); + // Should make 2 network calls since caching is disabled + expect(mockedAxios.request).toHaveBeenCalledTimes(2); + }); + + it('does not cache token when useCache is false', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + // First call with useCache=false + await getIasClientCredentialsToken(mockIasService, { + useCache: false + }); + + // Second call with useCache=true (default) should still make a network call + await getIasClientCredentialsToken(mockIasService); + + // Should make 2 network calls: first didn't cache, second had no cached value + expect(mockedAxios.request).toHaveBeenCalledTimes(2); + }); + + it('uses cache by default when useCache is not specified', async () => { + mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + + const first = await getIasClientCredentialsToken(mockIasService); + const second = await getIasClientCredentialsToken(mockIasService); + + expect(first).toEqual(mockTokenResponse); + expect(second).toEqual(mockTokenResponse); + // Should make only 1 network call since caching is enabled by default + expect(mockedAxios.request).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 5c5145e42f..76eca453fc 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -20,20 +20,6 @@ const logger = createLogger({ messageContext: 'identity-service' }); -/** - * Specifies which user identity should be used for authentication. - * Determines whether to use technical client credentials or propagate a business user's identity. - */ -export type ActAs = - /** - * Technical user from the service binding (default). - */ - | 'technical-user' - /** - * Business user from the current request context (requires JWT). - */ - | 'business-user'; - /** * @internal * Checks whether the IAS token to XSUAA token exchange should be applied. @@ -57,7 +43,7 @@ type IasParameters = { * Make a client credentials request against the IAS OAuth2 endpoint. * Supports both certificate-based (mTLS) and client secret authentication. * @param service - Service as it is defined in the environment variable. - * @param options - Options for token fetching, including actAs to specify authentication mode, optional resource parameter for app2app, appTenantId for multi-tenant scenarios, and extraParams for additional OAuth2 parameters. + * @param options - Options for token fetching, including authenticationType to specify authentication mode, optional resource parameter for app2app, appTenantId for multi-tenant scenarios, and extraParams for additional OAuth2 parameters. * @returns Client credentials token response. * @internal */ @@ -67,10 +53,13 @@ export async function getIasClientCredentialsToken( ): Promise { const resolvedService = resolveServiceBinding(service); const { clientid } = resolvedService.credentials; + const useCache = options.useCache ?? true; - const cachedToken = iasTokenCache.getToken(clientid, options); - if (cachedToken) { - return cachedToken; + if (useCache) { + const cachedToken = iasTokenCache.getToken(clientid, options); + if (cachedToken) { + return cachedToken; + } } const fnArgument: IasParameters = { @@ -95,8 +84,10 @@ export async function getIasClientCredentialsToken( ); }); - // Cache the token - iasTokenCache.cacheToken(clientid, options, token); + // Cache the token if caching is enabled + if (useCache) { + iasTokenCache.cacheToken(clientid, options, token); + } return token; } @@ -124,14 +115,15 @@ async function getIasClientCredentialsTokenImpl( client_id: clientid }); - // Determine grant type based on actAs parameter - const actAs = arg.actAs || 'technical-user'; + // Determine grant type based on authenticationType parameter + const authenticationType = + arg.authenticationType || 'OAuth2ClientCredentials'; - if (actAs === 'business-user') { + if (authenticationType === 'OAuth2JWTBearer') { // JWT bearer grant for business user propagation if (!arg.assertion) { throw new Error( - 'JWT assertion required for actAs: "business-user". Provide iasOptions.assertion.' + 'JWT assertion required for authenticationType: "OAuth2JWTBearer". Provide iasOptions.assertion.' ); } params.append('assertion', arg.assertion); From de930e4bae5b0e297673216f134c9ebd63dc3730 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 15 Dec 2025 12:57:53 +0100 Subject: [PATCH 19/58] harmonize ias/xsuaa caching --- packages/connectivity/src/index.ts | 1 - .../client-credentials-token-cache.spec.ts | 170 ++++++++++- .../scp-cf/client-credentials-token-cache.ts | 46 ++- .../destination/destination-from-vcap.ts | 41 ++- .../environment-accessor-types.ts | 6 + .../src/scp-cf/ias-token-cache.spec.ts | 265 ------------------ .../src/scp-cf/ias-token-cache.ts | 135 --------- .../src/scp-cf/identity-service.spec.ts | 158 +---------- .../src/scp-cf/identity-service.ts | 15 - .../src/scp-cf/token-accessor.spec.ts | 28 +- .../connectivity/src/scp-cf/token-accessor.ts | 39 ++- 11 files changed, 302 insertions(+), 602 deletions(-) delete mode 100644 packages/connectivity/src/scp-cf/ias-token-cache.spec.ts delete mode 100644 packages/connectivity/src/scp-cf/ias-token-cache.ts diff --git a/packages/connectivity/src/index.ts b/packages/connectivity/src/index.ts index f28fc185d9..e83d7f1c69 100644 --- a/packages/connectivity/src/index.ts +++ b/packages/connectivity/src/index.ts @@ -59,7 +59,6 @@ export type { DestinationsByType, DestinationFromServiceBindingOptions, ServiceBindingTransformOptions, - ActAs, IasOptions } from './scp-cf'; diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts index a9184bbf3d..206c7c088b 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts @@ -1,5 +1,8 @@ import { createLogger } from '@sap-cloud-sdk/util'; -import { clientCredentialsTokenCache } from './client-credentials-token-cache'; +import { + clientCredentialsTokenCache, + getCacheKey +} from './client-credentials-token-cache'; const oneHourInSeconds = 60 * 60; @@ -18,6 +21,7 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', + undefined, validToken ); @@ -25,7 +29,8 @@ describe('ClientCredentialsTokenCache', () => { const valid = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - 'clientid' + 'clientid', + undefined ); expect(valid).toEqual(validToken); @@ -45,13 +50,15 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', + undefined, expiredToken ); jest.advanceTimersByTime(oneHourInSeconds * 2 * 1000); const expired = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - 'clientid' + 'clientid', + undefined ); expect(expired).toBeUndefined(); @@ -72,6 +79,7 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', + undefined, validToken ); @@ -79,7 +87,8 @@ describe('ClientCredentialsTokenCache', () => { const invalid = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - undefined as any + undefined as any, + undefined ); expect(invalid).toBeUndefined(); @@ -87,4 +96,157 @@ describe('ClientCredentialsTokenCache', () => { 'Cannot create cache key for client credentials token cache. The given client ID is undefined.' ); }); + + describe('IAS resource parameter support', () => { + const validToken = { + access_token: '1234567890', + token_type: 'Bearer', + expires_in: oneHourInSeconds * 3, + jti: '', + scope: '' + }; + + beforeEach(() => { + clientCredentialsTokenCache.clear(); + }); + + it('should cache and retrieve token with resource name', () => { + const resource = { name: 'my-app' }; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resource, + validToken + ); + + const cached = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource + ); + + expect(cached).toEqual(validToken); + }); + + it('should cache and retrieve token with resource clientId', () => { + const resource = { clientId: 'resource-client-123' }; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resource, + validToken + ); + + const cached = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource + ); + + expect(cached).toEqual(validToken); + }); + + it('should cache and retrieve token with resource clientId and tenantId', () => { + const resource = { + clientId: 'resource-client-123', + tenantId: 'tenant-456' + }; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resource, + validToken + ); + + const cached = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource + ); + + expect(cached).toEqual(validToken); + }); + + it('should isolate cache by resource name', () => { + const resource1 = { name: 'app-1' }; + const resource2 = { name: 'app-2' }; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resource1, + validToken + ); + + const cached1 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource1 + ); + const cached2 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource2 + ); + + expect(cached1).toEqual(validToken); + expect(cached2).toBeUndefined(); + }); + + it('should isolate cache by resource clientId', () => { + const resource1 = { clientId: 'client-1' }; + const resource2 = { clientId: 'client-2' }; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resource1, + validToken + ); + + const cached1 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource1 + ); + const cached2 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource2 + ); + + expect(cached1).toEqual(validToken); + expect(cached2).toBeUndefined(); + }); + + it('should generate correct cache key with resource name', () => { + const key = getCacheKey('tenant-123', 'client-id', { name: 'my-app' }); + expect(key).toBe('tenant-123:client-id:name=my-app'); + }); + + it('should generate correct cache key with resource clientId only', () => { + const key = getCacheKey('tenant-123', 'client-id', { + clientId: 'resource-client-123' + }); + expect(key).toBe('tenant-123:client-id:clientid=resource-client-123'); + }); + + it('should generate correct cache key with resource clientId and tenantId', () => { + const key = getCacheKey('tenant-123', 'client-id', { + clientId: 'resource-client-123', + tenantId: 'tenant-456' + }); + expect(key).toBe( + 'tenant-123:client-id:clientid=resource-client-123:tenantid=tenant-456' + ); + }); + + it('should generate cache key without resource when not provided', () => { + const key = getCacheKey('tenant-123', 'client-id'); + expect(key).toBe('tenant-123:client-id'); + }); + }); }); diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts index cd146bf4c2..7ec34e36f3 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sap-cloud-sdk/util'; import { Cache } from './cache'; import type { ClientCredentialsResponse } from './xsuaa-service-types'; +import type { IasResource } from './destination'; const logger = createLogger({ package: 'connectivity', @@ -12,16 +13,18 @@ const ClientCredentialsTokenCache = ( ) => ({ getToken: ( tenantId: string | undefined, - clientId: string + clientId: string, + resource?: IasResource ): ClientCredentialsResponse | undefined => - cache.get(getCacheKey(tenantId, clientId)), + cache.get(getCacheKey(tenantId, clientId, resource)), cacheToken: ( tenantId: string | undefined, clientId: string, + resource: IasResource | undefined, token: ClientCredentialsResponse ): void => { - cache.set(getCacheKey(tenantId, clientId), { + cache.set(getCacheKey(tenantId, clientId, resource), { entry: token, expires: token.expires_in ? Date.now() + token.expires_in * 1000 @@ -34,15 +37,39 @@ const ClientCredentialsTokenCache = ( getCacheInstance: () => cache }); +/** + * Normalizes the IAS resource parameter to a consistent string format for cache key. + * @param resource - The resource parameter from iasOptions. + * @returns Normalized resource string or empty string if not provided. + * @internal + */ +function normalizeResource(resource?: IasResource): string | undefined { + if (!resource) { + return undefined; + } + + if ('name' in resource) { + return `name=${resource.name}`; + } + + let normalized = `clientid=${resource.clientId}`; + if (resource.tenantId) { + normalized += `:tenantid=${resource.tenantId}`; + } + return normalized; +} + /** * * @internal * @param tenantId - The ID of the tenant to cache the token for. - * @param clientId - ClientId to fetch the token - * @returns the token + * @param clientId - ClientId to fetch the token. + * @param resource - Optional resource parameter (for IAS app2app scenarios). + * @returns The cache key. */ export function getCacheKey( tenantId: string | undefined, - clientId: string + clientId: string, + resource?: IasResource ): string | undefined { if (!tenantId) { logger.warn( @@ -56,7 +83,12 @@ export function getCacheKey( ); return; } - return [tenantId, clientId].join(':'); + const parts = [tenantId, clientId]; + const resourceStr = normalizeResource(resource); + if (resourceStr) { + parts.push(resourceStr); + } + return parts.join(':'); } /** diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index d1f0a43a8f..70fcb9c70c 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -101,6 +101,32 @@ export interface DestinationFromServiceBindingOptions { iasOptions?: IasOptions; } +/** + * The application resource for which the token is requested for App-to-App communication. + * The token will only be usable to call the requested application. + * Either provide the app name (common case) or the provider client ID + * and tenant ID (optional). + */ +export type IasResource = + Xor< + { + /** + * The name of the application resource. + */ + name: string; + }, + { + /** + * The client ID of the application resource. + */ + clientId: string; + /** + * The tenant ID of the application resource. (Optional) + */ + tenantId?: string; + } + >; + /** * Base options shared by all IAS authentication modes. */ @@ -116,15 +142,7 @@ interface IasOptionsBase { * Either provide the app name (common case) or the provider client ID * and tenant ID (optional). */ - resource?: Xor< - { - name: string; - }, - { - clientId: string; - tenantId?: string; - } - >; + resource?: IasResource; /** * The consumer (BTP) tenant ID of the application. * May be required for multi-tenant communication. @@ -134,11 +152,6 @@ interface IasOptionsBase { * Additional parameters to be sent along with the token request. */ extraParams?: Record; - /** - * Whether to use the cache for IAS tokens. - * @default true - */ - useCache?: boolean; } /** diff --git a/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts b/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts index d51f734eb9..ba12ef7040 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts @@ -1,3 +1,5 @@ +import type { IasResource } from '../destination'; + /** * Unspecific representation of a service as read from VCAP_SERVICES (for Cloud Foundry) or mounted secrets (for K8S). */ @@ -19,6 +21,10 @@ export interface Service { * The service credentials. */ credentials: ServiceCredentials; + /** + * IAS Resource to request for App-to-App communication. + */ + iasResource?: IasResource; } /** diff --git a/packages/connectivity/src/scp-cf/ias-token-cache.spec.ts b/packages/connectivity/src/scp-cf/ias-token-cache.spec.ts deleted file mode 100644 index d547aea6aa..0000000000 --- a/packages/connectivity/src/scp-cf/ias-token-cache.spec.ts +++ /dev/null @@ -1,265 +0,0 @@ -import { createLogger } from '@sap-cloud-sdk/util'; -import { signedJwt } from '../../../../test-resources/test/test-util'; -import { iasTokenCache, getCacheKey } from './ias-token-cache'; -import type { ClientCredentialsResponse } from './xsuaa-service-types'; - -const logger = createLogger({ - package: 'connectivity', - messageContext: 'ias-token-cache' -}); - -describe('ias-token-cache', () => { - const mockToken: ClientCredentialsResponse = { - access_token: 'mock-access-token', - token_type: 'Bearer', - expires_in: 3600, - scope: 'openid', - jti: 'mock-jti' - }; - - afterEach(() => { - iasTokenCache.clear(); - jest.restoreAllMocks(); - }); - - describe('getToken', () => { - it('returns cached token if valid', () => { - iasTokenCache.cacheToken('test-client-id', {}, mockToken); - - const cachedToken = iasTokenCache.getToken('test-client-id', {}); - expect(cachedToken).toEqual(mockToken); - }); - - it('returns undefined for expired token', () => { - const expiredToken = { - ...mockToken, - expires_in: -1 - }; - iasTokenCache.cacheToken('test-client-id', {}, expiredToken); - - // Wait for token to expire - jest.useFakeTimers(); - jest.advanceTimersByTime(2000); - - const cachedToken = iasTokenCache.getToken('test-client-id', {}); - expect(cachedToken).toBeUndefined(); - - jest.useRealTimers(); - }); - - it('returns undefined if cache key cannot be created', () => { - jest.spyOn(logger, 'warn'); - - const cachedToken = iasTokenCache.getToken('', {}); - expect(cachedToken).toBeUndefined(); - expect(logger.warn).toHaveBeenCalledWith( - 'Cannot create cache key for IAS token cache. The given client ID is undefined.' - ); - }); - }); - - describe('cacheToken', () => { - it('caches token with expiration', () => { - iasTokenCache.cacheToken('test-client-id', {}, mockToken); - - const cachedToken = iasTokenCache.getToken('test-client-id', {}); - expect(cachedToken).toEqual(mockToken); - }); - - it('does not cache if cache key cannot be created', () => { - jest.spyOn(logger, 'warn'); - - iasTokenCache.cacheToken('', {}, mockToken); - - expect(logger.warn).toHaveBeenCalledWith( - 'Cannot create cache key for IAS token cache. The given client ID is undefined.' - ); - }); - }); - - describe('getCacheKey', () => { - describe('technical-user flow', () => { - it('generates cache key with default tenant (provider-tenant)', () => { - const cacheKey = getCacheKey('test-client-id', {}); - expect(cacheKey).toBe('provider-tenant:test-client-id:'); - }); - - it('generates cache key with appTenantId', () => { - const cacheKey = getCacheKey('test-client-id', { - appTenantId: 'tenant-123' - }); - expect(cacheKey).toBe('tenant-123:test-client-id:'); - }); - - it('includes resource name in cache key', () => { - const cacheKey = getCacheKey('test-client-id', { - resource: { name: 'my-app' } - }); - expect(cacheKey).toBe('provider-tenant:test-client-id:name=my-app'); - }); - - it('includes resource clientId in cache key', () => { - const cacheKey = getCacheKey('test-client-id', { - resource: { clientId: 'resource-client-123' } - }); - expect(cacheKey).toBe( - 'provider-tenant:test-client-id:clientid=resource-client-123' - ); - }); - - it('includes resource clientId and tenantId in cache key', () => { - const cacheKey = getCacheKey('test-client-id', { - resource: { clientId: 'resource-client-123', tenantId: 'tenant-456' } - }); - expect(cacheKey).toBe( - 'provider-tenant:test-client-id:clientid=resource-client-123:tenantid=tenant-456' - ); - }); - - it('returns undefined if clientId is missing', () => { - jest.spyOn(logger, 'warn'); - - const cacheKey = getCacheKey('', {}); - expect(cacheKey).toBeUndefined(); - expect(logger.warn).toHaveBeenCalledWith( - 'Cannot create cache key for IAS token cache. The given client ID is undefined.' - ); - }); - }); - - describe('business-user flow', () => { - it('generates cache key with user and tenant from assertion', () => { - const assertion = signedJwt({ - user_uuid: 'user-123', - app_tid: 'tenant-456' - }); - - const cacheKey = getCacheKey('test-client-id', { - authenticationType: 'OAuth2JWTBearer', - assertion - }); - expect(cacheKey).toBe('user-123:tenant-456:test-client-id:'); - }); - - it('generates cache key with resource', () => { - const assertion = signedJwt({ - user_uuid: 'user-123', - app_tid: 'tenant-456' - }); - - const cacheKey = getCacheKey('test-client-id', { - authenticationType: 'OAuth2JWTBearer', - assertion, - resource: { name: 'my-app' } - }); - expect(cacheKey).toBe('user-123:tenant-456:test-client-id:name=my-app'); - }); - - it('returns undefined if assertion is missing', () => { - jest.spyOn(logger, 'warn'); - - const cacheKey = getCacheKey('test-client-id', { - authenticationType: 'OAuth2JWTBearer' - } as any); - expect(cacheKey).toBeUndefined(); - expect(logger.warn).toHaveBeenCalledWith( - 'Cannot create cache key for IAS token cache. OAuth2JWTBearer flow requires assertion JWT.' - ); - }); - - it('returns undefined if user ID cannot be extracted', () => { - jest.spyOn(logger, 'warn'); - const assertion = signedJwt({ - app_tid: 'tenant-456' - // Missing user_uuid - }); - - const cacheKey = getCacheKey('test-client-id', { - authenticationType: 'OAuth2JWTBearer', - assertion - }); - expect(cacheKey).toBeUndefined(); - expect(logger.warn).toHaveBeenCalledWith( - 'Cannot create cache key for IAS token cache. User ID could not be extracted from assertion JWT.' - ); - }); - - it('returns undefined if tenant ID cannot be extracted', () => { - jest.spyOn(logger, 'warn'); - const assertion = signedJwt({ - user_uuid: 'user-123' - // Missing app_tid or zid - }); - - const cacheKey = getCacheKey('test-client-id', { - authenticationType: 'OAuth2JWTBearer', - assertion - }); - expect(cacheKey).toBeUndefined(); - expect(logger.warn).toHaveBeenCalledWith( - 'Cannot create cache key for IAS token cache. Tenant ID could not be extracted from assertion JWT.' - ); - }); - }); - - describe('cache isolation', () => { - it('isolates cache by tenant for technical-user', () => { - const key1 = getCacheKey('test-client-id', { - appTenantId: 'tenant-1' - }); - const key2 = getCacheKey('test-client-id', { - appTenantId: 'tenant-2' - }); - - expect(key1).not.toBe(key2); - expect(key1).toBe('tenant-1:test-client-id:'); - expect(key2).toBe('tenant-2:test-client-id:'); - }); - - it('isolates cache by resource', () => { - const key1 = getCacheKey('test-client-id', { - resource: { name: 'app-1' } - }); - const key2 = getCacheKey('test-client-id', { - resource: { name: 'app-2' } - }); - - expect(key1).not.toBe(key2); - }); - - it('isolates cache by user for business-user', () => { - const assertion1 = signedJwt({ - user_uuid: 'user-1', - app_tid: 'tenant-123' - }); - const assertion2 = signedJwt({ - user_uuid: 'user-2', - app_tid: 'tenant-123' - }); - - const key1 = getCacheKey('test-client-id', { - authenticationType: 'OAuth2JWTBearer', - assertion: assertion1 - }); - const key2 = getCacheKey('test-client-id', { - authenticationType: 'OAuth2JWTBearer', - assertion: assertion2 - }); - - expect(key1).not.toBe(key2); - expect(key1).toBe('user-1:tenant-123:test-client-id:'); - expect(key2).toBe('user-2:tenant-123:test-client-id:'); - }); - }); - }); - - describe('clear', () => { - it('clears all cached tokens', () => { - iasTokenCache.cacheToken('test-client-id', {}, mockToken); - expect(iasTokenCache.getToken('test-client-id', {})).toEqual(mockToken); - - iasTokenCache.clear(); - expect(iasTokenCache.getToken('test-client-id', {})).toBeUndefined(); - }); - }); -}); diff --git a/packages/connectivity/src/scp-cf/ias-token-cache.ts b/packages/connectivity/src/scp-cf/ias-token-cache.ts deleted file mode 100644 index e0c169abae..0000000000 --- a/packages/connectivity/src/scp-cf/ias-token-cache.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { createLogger } from '@sap-cloud-sdk/util'; -import { - decodeJwt, - getTenantId, - userId, - getTenantIdFromBinding, - getDefaultTenantId -} from './jwt'; -import { Cache } from './cache'; -import type { ClientCredentialsResponse } from './xsuaa-service-types'; -import type { IasOptions, ServiceBindingTransformOptions } from './destination'; - -const logger = createLogger({ - package: 'connectivity', - messageContext: 'ias-token-cache' -}); - -const IasTokenCache = (cache: Cache) => ({ - getToken: ( - clientId: string, - options: IasOptions = {} - ): ClientCredentialsResponse | undefined => { - const cacheKey = getCacheKey(clientId, options); - return cacheKey ? cache.get(cacheKey) : undefined; - }, - - cacheToken: ( - clientId: string, - options: IasOptions = {}, - token: ClientCredentialsResponse - ): void => { - const cacheKey = getCacheKey(clientId, options); - if (cacheKey) { - cache.set(cacheKey, { - entry: token, - expires: token.expires_in - ? Date.now() + token.expires_in * 1000 - : undefined - }); - } - }, - - clear: (): void => { - cache.clear(); - }, - - getCacheInstance: () => cache -}); - -/** - * Normalizes the resource parameter to a consistent string format for cache key. - * @param resource - The resource parameter from iasOptions. - * @returns Normalized resource string or empty string if not provided. - * @internal - */ -function normalizeResource(resource?: IasOptions['resource']): string { - if (!resource) { - return ''; - } - - if ('name' in resource) { - return `name=${resource.name}`; - } - - let normalized = `clientid=${resource.clientId}`; - if (resource.tenantId) { - normalized += `:tenantid=${resource.tenantId}`; - } - return normalized; -} - -/** - * Generates a cache key for IAS tokens based on authenticationType, user/tenant context, and resource. - * @param clientId - The client ID from service credentials. - * @param options - IAS options containing authenticationType, assertion, resource, and appTenantId. - * @returns Cache key string or undefined if required components are missing. - * @internal - */ -export function getCacheKey( - clientId: string, - options: ServiceBindingTransformOptions['iasOptions'] = {} -): string | undefined { - const authenticationType = - options.authenticationType || 'OAuth2ClientCredentials'; - const resourceStr = normalizeResource(options.resource); - - if (!clientId) { - logger.warn( - 'Cannot create cache key for IAS token cache. The given client ID is undefined.' - ); - return undefined; - } - - if (authenticationType === 'OAuth2JWTBearer') { - if (!options.assertion) { - logger.warn( - 'Cannot create cache key for IAS token cache. OAuth2JWTBearer flow requires assertion JWT.' - ); - return undefined; - } - - const decodedAssertion = decodeJwt(options.assertion); - const user = userId(decodedAssertion); - - if (!user) { - logger.warn( - 'Cannot create cache key for IAS token cache. User ID could not be extracted from assertion JWT.' - ); - return undefined; - } - - const assertionTenant = getTenantId(decodedAssertion); - - if (!assertionTenant) { - logger.warn( - 'Cannot create cache key for IAS token cache. Tenant ID could not be extracted from assertion JWT.' - ); - return undefined; - } - - return [user, assertionTenant, clientId, resourceStr].join(':'); - } - - const tenant = - options.appTenantId || getTenantIdFromBinding() || getDefaultTenantId(); - - return [tenant, clientId, resourceStr].join(':'); -} - -/** - * @internal - */ -export const iasTokenCache = IasTokenCache( - new Cache(5 * 60 * 1000 /* 5 minutes in ms */) -); diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 2034c21641..7965883398 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -4,7 +4,6 @@ import { getIasClientCredentialsToken, shouldExchangeToken } from './identity-service'; -import { iasTokenCache } from './ias-token-cache'; import type { Service } from './environment-accessor'; jest.mock('axios'); @@ -73,10 +72,6 @@ describe('getIasClientCredentialsToken', () => { jest.clearAllMocks(); }); - afterEach(() => { - iasTokenCache.clear(); - }); - it('fetches IAS token with mTLS authentication', async () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); @@ -253,8 +248,8 @@ describe('getIasClientCredentialsToken', () => { ); }); - describe('actAs parameter', () => { - it('uses technical-user by default', async () => { + describe('authenticationType parameter', () => { + it('uses OAuth2ClientCredentials (technical-user) by default', async () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); await getIasClientCredentialsToken(mockIasService, {}); @@ -265,7 +260,7 @@ describe('getIasClientCredentialsToken', () => { expect(callData).not.toContain('refresh_token=0'); }); - it('uses client credentials for technical-user', async () => { + it('uses client credentials for OAuth2ClientCredentials', async () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); await getIasClientCredentialsToken(mockIasService, { @@ -278,7 +273,7 @@ describe('getIasClientCredentialsToken', () => { expect(callData).not.toContain('refresh_token=0'); }); - it('uses JWT bearer grant for business-user with assertion', async () => { + it('uses JWT bearer grant for OAuth2JWTBearer (business-user) with assertion', async () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); const userAssertion = signedJwt({ @@ -301,7 +296,7 @@ describe('getIasClientCredentialsToken', () => { expect(callData).toContain('refresh_token=0'); // Workaround applied for business users }); - it('throws error for business-user without assertion', async () => { + it('throws error for OAuth2JWTBearer (business-user) without assertion', async () => { await expect( getIasClientCredentialsToken(mockIasService, { authenticationType: 'OAuth2JWTBearer' @@ -311,7 +306,7 @@ describe('getIasClientCredentialsToken', () => { ); }); - it('includes refresh_token workaround only for business-user', async () => { + it('includes refresh_token workaround only for OAuth2ClientCredentials (technical-user)', async () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); // Technical user - no refresh_token @@ -322,7 +317,6 @@ describe('getIasClientCredentialsToken', () => { expect(callData).not.toContain('refresh_token=0'); jest.clearAllMocks(); - iasTokenCache.clear(); // Business user - has refresh_token const userAssertion = signedJwt({ @@ -337,7 +331,7 @@ describe('getIasClientCredentialsToken', () => { expect(callData).toContain('refresh_token=0'); }); - it('supports business-user with resource and appTenantId', async () => { + it('supports OAuth2JWTBearer (business-user) with resource and appTenantId', async () => { mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); const userAssertion = signedJwt({ @@ -366,142 +360,4 @@ describe('getIasClientCredentialsToken', () => { expect(callData).toContain('refresh_token=0'); }); }); - - describe('token caching', () => { - it('caches technical-user tokens and returns from cache on second call', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); - - const first = await getIasClientCredentialsToken(mockIasService, {}); - const second = await getIasClientCredentialsToken(mockIasService, {}); - - expect(first).toEqual(mockTokenResponse); - expect(second).toEqual(mockTokenResponse); - expect(mockedAxios.request).toHaveBeenCalledTimes(1); - }); - - it('caches business-user tokens per user', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); - - const assertion1 = signedJwt({ - user_uuid: 'user-1', - app_tid: 'tenant-1' - }); - const assertion2 = signedJwt({ - user_uuid: 'user-2', - app_tid: 'tenant-1' - }); - - await getIasClientCredentialsToken(mockIasService, { - authenticationType: 'OAuth2JWTBearer', - assertion: assertion1 - }); - await getIasClientCredentialsToken(mockIasService, { - authenticationType: 'OAuth2JWTBearer', - assertion: assertion2 - }); - - // Different users should result in 2 network calls - expect(mockedAxios.request).toHaveBeenCalledTimes(2); - }); - - it('isolates cache by tenant for technical-user', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); - - await getIasClientCredentialsToken(mockIasService, { - appTenantId: 'tenant-1' - }); - await getIasClientCredentialsToken(mockIasService, { - appTenantId: 'tenant-2' - }); - - // Different tenants should result in 2 network calls - expect(mockedAxios.request).toHaveBeenCalledTimes(2); - }); - - it('isolates cache by resource parameter', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); - - await getIasClientCredentialsToken(mockIasService, { - resource: { name: 'app-1' } - }); - await getIasClientCredentialsToken(mockIasService, { - resource: { name: 'app-2' } - }); - - // Different resources should result in 2 network calls - expect(mockedAxios.request).toHaveBeenCalledTimes(2); - }); - - it('reuses cache for same resource across calls', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); - - const first = await getIasClientCredentialsToken(mockIasService, { - resource: { name: 'my-app' } - }); - const second = await getIasClientCredentialsToken(mockIasService, { - resource: { name: 'my-app' } - }); - - expect(first).toEqual(mockTokenResponse); - expect(second).toEqual(mockTokenResponse); - expect(mockedAxios.request).toHaveBeenCalledTimes(1); - }); - - it('does not cache if token fetch fails', async () => { - mockedAxios.request.mockRejectedValue(new Error('Network error')); - - await expect( - getIasClientCredentialsToken(mockIasService, {}) - ).rejects.toThrow(); - - // Second call should also make a network request - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); - await getIasClientCredentialsToken(mockIasService, {}); - - expect(mockedAxios.request).toHaveBeenCalledTimes(2); - }); - - it('does not use cache when useCache is false', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); - - const first = await getIasClientCredentialsToken(mockIasService, { - useCache: false - }); - const second = await getIasClientCredentialsToken(mockIasService, { - useCache: false - }); - - expect(first).toEqual(mockTokenResponse); - expect(second).toEqual(mockTokenResponse); - // Should make 2 network calls since caching is disabled - expect(mockedAxios.request).toHaveBeenCalledTimes(2); - }); - - it('does not cache token when useCache is false', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); - - // First call with useCache=false - await getIasClientCredentialsToken(mockIasService, { - useCache: false - }); - - // Second call with useCache=true (default) should still make a network call - await getIasClientCredentialsToken(mockIasService); - - // Should make 2 network calls: first didn't cache, second had no cached value - expect(mockedAxios.request).toHaveBeenCalledTimes(2); - }); - - it('uses cache by default when useCache is not specified', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); - - const first = await getIasClientCredentialsToken(mockIasService); - const second = await getIasClientCredentialsToken(mockIasService); - - expect(first).toEqual(mockTokenResponse); - expect(second).toEqual(mockTokenResponse); - // Should make only 1 network call since caching is enabled by default - expect(mockedAxios.request).toHaveBeenCalledTimes(1); - }); - }); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 76eca453fc..c2b7ea5619 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -5,7 +5,6 @@ import { createLogger } from '@sap-cloud-sdk/util'; import axios from 'axios'; import { decodeJwt, isXsuaaToken } from './jwt'; import { resolveServiceBinding } from './environment-accessor'; -import { iasTokenCache } from './ias-token-cache'; import type { DestinationOptions, ServiceBindingTransformOptions @@ -53,14 +52,6 @@ export async function getIasClientCredentialsToken( ): Promise { const resolvedService = resolveServiceBinding(service); const { clientid } = resolvedService.credentials; - const useCache = options.useCache ?? true; - - if (useCache) { - const cachedToken = iasTokenCache.getToken(clientid, options); - if (cachedToken) { - return cachedToken; - } - } const fnArgument: IasParameters = { serviceCredentials: resolvedService.credentials, @@ -83,12 +74,6 @@ export async function getIasClientCredentialsToken( `Could not fetch IAS client credentials token for service of type ${resolvedService.label}: ${err.message}` ); }); - - // Cache the token if caching is enabled - if (useCache) { - iasTokenCache.cacheToken(clientid, options, token); - } - return token; } diff --git a/packages/connectivity/src/scp-cf/token-accessor.spec.ts b/packages/connectivity/src/scp-cf/token-accessor.spec.ts index deb0fd0dfe..f1739c04bf 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.spec.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.spec.ts @@ -205,11 +205,13 @@ describe('token accessor', () => { const providerTokenFromCache = clientCredentialsTokenCache.getToken( providerUserPayload.zid, - destinationBindingClientSecretMock.credentials.clientid + destinationBindingClientSecretMock.credentials.clientid, + undefined ); const subscriberTokenFromCache = clientCredentialsTokenCache.getToken( subscriberUserPayload.zid, - destinationBindingClientSecretMock.credentials.clientid + destinationBindingClientSecretMock.credentials.clientid, + undefined ); expect(providerTokenFromCache?.access_token).toEqual(providerToken); @@ -218,23 +220,33 @@ describe('token accessor', () => { expect( clientCredentialsTokenCache.getToken( 'https://doesnotexist.example.com', - destinationBindingClientSecretMock.credentials.clientid + destinationBindingClientSecretMock.credentials.clientid, + undefined ) ).toBeUndefined(); expect( clientCredentialsTokenCache.getToken( 'https://doesnotexist.example.com', - 'schmusername' + 'schmusername', + undefined ) ).toBeUndefined(); expect( - clientCredentialsTokenCache.getToken(providerXsuaaUrl, 'schmusername') + clientCredentialsTokenCache.getToken( + providerXsuaaUrl, + 'schmusername', + undefined + ) ).toBeUndefined(); expect( - clientCredentialsTokenCache.getToken(subscriberXsuaaUrl, 'schmusername') + clientCredentialsTokenCache.getToken( + subscriberXsuaaUrl, + 'schmusername', + undefined + ) ).toBeUndefined(); }); @@ -320,6 +332,7 @@ describe('token accessor', () => { clientCredentialsTokenCache.cacheToken( destinationBindingClientSecretMock.credentials.tenantid, destinationBindingClientSecretMock.credentials.clientid, + undefined, { access_token: token } as ClientCredentialsResponse ); @@ -348,7 +361,8 @@ describe('token accessor', () => { expect( clientCredentialsTokenCache.getToken( destinationBindingClientSecretMock.credentials.tenantid, - destinationBindingClientSecretMock.credentials.clientid + destinationBindingClientSecretMock.credentials.clientid, + undefined ) ).toEqual(token); }); diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index 7d2b210436..48bc1abe11 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -8,9 +8,11 @@ import { import { clientCredentialsTokenCache } from './client-credentials-token-cache'; import { resolveServiceBinding } from './environment-accessor'; import { getClientCredentialsToken, getUserToken } from './xsuaa-service'; +import { getIasClientCredentialsToken } from './identity-service'; import type { Service } from './environment-accessor'; import type { CachingOptions } from './cache'; import type { JwtPayload } from './jsonwebtoken-type'; +import type { IasOptions } from './destination'; /** * Returns an access token that can be used to call the given service. The token is fetched via a client credentials grant with the credentials of the given service. @@ -40,11 +42,13 @@ export async function serviceToken( const tenantForCaching = options?.jwt ? getTenantId(options.jwt) || getSubdomain(options.jwt) : getTenantIdFromBinding() || getDefaultTenantId(); + const resourceForCaching = serviceBinding?.iasResource; if (opts.useCache) { const cachedToken = clientCredentialsTokenCache.getToken( tenantForCaching, - serviceCredentials.clientid + serviceCredentials.clientid, + resourceForCaching ); if (cachedToken) { return cachedToken.access_token; @@ -52,12 +56,19 @@ export async function serviceToken( } try { - const token = await getClientCredentialsToken(serviceBinding, options?.jwt); + const token = + serviceBinding.label === 'identity' + ? await getIasClientCredentialsToken(serviceBinding, { + resource: resourceForCaching, + ...options + }) + : await getClientCredentialsToken(serviceBinding, options?.jwt); if (opts.useCache) { clientCredentialsTokenCache.cacheToken( tenantForCaching, serviceCredentials.clientid, + resourceForCaching, token ); } @@ -75,15 +86,37 @@ export async function serviceToken( * Returns a JWT bearer token that can be used to call the given service. * The token is fetched via a JWT bearer token grant using the user token + client credentials. * + * This function automatically detects the service type (XSUAA or IAS) based on the label + * and uses the appropriate authentication flow. + * For IAS services, you can pass IAS-specific options like `resource` and `appTenantId`. + * * Throws an error if there is no instance of the given service type. * @param jwt - The JWT of the user for whom the access token should be fetched. * @param service - The type of the service or an instance of {@link Service}. + * @param options - Optional IAS-specific options like resource, appTenantId, and caching behavior. Only used for IAS services. * @returns A JWT bearer token. */ export async function jwtBearerToken( jwt: string, - service: string | Service + service: string | Service, + options?: Omit< + IasOptions, + 'authenticationType' | 'assertion' | 'destinationUrl' + > ): Promise { const resolvedService = resolveServiceBinding(service); + + // Detect if this is an IAS service + if (resolvedService.label === 'identity') { + return ( + await getIasClientCredentialsToken(resolvedService, { + ...options, + authenticationType: 'OAuth2JWTBearer', + assertion: jwt + }) + ).access_token; + } + + // XSUAA flow return getUserToken(resolvedService, jwt); } From fbcca4b08372ccb20589996f697e1885cddab260 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 16 Dec 2025 12:12:10 +0100 Subject: [PATCH 20/58] use xssec as the base implementation --- packages/connectivity/src/index.ts | 3 +- .../destination/destination-from-vcap.ts | 45 ++- .../environment-accessor-types.ts | 12 + .../scp-cf/environment-accessor/ias.spec.ts | 56 ++++ .../src/scp-cf/environment-accessor/ias.ts | 48 +++ .../src/scp-cf/environment-accessor/index.ts | 1 + .../src/scp-cf/identity-service.spec.ts | 284 ++++++------------ .../src/scp-cf/identity-service.ts | 213 +++++++------ packages/connectivity/src/scp-cf/jwt/jwt.ts | 22 +- .../connectivity/src/scp-cf/token-accessor.ts | 4 +- 10 files changed, 364 insertions(+), 324 deletions(-) create mode 100644 packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts create mode 100644 packages/connectivity/src/scp-cf/environment-accessor/ias.ts diff --git a/packages/connectivity/src/index.ts b/packages/connectivity/src/index.ts index e83d7f1c69..fc13a95dd0 100644 --- a/packages/connectivity/src/index.ts +++ b/packages/connectivity/src/index.ts @@ -59,7 +59,8 @@ export type { DestinationsByType, DestinationFromServiceBindingOptions, ServiceBindingTransformOptions, - IasOptions + IasOptions, + IasResource } from './scp-cf'; export type { diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index 70fcb9c70c..a518b95973 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -107,25 +107,24 @@ export interface DestinationFromServiceBindingOptions { * Either provide the app name (common case) or the provider client ID * and tenant ID (optional). */ -export type IasResource = - Xor< - { - /** - * The name of the application resource. - */ - name: string; - }, - { - /** - * The client ID of the application resource. - */ - clientId: string; - /** - * The tenant ID of the application resource. (Optional) - */ - tenantId?: string; - } - >; +export type IasResource = Xor< + { + /** + * The name of the application resource. + */ + name: string; + }, + { + /** + * The client ID of the application resource. + */ + clientId: string; + /** + * The tenant ID of the application resource (Optional). + */ + tenantId?: string; + } +>; /** * Base options shared by all IAS authentication modes. @@ -137,17 +136,17 @@ interface IasOptionsBase { */ targetUrl?: string; /** - * The application resource for which the token is requested. - * The token will only be usable to call the requested application. + * The application resource(s) for which the token is requested. + * The token will only be usable to call the requested application(s). * Either provide the app name (common case) or the provider client ID * and tenant ID (optional). */ - resource?: IasResource; + resource?: IasResource | IasResource[]; /** * The consumer (BTP) tenant ID of the application. * May be required for multi-tenant communication. */ - appTenantId?: string; + appTid?: string; /** * Additional parameters to be sent along with the token request. */ diff --git a/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts b/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts index ba12ef7040..10ad8dda49 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts @@ -73,3 +73,15 @@ export type XsuaaServiceCredentials = ServiceCredentials & { verificationkey: string; xsappname: string; }; + +/** + * Credentials for the Identity Authentication Service (IAS). + * Matches the type definition from @sap/xssec. + * @internal + */ +export type IdentityServiceCredentials = ServiceCredentials & { + /** + * Application tenant ID. Can be used to override the tenant context. + */ + app_tid?: string; +}; diff --git a/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts b/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts new file mode 100644 index 0000000000..978ca8d9c9 --- /dev/null +++ b/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts @@ -0,0 +1,56 @@ +import { + clearIdentityServices, + getIdentityServiceInstanceFromCredentials +} from './ias'; +import type { ServiceCredentials } from './environment-accessor-types'; + +describe('ias', () => { + describe('getIdentityServiceInstanceFromCredentials()', () => { + afterEach(() => { + clearIdentityServices(); + }); + + it('creates a new service instance', () => { + expect( + getIdentityServiceInstanceFromCredentials(createServiceCredentials()) + ).toBeDefined(); + }); + + it('retrieves the same service instance for the same credentials', () => { + expect( + getIdentityServiceInstanceFromCredentials(createServiceCredentials()) + ).toBe( + getIdentityServiceInstanceFromCredentials(createServiceCredentials()) + ); + }); + + it('retrieves different service instances for the different credentials', () => { + expect( + getIdentityServiceInstanceFromCredentials(createServiceCredentials()) + ).not.toBe( + getIdentityServiceInstanceFromCredentials( + createServiceCredentials('another-clientid') + ) + ); + }); + + it('retrieves different service instances for the same credentials, but different caching behavior', () => { + expect( + getIdentityServiceInstanceFromCredentials(createServiceCredentials()) + ).not.toBe( + getIdentityServiceInstanceFromCredentials( + createServiceCredentials(), + true + ) + ); + }); + }); +}); + +function createServiceCredentials(clientid = 'clientid'): ServiceCredentials { + return { + clientid, + clientsecret: 'clientsecret', + url: 'https://tenant.accounts.ondemand.com' + } as unknown as ServiceCredentials; +} diff --git a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts new file mode 100644 index 0000000000..3a8fbb60ba --- /dev/null +++ b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts @@ -0,0 +1,48 @@ +import { IdentityService } from '@sap/xssec'; +import type { + ServiceCredentials, + IdentityServiceCredentials +} from './environment-accessor-types'; + +const identityServices: Record = {}; + +/** + * @internal + * Clears the cache of Identity services. + * Should only be used for testing purposes. + */ +export function clearIdentityServices(): void { + Object.keys(identityServices).forEach(key => delete identityServices[key]); +} + +/** + * @internal + * @param credentials - Identity service credentials extracted from a service binding or re-use service. Required to create the xssec IdentityService instance. + * @param disableCache - Value to enable or disable JWKS cache in xssec library. Defaults to false. + * @returns An instance of {@code @sap/xssec/IdentityService} for the provided credentials. + */ +export function getIdentityServiceInstanceFromCredentials( + credentials: ServiceCredentials, + disableCache: boolean = false +): IdentityService { + const serviceConfig = disableCache + ? { + validation: { + jwks: { + expirationTime: 0, + refreshPeriod: 0 + } + } + } + : undefined; + + const cacheKey = `${credentials.clientid}:${disableCache}`; + + if (!identityServices[cacheKey]) { + identityServices[cacheKey] = new IdentityService( + credentials as IdentityServiceCredentials, + serviceConfig as any + ); + } + return identityServices[cacheKey]; +} diff --git a/packages/connectivity/src/scp-cf/environment-accessor/index.ts b/packages/connectivity/src/scp-cf/environment-accessor/index.ts index 1000e5d362..2bb278309a 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/index.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/index.ts @@ -3,3 +3,4 @@ export * from './service-bindings'; export * from './environment-accessor-types'; export * from './service-credentials'; export * from './xsuaa'; +export * from './ias'; diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 7965883398..b4919bd9b1 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -1,13 +1,20 @@ -import axios from 'axios'; import { signedJwt } from '../../../../test-resources/test/test-util'; import { getIasClientCredentialsToken, - shouldExchangeToken + shouldExchangeToken, + clearIdentityServices } from './identity-service'; import type { Service } from './environment-accessor'; -jest.mock('axios'); -const mockedAxios = axios as jest.Mocked; +const mockFetchClientCredentialsToken = jest.fn(); +const mockFetchJwtBearerToken = jest.fn(); + +jest.mock('@sap/xssec', () => ({ + IdentityService: jest.fn().mockImplementation(() => ({ + fetchClientCredentialsToken: mockFetchClientCredentialsToken, + fetchJwtBearerToken: mockFetchJwtBearerToken + })) +})); describe('shouldExchangeToken', () => { it('should not exchange token from XSUAA', async () => { @@ -18,9 +25,12 @@ describe('shouldExchangeToken', () => { ).toBe(false); }); - it('should exchange non-XSUAA token', async () => { + it('should exchange IAS token', async () => { expect( - shouldExchangeToken({ iasToXsuaaTokenExchange: true, jwt: signedJwt({}) }) + shouldExchangeToken({ + iasToXsuaaTokenExchange: true, + jwt: signedJwt({ iss: 'https://tenant.accounts.ondemand.com' }) + }) ).toBe(true); }); @@ -61,39 +71,35 @@ describe('getIasClientCredentialsToken', () => { }; const mockTokenResponse = { - access_token: 'mock-ias-access-token', + access_token: signedJwt({ + jti: 'mock-jti', + aud: 'test-audience', + ias_apis: ['dummy'] + }), token_type: 'Bearer', - expires_in: 3600, - scope: 'openid', - jti: 'mock-jti' + expires_in: 3600 }; beforeEach(() => { jest.clearAllMocks(); + clearIdentityServices(); }); it('fetches IAS token with mTLS authentication', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); const result = await getIasClientCredentialsToken(mockIasService); - expect(result).toEqual(mockTokenResponse); - expect(mockedAxios.request).toHaveBeenCalledWith( - expect.objectContaining({ - method: 'post', - url: 'https://tenant.accounts.ondemand.com/oauth2/token', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - }, - httpsAgent: expect.any(Object) - }) - ); - // Verify the data contains required parameters (order may vary) - const callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).toContain('grant_type=client_credentials'); - expect(callData).toContain('client_id=test-client-id'); - expect(callData).toContain('token_format=jwt'); - expect(callData).not.toContain('refresh_token=0'); // Should not appear for technical users + expect(result).toEqual({ + access_token: mockTokenResponse.access_token, + token_type: mockTokenResponse.token_type, + expires_in: mockTokenResponse.expires_in, + scope: '', + jti: 'mock-jti', + aud: 'test-audience', + ias_apis: ['dummy'] + }); + expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({}); }); it('fetches IAS token with client secret authentication', async () => { @@ -106,142 +112,76 @@ describe('getIasClientCredentialsToken', () => { } }; - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); const result = await getIasClientCredentialsToken(serviceWithSecret); - expect(result).toEqual(mockTokenResponse); - expect(mockedAxios.request).toHaveBeenCalledWith( - expect.objectContaining({ - method: 'post', - url: 'https://tenant.accounts.ondemand.com/oauth2/token', - headers: expect.objectContaining({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - }) - ); - // Verify the data contains required parameters (order may vary) - const callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).toContain('grant_type=client_credentials'); - expect(callData).toContain('client_id=test-client-id'); - expect(callData).toContain('token_format=jwt'); - expect(callData).toContain('client_secret=test-client-secret'); - expect(callData).not.toContain('refresh_token=0'); // Should not appear for technical users + expect(result).toEqual({ + access_token: mockTokenResponse.access_token, + token_type: mockTokenResponse.token_type, + expires_in: mockTokenResponse.expires_in, + scope: '', + jti: 'mock-jti', + aud: 'test-audience', + ias_apis: ['dummy'] + }); + expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({}); }); it('includes resource parameter for app2app flow', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); await getIasClientCredentialsToken(mockIasService, { resource: { name: 'my-app' } }); - const callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).toContain('grant_type=client_credentials'); - expect(callData).toContain( - 'resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app' - ); - expect(callData).not.toContain('refresh_token=0'); + expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ + resource: 'urn:sap:identity:application:provider:name:my-app' + }); }); - it('includes appTenantId parameter for multi-tenant scenarios', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + it('includes appTid parameter for multi-tenant scenarios', async () => { + mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); await getIasClientCredentialsToken(mockIasService, { - appTenantId: 'tenant-123' + appTid: 'tenant-123' }); - const callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).toContain('grant_type=client_credentials'); - expect(callData).toContain('app_tid=tenant-123'); - expect(callData).not.toContain('refresh_token=0'); + expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ + app_tid: 'tenant-123' + }); }); - it('includes both appName and appTenantId parameters', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + it('includes both appName and appTid parameters', async () => { + mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); await getIasClientCredentialsToken(mockIasService, { resource: { name: 'my-app' }, - appTenantId: 'tenant-123' + appTid: 'tenant-123' }); - const callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).toContain('grant_type=client_credentials'); - expect(callData).toContain( - 'resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app' - ); - expect(callData).toContain('app_tid=tenant-123'); - expect(callData).not.toContain('refresh_token=0'); + expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ + resource: 'urn:sap:identity:application:provider:name:my-app', + app_tid: 'tenant-123' + }); }); it('includes extraParams for additional OAuth2 parameters', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); await getIasClientCredentialsToken(mockIasService, { - extraParams: { - custom_param: 'custom_value', - another_param: 'another_value' - } + extraParams: { custom_param: 'custom_value' } }); - expect(mockedAxios.request).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.stringContaining('custom_param=custom_value') - }) - ); - expect(mockedAxios.request).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.stringContaining('another_param=another_value') - }) - ); - }); - - it('throws error when url is missing', async () => { - const invalidService: Service = { - ...mockIasService, - credentials: { - clientid: 'test-client-id', - certificate: 'cert', - key: 'key' - } as any - }; - - await expect(getIasClientCredentialsToken(invalidService)).rejects.toThrow( - 'IAS credentials must contain "url" and "clientid"' - ); - }); - - it('throws error when clientid is missing', async () => { - const invalidService: Service = { - ...mockIasService, - credentials: { - url: 'https://tenant.accounts.ondemand.com', - certificate: 'cert', - key: 'key' - } as any - }; - - await expect(getIasClientCredentialsToken(invalidService)).rejects.toThrow( - 'IAS credentials must contain "url" and "clientid"' - ); - }); - - it('throws error when neither certificate/key nor clientsecret is provided', async () => { - const invalidService: Service = { - ...mockIasService, - credentials: { - url: 'https://tenant.accounts.ondemand.com', - clientid: 'test-client-id' - } as any - }; - - await expect(getIasClientCredentialsToken(invalidService)).rejects.toThrow( - 'IAS credentials must contain either "certificate" and "key" for mTLS, or "clientsecret" for client secret authentication' - ); + expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ + custom_param: 'custom_value' + }); }); it('handles token fetch errors gracefully', async () => { - mockedAxios.request.mockRejectedValue(new Error('Network error')); + mockFetchClientCredentialsToken.mockRejectedValue( + new Error('Network error') + ); await expect(getIasClientCredentialsToken(mockIasService)).rejects.toThrow( 'Could not fetch IAS client credentials token for service of type identity' @@ -250,35 +190,31 @@ describe('getIasClientCredentialsToken', () => { describe('authenticationType parameter', () => { it('uses OAuth2ClientCredentials (technical-user) by default', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); await getIasClientCredentialsToken(mockIasService, {}); - const callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).toContain('grant_type=client_credentials'); - expect(callData).not.toContain('assertion='); - expect(callData).not.toContain('refresh_token=0'); + expect(mockFetchClientCredentialsToken).toHaveBeenCalled(); + expect(mockFetchJwtBearerToken).not.toHaveBeenCalled(); }); it('uses client credentials for OAuth2ClientCredentials', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); await getIasClientCredentialsToken(mockIasService, { authenticationType: 'OAuth2ClientCredentials' }); - const callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).toContain('grant_type=client_credentials'); - expect(callData).not.toContain('assertion='); - expect(callData).not.toContain('refresh_token=0'); + expect(mockFetchClientCredentialsToken).toHaveBeenCalled(); + expect(mockFetchJwtBearerToken).not.toHaveBeenCalled(); }); it('uses JWT bearer grant for OAuth2JWTBearer (business-user) with assertion', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + mockFetchJwtBearerToken.mockResolvedValue(mockTokenResponse); const userAssertion = signedJwt({ - user_uuid: 'test-user', - app_tid: 'test-tenant' + user_uuid: 'user-123', + app_tid: 'tenant-456' }); await getIasClientCredentialsToken(mockIasService, { @@ -286,14 +222,8 @@ describe('getIasClientCredentialsToken', () => { assertion: userAssertion }); - const callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).toContain( - 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer' - ); - expect(callData).toContain( - `assertion=${encodeURIComponent(userAssertion)}` - ); - expect(callData).toContain('refresh_token=0'); // Workaround applied for business users + expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(userAssertion, {}); + expect(mockFetchClientCredentialsToken).not.toHaveBeenCalled(); }); it('throws error for OAuth2JWTBearer (business-user) without assertion', async () => { @@ -306,58 +236,26 @@ describe('getIasClientCredentialsToken', () => { ); }); - it('includes refresh_token workaround only for OAuth2ClientCredentials (technical-user)', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); - - // Technical user - no refresh_token - await getIasClientCredentialsToken(mockIasService, { - authenticationType: 'OAuth2ClientCredentials' - }); - let callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).not.toContain('refresh_token=0'); - - jest.clearAllMocks(); - - // Business user - has refresh_token - const userAssertion = signedJwt({ - user_uuid: 'test-user', - app_tid: 'test-tenant' - }); - await getIasClientCredentialsToken(mockIasService, { - authenticationType: 'OAuth2JWTBearer', - assertion: userAssertion - }); - callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).toContain('refresh_token=0'); - }); - - it('supports OAuth2JWTBearer (business-user) with resource and appTenantId', async () => { - mockedAxios.request.mockResolvedValue({ data: mockTokenResponse }); + it('supports OAuth2JWTBearer (business-user) with resource and appTid', async () => { + mockFetchJwtBearerToken.mockResolvedValue(mockTokenResponse); const userAssertion = signedJwt({ - user_uuid: 'test-user', - app_tid: 'test-tenant' + user_uuid: 'user-123', + app_tid: 'tenant-456' }); await getIasClientCredentialsToken(mockIasService, { authenticationType: 'OAuth2JWTBearer', assertion: userAssertion, resource: { name: 'my-app' }, - appTenantId: 'tenant-123' + appTid: 'tenant-123' }); - const callData = mockedAxios.request.mock.calls[0][0].data; - expect(callData).toContain( - 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer' - ); - expect(callData).toContain( - `assertion=${encodeURIComponent(userAssertion)}` - ); - expect(callData).toContain( - 'resource=urn%3Asap%3Aidentity%3Aapplication%3Aprovider%3Aname%3Amy-app' - ); - expect(callData).toContain('app_tid=tenant-123'); - expect(callData).toContain('refresh_token=0'); + expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(userAssertion, { + resource: 'urn:sap:identity:application:provider:name:my-app', + app_tid: 'tenant-123', + refresh_token: '0' + }); }); }); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index c2b7ea5619..933305b2b3 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -1,23 +1,38 @@ -import https from 'https'; import { executeWithMiddleware } from '@sap-cloud-sdk/resilience/internal'; import { resilience } from '@sap-cloud-sdk/resilience'; -import { createLogger } from '@sap-cloud-sdk/util'; -import axios from 'axios'; -import { decodeJwt, isXsuaaToken } from './jwt'; -import { resolveServiceBinding } from './environment-accessor'; +import { decodeJwt, isIasToken } from './jwt'; +import { + resolveServiceBinding, + getIdentityServiceInstanceFromCredentials +} from './environment-accessor'; +import type { IdentityService } from '@sap/xssec'; import type { DestinationOptions, - ServiceBindingTransformOptions + IasOptions, + IasResource } from './destination'; import type { MiddlewareContext } from '@sap-cloud-sdk/resilience'; import type { Service, ServiceCredentials } from './environment-accessor'; -import type { RawAxiosRequestConfig } from 'axios'; import type { ClientCredentialsResponse } from './xsuaa-service-types'; -const logger = createLogger({ - package: 'connectivity', - messageContext: 'identity-service' -}); +export { clearIdentityServices } from './environment-accessor'; + +/** + * @internal + * Represents the response to an IAS client credentials request. + * Extends the XSUAA response with IAS-specific fields. + */ +export interface IasClientCredentialsResponse + extends ClientCredentialsResponse { + /** + * Audience claim from the JWT token. + */ + aud?: string | string[]; + /** + * IAS API resources. Present when resource parameter is specified in the token request. + */ + ias_apis?: string[]; +} /** * @internal @@ -30,28 +45,40 @@ export function shouldExchangeToken(options: DestinationOptions): boolean { return ( options.iasToXsuaaTokenExchange === true && !!options.jwt && - !isXsuaaToken(decodeJwt(options.jwt)) + isIasToken(decodeJwt(options.jwt)) ); } type IasParameters = { serviceCredentials: ServiceCredentials; -} & ServiceBindingTransformOptions['iasOptions']; +} & IasOptions; + +type FirstArg = T extends (arg1: infer U, ...args: any[]) => any ? U : never; +type UnwrapPromise = T extends Promise ? U : T; + +// xssec does not properly export these +type IdTokenFetchOptions = FirstArg< + InstanceType['fetchClientCredentialsToken'] +>; +type TokenFetchResponse = UnwrapPromise< + ReturnType< + InstanceType['fetchClientCredentialsToken'] + > +>; /** * Make a client credentials request against the IAS OAuth2 endpoint. * Supports both certificate-based (mTLS) and client secret authentication. * @param service - Service as it is defined in the environment variable. - * @param options - Options for token fetching, including authenticationType to specify authentication mode, optional resource parameter for app2app, appTenantId for multi-tenant scenarios, and extraParams for additional OAuth2 parameters. + * @param options - Options for token fetching, including authenticationType to specify authentication mode, optional resource parameter for app2app, appTid for multi-tenant scenarios, and extraParams for additional OAuth2 parameters. * @returns Client credentials token response. * @internal */ export async function getIasClientCredentialsToken( service: string | Service, - options: ServiceBindingTransformOptions['iasOptions'] = {} -): Promise { + options: IasOptions = {} +): Promise { const resolvedService = resolveServiceBinding(service); - const { clientid } = resolvedService.credentials; const fnArgument: IasParameters = { serviceCredentials: resolvedService.credentials, @@ -60,7 +87,7 @@ export async function getIasClientCredentialsToken( const token = await executeWithMiddleware< IasParameters, - ClientCredentialsResponse, + IasClientCredentialsResponse, MiddlewareContext >(resilience(), { fn: getIasClientCredentialsTokenImpl, @@ -78,31 +105,62 @@ export async function getIasClientCredentialsToken( } /** - * Implementation of the IAS client credentials token retrieval. + * Converts an IasResource to the URN format expected by @sap/xssec. + * @param resource - The IAS resource to convert. + * @returns The resource in URN format. + * @internal + */ +function convertResourceToUrn(resource: IasResource): string { + if (!resource) { + throw new Error('Resource parameter is required'); + } + + if ('name' in resource) { + return `urn:sap:identity:application:provider:name:${resource.name}`; + } + + let urn = `urn:sap:identity:application:provider:clientid:${resource.clientId}`; + if (resource.tenantId) { + urn += `:tenantid:${resource.tenantId}`; + } + return urn; +} + +/** + * Implementation of the IAS client credentials token retrieval using @sap/xssec. * @param arg - The parameters for IAS token retrieval. * @returns A promise resolving to the client credentials response. * @internal */ async function getIasClientCredentialsTokenImpl( arg: IasParameters -): Promise { - const { url, clientid, certificate, key, clientsecret } = - arg.serviceCredentials; +): Promise { + const identityService = getIdentityServiceInstanceFromCredentials( + arg.serviceCredentials + ); - if (!url || !clientid) { - throw new Error( - 'IAS credentials must contain "url" and "clientid" properties.' - ); + const authenticationType = + arg.authenticationType || 'OAuth2ClientCredentials'; + + const tokenOptions: IdTokenFetchOptions = {}; + + // Stringify resource(s) + if (arg.resource) { + tokenOptions.resource = Array.isArray(arg.resource) + ? arg.resource.map(convertResourceToUrn) + : convertResourceToUrn(arg.resource); } - // Build form data - const params = new URLSearchParams({ - client_id: clientid - }); + if (arg.appTid) { + tokenOptions.app_tid = arg.appTid; + } - // Determine grant type based on authenticationType parameter - const authenticationType = - arg.authenticationType || 'OAuth2ClientCredentials'; + // Add any extra parameters + if (arg.extraParams) { + Object.assign(tokenOptions, arg.extraParams); + } + + let response: undefined | TokenFetchResponse; if (authenticationType === 'OAuth2JWTBearer') { // JWT bearer grant for business user propagation @@ -111,78 +169,33 @@ async function getIasClientCredentialsTokenImpl( 'JWT assertion required for authenticationType: "OAuth2JWTBearer". Provide iasOptions.assertion.' ); } - params.append('assertion', arg.assertion); - params.append('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer'); - // Workaround for an IAS issue - params.append('refresh_token', '0'); - } else { - // Client credentials for technical users - params.append('grant_type', 'client_credentials'); - } - if (arg.resource) { - let fullResource = ''; - if ('name' in arg.resource) { - fullResource = `urn:sap:identity:application:provider:name:${arg.resource.name}`; - } else { - fullResource = `urn:sap:identity:application:provider:clientid:${arg.resource.clientId}`; - if (arg.resource.tenantId) { - fullResource += `:tenantid:${arg.resource.tenantId}`; - } + // Workaround for IAS bug + // https://github.com/SAP/cloud-sdk-java/blob/61903347b607a8397f7930709cd52526f05269b1/cloudplatform/connectivity-oauth/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/OAuth2Service.java#L225-L236 + if (arg.appTid) { + (tokenOptions as any).refresh_token = '0'; } - params.append('resource', fullResource); - logger.debug(`Fetching IAS token with resource parameter: ${fullResource}`); - } - - if (arg.appTenantId) { - params.append('app_tid', arg.appTenantId); - logger.debug( - `Fetching IAS token with app_tid parameter: ${arg.appTenantId}` + response = await identityService.fetchJwtBearerToken( + arg.assertion, + tokenOptions ); - } - - // Ensure JWT token format, not mandatory but we expect JWTs - // and the docs mention in some cases we may get opaque tokens otherwise - params.append('token_format', 'jwt'); - - const tokenUrl = `${url}/oauth2/token`; - const headers: Record = { - 'Content-Type': 'application/x-www-form-urlencoded' - }; - - const requestConfig: RawAxiosRequestConfig = { - method: 'post', - url: tokenUrl, - headers - }; - - // Determine authentication method - if (certificate && key) { - // mTLS authentication - logger.debug('Using certificate-based authentication for IAS token.'); - requestConfig.httpsAgent = new https.Agent({ - cert: certificate, - key - }); - } else if (clientsecret) { - logger.debug('Using client secret authentication for IAS token.'); - params.append('client_secret', clientsecret); } else { - throw new Error( - 'IAS credentials must contain either "certificate" and "key" for mTLS, or "clientsecret" for client secret authentication.' - ); - } - - if (arg.extraParams) { - for (const [paramKey, paramValue] of Object.entries(arg.extraParams)) { - params.append(paramKey, paramValue); - } + // Client credentials for technical users + response = await identityService.fetchClientCredentialsToken(tokenOptions); } - requestConfig.data = params.toString(); - - const response = - await axios.request(requestConfig); - return response.data; + const decodedJwt = decodeJwt(response.access_token); + + return { + access_token: response.access_token, + token_type: response.token_type, + expires_in: response.expires_in, + // IAS tokens don't have scope property + scope: '', + jti: decodedJwt.jti ?? '', + aud: decodedJwt.aud, + // Added if resource parameter was specified + ias_apis: decodedJwt?.ias_apis + }; } diff --git a/packages/connectivity/src/scp-cf/jwt/jwt.ts b/packages/connectivity/src/scp-cf/jwt/jwt.ts index 43cd8f5024..d59170c52b 100644 --- a/packages/connectivity/src/scp-cf/jwt/jwt.ts +++ b/packages/connectivity/src/scp-cf/jwt/jwt.ts @@ -68,18 +68,30 @@ export function getTenantId( return decodedJwt.zid || decodedJwt.app_tid || undefined; } +/** + * Check if the given JWT is an IAS token. + * Currently, there are only two domains for IAS tokens: + * `accounts.ondemand.com` and `accounts400.ondemand.com`. + * @param decodedJwt - The decoded JWT to check. + * @returns Whether the given JWT is an IAS token. + * @internal + */ +export function isIasToken(decodedJwt: JwtPayload): boolean { + return !!( + decodedJwt.iss?.includes('accounts.ondemand.com') || + decodedJwt.iss?.includes('accounts400.ondemand.com') + ); +} + /** * Check if the given JWT is not an IAS token. * Currently, there are only two domains for IAS tokens: - * `accounts.ondemand.com` and `accounts400.onemand.com`. + * `accounts.ondemand.com` and `accounts400.ondemand.com`. * @param decodedJwt - The decoded JWT to check. * @returns Whether the given JWT is not an IAS token. */ function isNotIasToken(decodedJwt: JwtPayload): boolean { - return ( - !decodedJwt.iss?.includes('accounts.ondemand.com') && - !decodedJwt.iss?.includes('accounts400.ondemand.com') - ); + return !isIasToken(decodedJwt); } /** diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index 48bc1abe11..2c1789bbb0 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -88,12 +88,12 @@ export async function serviceToken( * * This function automatically detects the service type (XSUAA or IAS) based on the label * and uses the appropriate authentication flow. - * For IAS services, you can pass IAS-specific options like `resource` and `appTenantId`. + * For IAS services, you can pass IAS-specific options like `resource` and `appTid`. * * Throws an error if there is no instance of the given service type. * @param jwt - The JWT of the user for whom the access token should be fetched. * @param service - The type of the service or an instance of {@link Service}. - * @param options - Optional IAS-specific options like resource, appTenantId, and caching behavior. Only used for IAS services. + * @param options - Optional IAS-specific options like resource, appTid, and caching behavior. Only used for IAS services. * @returns A JWT bearer token. */ export async function jwtBearerToken( From e71c2685ff87964070e3caf63ab3bd49d303781b Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 16 Dec 2025 12:23:34 +0100 Subject: [PATCH 21/58] make codeql happy --- packages/connectivity/src/scp-cf/jwt/jwt.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/connectivity/src/scp-cf/jwt/jwt.ts b/packages/connectivity/src/scp-cf/jwt/jwt.ts index d59170c52b..67d55bec81 100644 --- a/packages/connectivity/src/scp-cf/jwt/jwt.ts +++ b/packages/connectivity/src/scp-cf/jwt/jwt.ts @@ -77,10 +77,19 @@ export function getTenantId( * @internal */ export function isIasToken(decodedJwt: JwtPayload): boolean { - return !!( - decodedJwt.iss?.includes('accounts.ondemand.com') || - decodedJwt.iss?.includes('accounts400.ondemand.com') - ); + if (!decodedJwt.iss) { + return false; + } + try { + const issUrl = new URL(decodedJwt.iss); + const hostname = issUrl.hostname.toLowerCase(); + return ( + hostname.endsWith('.accounts.ondemand.com') || + hostname.endsWith('.accounts400.ondemand.com') + ); + } catch { + return false; + } } /** From 4f925f5fd67fcc1887c37dbcea2579567f5b4ddb Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 16 Dec 2025 15:06:24 +0100 Subject: [PATCH 22/58] handle multi-resource caching --- .../client-credentials-token-cache.spec.ts | 145 ++++++++++++++++++ .../scp-cf/client-credentials-token-cache.ts | 33 ++-- 2 files changed, 166 insertions(+), 12 deletions(-) diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts index 206c7c088b..a052e03a8d 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts @@ -249,4 +249,149 @@ describe('ClientCredentialsTokenCache', () => { expect(key).toBe('tenant-123:client-id'); }); }); + + describe('Multiple resources (array) support', () => { + const validToken = { + access_token: 'multi-resource-token', + token_type: 'Bearer', + expires_in: oneHourInSeconds * 3, + jti: '', + scope: '' + }; + + beforeEach(() => { + clientCredentialsTokenCache.clear(); + }); + + it('should cache and retrieve token with array of resource clientIds', () => { + const resources = [ + { clientId: 'client-1', tenantId: 'tenant-1' }, + { clientId: 'client-2' }, + { name: 'app3' } + ]; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resources, + validToken + ); + + const cached = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resources + ); + + expect(cached).toEqual(validToken); + }); + it('should treat resource array as a set (order-independent)', () => { + const resources1 = [{ name: 'app-1' }, { name: 'app-2' }]; + const resources2 = [{ name: 'app-2' }, { name: 'app-1' }]; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resources1, + validToken + ); + + const cached1 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resources1 + ); + const cached2 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resources2 + ); + + expect(cached1).toEqual(validToken); + expect(cached2).toEqual(validToken); + }); + + it('should isolate cache by number of resources in array', () => { + const resources1 = [{ name: 'app-1' }]; + const resources2 = [{ name: 'app-1' }, { name: 'app-2' }]; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resources1, + validToken + ); + + const cached1 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resources1 + ); + const cached2 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resources2 + ); + + expect(cached1).toEqual(validToken); + expect(cached2).toBeUndefined(); + }); + + it('should not treat single resource and single-element array differently', () => { + const singleResource = { name: 'app-1' }; + const arrayResource = [{ name: 'app-1' }]; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + singleResource, + validToken + ); + + const cachedSingle = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + singleResource + ); + const cachedArray = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + arrayResource + ); + + expect(cachedSingle).toEqual(validToken); + expect(cachedArray).toEqual(validToken); + }); + + it('should generate correct cache key with array of resource names', () => { + const key = getCacheKey('tenant-123', 'client-id', [ + { name: 'app-1' }, + { name: 'app-2' } + ]); + expect(key).toBe('tenant-123:client-id:name=app-1::name=app-2'); + }); + + it('should generate correct cache key with array of resource clientIds', () => { + const key = getCacheKey('tenant-123', 'client-id', [ + { clientId: 'client-1' }, + { clientId: 'client-2', tenantId: 'tenant-2' } + ]); + expect(key).toBe( + 'tenant-123:client-id:clientid=client-1::clientid=client-2:tenantid=tenant-2' + ); + }); + + it('should generate correct cache key with array of mixed resource types', () => { + const key = getCacheKey('tenant-123', 'client-id', [ + { name: 'app-1' }, + { clientId: 'client-2' } + ]); + expect(key).toBe('tenant-123:client-id:clientid=client-2::name=app-1'); + }); + + it('should handle empty resource array', () => { + const key = getCacheKey('tenant-123', 'client-id', []); + expect(key).toBe('tenant-123:client-id'); + }); + }); }); diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts index 7ec34e36f3..5427d987ed 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts @@ -14,14 +14,14 @@ const ClientCredentialsTokenCache = ( getToken: ( tenantId: string | undefined, clientId: string, - resource?: IasResource + resource?: IasResource | IasResource[] ): ClientCredentialsResponse | undefined => cache.get(getCacheKey(tenantId, clientId, resource)), cacheToken: ( tenantId: string | undefined, clientId: string, - resource: IasResource | undefined, + resource: IasResource | IasResource[] | undefined, token: ClientCredentialsResponse ): void => { cache.set(getCacheKey(tenantId, clientId, resource), { @@ -43,20 +43,29 @@ const ClientCredentialsTokenCache = ( * @returns Normalized resource string or empty string if not provided. * @internal */ -function normalizeResource(resource?: IasResource): string | undefined { +function normalizeResource( + resource?: IasResource | IasResource[] +): string | undefined { if (!resource) { return undefined; } - if ('name' in resource) { - return `name=${resource.name}`; - } + const resources = Array.isArray(resource) ? resource : [resource]; - let normalized = `clientid=${resource.clientId}`; - if (resource.tenantId) { - normalized += `:tenantid=${resource.tenantId}`; - } - return normalized; + return resources + .map(r => { + if ('name' in r) { + return `name=${r.name}`; + } + + let normalized = `clientid=${r.clientId}`; + if (r.tenantId) { + normalized += `:tenantid=${r.tenantId}`; + } + return normalized; + }) + .sort() + .join('::'); } /** * @@ -69,7 +78,7 @@ function normalizeResource(resource?: IasResource): string | undefined { export function getCacheKey( tenantId: string | undefined, clientId: string, - resource?: IasResource + resource?: IasResource | IasResource[] ): string | undefined { if (!tenantId) { logger.warn( From 41570942e9780d786dd27052e579d61034d856ce Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 16 Dec 2025 17:23:51 +0100 Subject: [PATCH 23/58] allow listing multiple IasResource in Service --- .../scp-cf/environment-accessor/environment-accessor-types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts b/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts index 10ad8dda49..e7dc99a2a2 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts @@ -24,7 +24,7 @@ export interface Service { /** * IAS Resource to request for App-to-App communication. */ - iasResource?: IasResource; + iasResource?: IasResource | IasResource[]; } /** From 41dde3cf014d89b2fbddc25935dbc9a13476eaeb Mon Sep 17 00:00:00 2001 From: David Knaack Date: Wed, 17 Dec 2025 09:26:18 +0100 Subject: [PATCH 24/58] remove iasResource from Service interface --- .../environment-accessor/environment-accessor-types.ts | 6 ------ .../connectivity/src/scp-cf/environment-accessor/ias.ts | 2 +- packages/connectivity/src/scp-cf/token-accessor.ts | 9 +++++---- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts b/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts index e7dc99a2a2..acc21a1aee 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/environment-accessor-types.ts @@ -1,5 +1,3 @@ -import type { IasResource } from '../destination'; - /** * Unspecific representation of a service as read from VCAP_SERVICES (for Cloud Foundry) or mounted secrets (for K8S). */ @@ -21,10 +19,6 @@ export interface Service { * The service credentials. */ credentials: ServiceCredentials; - /** - * IAS Resource to request for App-to-App communication. - */ - iasResource?: IasResource | IasResource[]; } /** diff --git a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts index 3a8fbb60ba..858fb02b94 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts @@ -41,7 +41,7 @@ export function getIdentityServiceInstanceFromCredentials( if (!identityServices[cacheKey]) { identityServices[cacheKey] = new IdentityService( credentials as IdentityServiceCredentials, - serviceConfig as any + serviceConfig ); } return identityServices[cacheKey]; diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index 2c1789bbb0..c711be96f7 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -27,9 +27,10 @@ import type { IasOptions } from './destination'; */ export async function serviceToken( service: string | Service, - options?: CachingOptions & { - jwt?: string | JwtPayload; - } + options?: CachingOptions & + Omit & { + jwt?: string | JwtPayload; + } ): Promise { const opts = { useCache: true, @@ -42,7 +43,7 @@ export async function serviceToken( const tenantForCaching = options?.jwt ? getTenantId(options.jwt) || getSubdomain(options.jwt) : getTenantIdFromBinding() || getDefaultTenantId(); - const resourceForCaching = serviceBinding?.iasResource; + const resourceForCaching = options?.resource; if (opts.useCache) { const cachedToken = clientCredentialsTokenCache.getToken( From a3a8b5e83306a137292cc9286c09162e791cf788 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Wed, 17 Dec 2025 10:08:27 +0100 Subject: [PATCH 25/58] move IAS-specific options for serviceToken & jwtBearerToken to iasOptions --- .../connectivity/src/scp-cf/token-accessor.ts | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index c711be96f7..0ba410519f 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -27,10 +27,13 @@ import type { IasOptions } from './destination'; */ export async function serviceToken( service: string | Service, - options?: CachingOptions & - Omit & { - jwt?: string | JwtPayload; - } + options?: CachingOptions & { + jwt?: string | JwtPayload; + iasOptions?: Omit< + IasOptions, + 'authenticationType' | 'assertion' | 'destinationUrl' + >; + } ): Promise { const opts = { useCache: true, @@ -43,7 +46,7 @@ export async function serviceToken( const tenantForCaching = options?.jwt ? getTenantId(options.jwt) || getSubdomain(options.jwt) : getTenantIdFromBinding() || getDefaultTenantId(); - const resourceForCaching = options?.resource; + const resourceForCaching = options?.iasOptions?.resource; if (opts.useCache) { const cachedToken = clientCredentialsTokenCache.getToken( @@ -94,16 +97,19 @@ export async function serviceToken( * Throws an error if there is no instance of the given service type. * @param jwt - The JWT of the user for whom the access token should be fetched. * @param service - The type of the service or an instance of {@link Service}. - * @param options - Optional IAS-specific options like resource, appTid, and caching behavior. Only used for IAS services. + * @param options - Optional options to modify token fetching behaviour. + * @param options.iasOptions - Options to change IAS token fetching (see {@link IasOptions}). * @returns A JWT bearer token. */ export async function jwtBearerToken( jwt: string, service: string | Service, - options?: Omit< - IasOptions, - 'authenticationType' | 'assertion' | 'destinationUrl' - > + options?: { + iasOptions?: Omit< + IasOptions, + 'authenticationType' | 'assertion' | 'destinationUrl' + >; + } ): Promise { const resolvedService = resolveServiceBinding(service); From 7c166fe735f274dde269f76621cbfe8a45feb00f Mon Sep 17 00:00:00 2001 From: David Knaack Date: Wed, 17 Dec 2025 10:38:13 +0100 Subject: [PATCH 26/58] clean up commnets --- packages/connectivity/src/scp-cf/token-accessor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index 0ba410519f..49cb2cfce3 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -23,6 +23,7 @@ import type { IasOptions } from './destination'; * Throws an error if there is no instance of the given service type or the XSUAA service, or if the request to the XSUAA service fails. * @param service - The type of the service or an instance of {@link Service}. * @param options - Options to influence caching behavior (see {@link CachingOptions}) and a JWT. By default, caching and usage of a circuit breaker are enabled. + * @param options.iasOptions - Options to change IAS token fetching (see {@link IasOptions}). * @returns Access token. */ export async function serviceToken( @@ -92,7 +93,7 @@ export async function serviceToken( * * This function automatically detects the service type (XSUAA or IAS) based on the label * and uses the appropriate authentication flow. - * For IAS services, you can pass IAS-specific options like `resource` and `appTid`. + * For IAS services, you can pass IAS-specific options like `resource` and `appTid` in `options.iasOptions`. * * Throws an error if there is no instance of the given service type. * @param jwt - The JWT of the user for whom the access token should be fetched. From 714623cfa80498229a91445844883faa800521d9 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Wed, 17 Dec 2025 12:26:11 +0100 Subject: [PATCH 27/58] fix ias option forwarding --- packages/connectivity/src/scp-cf/token-accessor.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index 49cb2cfce3..9338470515 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -64,8 +64,8 @@ export async function serviceToken( const token = serviceBinding.label === 'identity' ? await getIasClientCredentialsToken(serviceBinding, { - resource: resourceForCaching, - ...options + ...(options?.iasOptions ?? {}), + authenticationType: 'OAuth2ClientCredentials' }) : await getClientCredentialsToken(serviceBinding, options?.jwt); @@ -118,7 +118,7 @@ export async function jwtBearerToken( if (resolvedService.label === 'identity') { return ( await getIasClientCredentialsToken(resolvedService, { - ...options, + ...(options?.iasOptions ?? {}), authenticationType: 'OAuth2JWTBearer', assertion: jwt }) From 5fdc97e1e77a4097c80ac18092ae6fa51c0fb808 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Wed, 17 Dec 2025 17:09:42 +0100 Subject: [PATCH 28/58] forward resolved tenantId in serviceToken --- packages/connectivity/src/scp-cf/token-accessor.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index 9338470515..901ccaaefa 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -32,7 +32,7 @@ export async function serviceToken( jwt?: string | JwtPayload; iasOptions?: Omit< IasOptions, - 'authenticationType' | 'assertion' | 'destinationUrl' + 'authenticationType' | 'assertion' | 'destinationUrl' | 'appTid' >; } ): Promise { @@ -65,7 +65,8 @@ export async function serviceToken( serviceBinding.label === 'identity' ? await getIasClientCredentialsToken(serviceBinding, { ...(options?.iasOptions ?? {}), - authenticationType: 'OAuth2ClientCredentials' + authenticationType: 'OAuth2ClientCredentials', + appTid: tenantForCaching }) : await getClientCredentialsToken(serviceBinding, options?.jwt); From 08313bace89e0ebc4b7ccf58a64bac041fbd5124 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Thu, 18 Dec 2025 17:20:25 +0100 Subject: [PATCH 29/58] always set token_format: 'jwt' --- packages/connectivity/src/scp-cf/identity-service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 933305b2b3..eebcd0a544 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -142,7 +142,9 @@ async function getIasClientCredentialsTokenImpl( const authenticationType = arg.authenticationType || 'OAuth2ClientCredentials'; - const tokenOptions: IdTokenFetchOptions = {}; + const tokenOptions: IdTokenFetchOptions = { + token_format: 'jwt' + }; // Stringify resource(s) if (arg.resource) { From 5cda06c825bce3e164114edd6b1bfb7418d791c7 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 19 Dec 2025 08:55:44 +0100 Subject: [PATCH 30/58] remove iasResource list support & fix tests --- .../client-credentials-token-cache.spec.ts | 145 ------------------ .../scp-cf/client-credentials-token-cache.ts | 34 ++-- .../destination/destination-from-vcap.ts | 2 +- .../src/scp-cf/identity-service.spec.ts | 22 ++- .../src/scp-cf/identity-service.ts | 6 +- 5 files changed, 31 insertions(+), 178 deletions(-) diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts index a052e03a8d..206c7c088b 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts @@ -249,149 +249,4 @@ describe('ClientCredentialsTokenCache', () => { expect(key).toBe('tenant-123:client-id'); }); }); - - describe('Multiple resources (array) support', () => { - const validToken = { - access_token: 'multi-resource-token', - token_type: 'Bearer', - expires_in: oneHourInSeconds * 3, - jti: '', - scope: '' - }; - - beforeEach(() => { - clientCredentialsTokenCache.clear(); - }); - - it('should cache and retrieve token with array of resource clientIds', () => { - const resources = [ - { clientId: 'client-1', tenantId: 'tenant-1' }, - { clientId: 'client-2' }, - { name: 'app3' } - ]; - - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resources, - validToken - ); - - const cached = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resources - ); - - expect(cached).toEqual(validToken); - }); - it('should treat resource array as a set (order-independent)', () => { - const resources1 = [{ name: 'app-1' }, { name: 'app-2' }]; - const resources2 = [{ name: 'app-2' }, { name: 'app-1' }]; - - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resources1, - validToken - ); - - const cached1 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resources1 - ); - const cached2 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resources2 - ); - - expect(cached1).toEqual(validToken); - expect(cached2).toEqual(validToken); - }); - - it('should isolate cache by number of resources in array', () => { - const resources1 = [{ name: 'app-1' }]; - const resources2 = [{ name: 'app-1' }, { name: 'app-2' }]; - - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resources1, - validToken - ); - - const cached1 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resources1 - ); - const cached2 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resources2 - ); - - expect(cached1).toEqual(validToken); - expect(cached2).toBeUndefined(); - }); - - it('should not treat single resource and single-element array differently', () => { - const singleResource = { name: 'app-1' }; - const arrayResource = [{ name: 'app-1' }]; - - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - singleResource, - validToken - ); - - const cachedSingle = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - singleResource - ); - const cachedArray = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - arrayResource - ); - - expect(cachedSingle).toEqual(validToken); - expect(cachedArray).toEqual(validToken); - }); - - it('should generate correct cache key with array of resource names', () => { - const key = getCacheKey('tenant-123', 'client-id', [ - { name: 'app-1' }, - { name: 'app-2' } - ]); - expect(key).toBe('tenant-123:client-id:name=app-1::name=app-2'); - }); - - it('should generate correct cache key with array of resource clientIds', () => { - const key = getCacheKey('tenant-123', 'client-id', [ - { clientId: 'client-1' }, - { clientId: 'client-2', tenantId: 'tenant-2' } - ]); - expect(key).toBe( - 'tenant-123:client-id:clientid=client-1::clientid=client-2:tenantid=tenant-2' - ); - }); - - it('should generate correct cache key with array of mixed resource types', () => { - const key = getCacheKey('tenant-123', 'client-id', [ - { name: 'app-1' }, - { clientId: 'client-2' } - ]); - expect(key).toBe('tenant-123:client-id:clientid=client-2::name=app-1'); - }); - - it('should handle empty resource array', () => { - const key = getCacheKey('tenant-123', 'client-id', []); - expect(key).toBe('tenant-123:client-id'); - }); - }); }); diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts index 5427d987ed..98ad801e81 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts @@ -14,14 +14,14 @@ const ClientCredentialsTokenCache = ( getToken: ( tenantId: string | undefined, clientId: string, - resource?: IasResource | IasResource[] + resource?: IasResource ): ClientCredentialsResponse | undefined => cache.get(getCacheKey(tenantId, clientId, resource)), cacheToken: ( tenantId: string | undefined, clientId: string, - resource: IasResource | IasResource[] | undefined, + resource: IasResource | undefined, token: ClientCredentialsResponse ): void => { cache.set(getCacheKey(tenantId, clientId, resource), { @@ -43,29 +43,19 @@ const ClientCredentialsTokenCache = ( * @returns Normalized resource string or empty string if not provided. * @internal */ -function normalizeResource( - resource?: IasResource | IasResource[] -): string | undefined { +function normalizeResource(resource?: IasResource): string | undefined { if (!resource) { return undefined; } + if ('name' in resource) { + return `name=${resource.name}`; + } - const resources = Array.isArray(resource) ? resource : [resource]; - - return resources - .map(r => { - if ('name' in r) { - return `name=${r.name}`; - } - - let normalized = `clientid=${r.clientId}`; - if (r.tenantId) { - normalized += `:tenantid=${r.tenantId}`; - } - return normalized; - }) - .sort() - .join('::'); + let normalized = `clientid=${resource.clientId}`; + if (resource.tenantId) { + normalized += `:tenantid=${resource.tenantId}`; + } + return normalized; } /** * @@ -78,7 +68,7 @@ function normalizeResource( export function getCacheKey( tenantId: string | undefined, clientId: string, - resource?: IasResource | IasResource[] + resource?: IasResource ): string | undefined { if (!tenantId) { logger.warn( diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index a518b95973..4a59e4859b 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -141,7 +141,7 @@ interface IasOptionsBase { * Either provide the app name (common case) or the provider client ID * and tenant ID (optional). */ - resource?: IasResource | IasResource[]; + resource?: IasResource; /** * The consumer (BTP) tenant ID of the application. * May be required for multi-tenant communication. diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index b4919bd9b1..88df28b053 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -99,7 +99,9 @@ describe('getIasClientCredentialsToken', () => { aud: 'test-audience', ias_apis: ['dummy'] }); - expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({}); + expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ + token_format: 'jwt' + }); }); it('fetches IAS token with client secret authentication', async () => { @@ -125,7 +127,9 @@ describe('getIasClientCredentialsToken', () => { aud: 'test-audience', ias_apis: ['dummy'] }); - expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({}); + expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ + token_format: 'jwt' + }); }); it('includes resource parameter for app2app flow', async () => { @@ -136,7 +140,8 @@ describe('getIasClientCredentialsToken', () => { }); expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ - resource: 'urn:sap:identity:application:provider:name:my-app' + resource: 'urn:sap:identity:application:provider:name:my-app', + token_format: 'jwt' }); }); @@ -162,7 +167,8 @@ describe('getIasClientCredentialsToken', () => { expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ resource: 'urn:sap:identity:application:provider:name:my-app', - app_tid: 'tenant-123' + app_tid: 'tenant-123', + token_format: 'jwt' }); }); @@ -174,6 +180,7 @@ describe('getIasClientCredentialsToken', () => { }); expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ + token_format: 'jwt', custom_param: 'custom_value' }); }); @@ -222,7 +229,9 @@ describe('getIasClientCredentialsToken', () => { assertion: userAssertion }); - expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(userAssertion, {}); + expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(userAssertion, { + token_format: 'jwt' + }); expect(mockFetchClientCredentialsToken).not.toHaveBeenCalled(); }); @@ -254,7 +263,8 @@ describe('getIasClientCredentialsToken', () => { expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(userAssertion, { resource: 'urn:sap:identity:application:provider:name:my-app', app_tid: 'tenant-123', - refresh_token: '0' + refresh_token: '0', + token_format: 'jwt' }); }); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index eebcd0a544..43d340e0fa 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -146,11 +146,9 @@ async function getIasClientCredentialsTokenImpl( token_format: 'jwt' }; - // Stringify resource(s) + // Stringify resource if (arg.resource) { - tokenOptions.resource = Array.isArray(arg.resource) - ? arg.resource.map(convertResourceToUrn) - : convertResourceToUrn(arg.resource); + tokenOptions.resource = convertResourceToUrn(arg.resource); } if (arg.appTid) { From 67e7d13cbba3f0385e2e779f27db1c514e993bb6 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 19 Dec 2025 10:34:25 +0100 Subject: [PATCH 31/58] fix test again --- packages/connectivity/src/scp-cf/identity-service.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 88df28b053..e98883088c 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -153,7 +153,8 @@ describe('getIasClientCredentialsToken', () => { }); expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ - app_tid: 'tenant-123' + app_tid: 'tenant-123', + token_format: 'jwt' }); }); From 41cac51c8cab3cef7d97cf494e9763215f68da8e Mon Sep 17 00:00:00 2001 From: cloud-sdk-js Date: Fri, 19 Dec 2025 12:18:07 +0000 Subject: [PATCH 32/58] Changes from lint:fix --- packages/connectivity/src/scp-cf/identity-service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 43d340e0fa..f54fdbecc9 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -22,8 +22,7 @@ export { clearIdentityServices } from './environment-accessor'; * Represents the response to an IAS client credentials request. * Extends the XSUAA response with IAS-specific fields. */ -export interface IasClientCredentialsResponse - extends ClientCredentialsResponse { +export interface IasClientCredentialsResponse extends ClientCredentialsResponse { /** * Audience claim from the JWT token. */ From 87c7a9f6d3a506c7e0061d459858e98e15065294 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 22 Dec 2025 12:31:56 +0100 Subject: [PATCH 33/58] adress review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename IAS resource properties for clarity (clientId → providerClientId, tenantId → providerTenantId) - Move iasOptions parameter to main function signature in getDestinationFromServiceBinding for better API ergonomics - Enhance IasClientCredentialsResponse interface with additional JWT claims (scimId, custom_iss, app_tid) - Use IdentityServiceToken class from @sap/xssec for IAS-specific token decoding - Add zone_uuid fallback for legacy tenant ID resolution - Prioritize XSUAA user_id over IAS user_uuid in userId() function (aligning with priority-handling in other parts) - Remove IAS handling in jwtBearerToken() to only handle XSUAA flow - Add comprehensive test coverage for IAS token caching and resource isolation - Improve documentation for targetUrl and resource parameters --- .../client-credentials-token-cache.spec.ts | 22 +-- .../scp-cf/client-credentials-token-cache.ts | 6 +- .../destination/destination-from-vcap.spec.ts | 20 +++ .../destination/destination-from-vcap.ts | 27 ++-- .../src/scp-cf/identity-service.spec.ts | 17 ++- .../src/scp-cf/identity-service.ts | 64 ++++---- .../connectivity/src/scp-cf/jwt/jwt.spec.ts | 4 +- packages/connectivity/src/scp-cf/jwt/jwt.ts | 25 +--- .../src/scp-cf/token-accessor.spec.ts | 140 ++++++++++++++++++ .../connectivity/src/scp-cf/token-accessor.ts | 27 +--- 10 files changed, 254 insertions(+), 98 deletions(-) diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts index 206c7c088b..d4843452fb 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts @@ -130,7 +130,7 @@ describe('ClientCredentialsTokenCache', () => { }); it('should cache and retrieve token with resource clientId', () => { - const resource = { clientId: 'resource-client-123' }; + const resource = { providerClientId: 'resource-client-123' }; clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', @@ -150,8 +150,8 @@ describe('ClientCredentialsTokenCache', () => { it('should cache and retrieve token with resource clientId and tenantId', () => { const resource = { - clientId: 'resource-client-123', - tenantId: 'tenant-456' + providerClientId: 'resource-client-123', + providerTenantId: 'tenant-456' }; clientCredentialsTokenCache.cacheToken( @@ -197,8 +197,8 @@ describe('ClientCredentialsTokenCache', () => { }); it('should isolate cache by resource clientId', () => { - const resource1 = { clientId: 'client-1' }; - const resource2 = { clientId: 'client-2' }; + const resource1 = { providerClientId: 'client-1' }; + const resource2 = { providerClientId: 'client-2' }; clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', @@ -229,18 +229,20 @@ describe('ClientCredentialsTokenCache', () => { it('should generate correct cache key with resource clientId only', () => { const key = getCacheKey('tenant-123', 'client-id', { - clientId: 'resource-client-123' + providerClientId: 'resource-client-123' }); - expect(key).toBe('tenant-123:client-id:clientid=resource-client-123'); + expect(key).toBe( + 'tenant-123:client-id:provider-clientId=resource-client-123' + ); }); it('should generate correct cache key with resource clientId and tenantId', () => { const key = getCacheKey('tenant-123', 'client-id', { - clientId: 'resource-client-123', - tenantId: 'tenant-456' + providerClientId: 'resource-client-123', + providerTenantId: 'tenant-456' }); expect(key).toBe( - 'tenant-123:client-id:clientid=resource-client-123:tenantid=tenant-456' + 'tenant-123:client-id:provider-clientId=resource-client-123:provider-tenantId=tenant-456' ); }); diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts index 98ad801e81..d69cac2d30 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts @@ -51,9 +51,9 @@ function normalizeResource(resource?: IasResource): string | undefined { return `name=${resource.name}`; } - let normalized = `clientid=${resource.clientId}`; - if (resource.tenantId) { - normalized += `:tenantid=${resource.tenantId}`; + let normalized = `provider-clientId=${resource.providerClientId}`; + if (resource.providerTenantId) { + normalized += `:provider-tenantId=${resource.providerTenantId}`; } return normalized; } diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts index 8503715fdb..8f1cd9e137 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts @@ -267,6 +267,26 @@ describe('vcap-service-destination', () => { expect(destination?.authTokens?.[0]).toMatchObject({ value: jwt }); }); + + it('forwards iasOptions to the transform function', async () => { + const iasOptions = { + resource: { providerClientId: 'test-client-id' } + }; + const serviceBindingTransformFn = jest.fn(async (service: Service) => ({ + url: service.credentials.sys + })); + + await getDestinationFromServiceBinding({ + destinationName: 'my-custom-service', + serviceBindingTransformFn, + iasOptions + }); + + expect(serviceBindingTransformFn).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ iasOptions }) + ); + }); }); function mockServiceBindings() { diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index 4a59e4859b..e77228cfa4 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -28,6 +28,7 @@ const logger = createLogger({ * Throws an error if no services are bound at all, no service with the given name can be found, or the service type is not supported. * The last error can be circumvent by using the second parameter to provide a custom function that transforms a service binding to a destination. * @param options - Options to customize the behavior of this function. + * @param options.iasOptions - Options for IAS token retrieval in case of IAS authentication. * @returns A destination. */ export async function getDestinationFromServiceBinding( @@ -35,7 +36,7 @@ export async function getDestinationFromServiceBinding( DestinationFetchOptions, 'jwt' | 'iss' | 'useCache' | 'destinationName' > & - DestinationFromServiceBindingOptions + DestinationFromServiceBindingOptions & { iasOptions?: IasOptions } ): Promise { const decodedJwt = options.iss ? { iss: options.iss } @@ -74,14 +75,17 @@ async function retrieveDestination({ useCache, jwt, destinationName, + iasOptions, serviceBindingTransformFn }: Pick & { jwt?: JwtPayload; + iasOptions?: IasOptions; } & DestinationFromServiceBindingOptions) { const service = getServiceBindingByInstanceName(destinationName); const destination = await (serviceBindingTransformFn || transform)(service, { useCache, - jwt + jwt, + ...(iasOptions ? { iasOptions } : {}) }); return { name: destinationName, ...destination }; @@ -95,10 +99,6 @@ export interface DestinationFromServiceBindingOptions { * Custom transformation function to control how a {@link Destination} is built from the given {@link Service}. */ serviceBindingTransformFn?: ServiceBindingTransformFunction; - /** - * Options for IAS token retrieval. - */ - iasOptions?: IasOptions; } /** @@ -118,11 +118,11 @@ export type IasResource = Xor< /** * The client ID of the application resource. */ - clientId: string; + providerClientId: string; /** * The tenant ID of the application resource (Optional). */ - tenantId?: string; + providerTenantId?: string; } >; @@ -132,7 +132,11 @@ export type IasResource = Xor< interface IasOptionsBase { /** * The target URL of the destination that the IAS token is requested for. - * @default to the (identity service) URL from the service binding. + * It is recommended to provide this for App-to-App communication (when resource parameter is used), + * otherwise the destination will point to the identity service URL from the service binding, + * instead of the actual target application. This function is not able to to infer + * the target application from the information available. + * @default The (identity service) URL from the service binding. */ targetUrl?: string; /** @@ -140,6 +144,11 @@ interface IasOptionsBase { * The token will only be usable to call the requested application(s). * Either provide the app name (common case) or the provider client ID * and tenant ID (optional). + * + * It is recommended to also provide the targetUrl parameter, otherwise + * the destination will point to the identity service URL from the service bindingm, + * instead of the actual target application. This function is not able to to infer + * the target application from the information available. */ resource?: IasResource; /** diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index e98883088c..122686a720 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -10,6 +10,7 @@ const mockFetchClientCredentialsToken = jest.fn(); const mockFetchJwtBearerToken = jest.fn(); jest.mock('@sap/xssec', () => ({ + ...jest.requireActual('@sap/xssec'), IdentityService: jest.fn().mockImplementation(() => ({ fetchClientCredentialsToken: mockFetchClientCredentialsToken, fetchJwtBearerToken: mockFetchJwtBearerToken @@ -74,7 +75,11 @@ describe('getIasClientCredentialsToken', () => { access_token: signedJwt({ jti: 'mock-jti', aud: 'test-audience', - ias_apis: ['dummy'] + ias_apis: ['dummy'], + // Fallback value if app_tid missing (legacy) + zone_uuid: 'custom-tenant-id', + iss: 'https://tenant.accounts.ondemand.com', + ias_iss: 'https://ias-tenant.accounts.ondemand.com' }), token_type: 'Bearer', expires_in: 3600 @@ -97,7 +102,10 @@ describe('getIasClientCredentialsToken', () => { scope: '', jti: 'mock-jti', aud: 'test-audience', - ias_apis: ['dummy'] + app_tid: 'custom-tenant-id', + custom_iss: 'https://tenant.accounts.ondemand.com', + ias_apis: ['dummy'], + scimId: undefined }); expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ token_format: 'jwt' @@ -125,7 +133,10 @@ describe('getIasClientCredentialsToken', () => { scope: '', jti: 'mock-jti', aud: 'test-audience', - ias_apis: ['dummy'] + app_tid: 'custom-tenant-id', + custom_iss: 'https://tenant.accounts.ondemand.com', + ias_apis: ['dummy'], + scimId: undefined }); expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ token_format: 'jwt' diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index f54fdbecc9..429f68100b 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -1,11 +1,11 @@ import { executeWithMiddleware } from '@sap-cloud-sdk/resilience/internal'; import { resilience } from '@sap-cloud-sdk/resilience'; -import { decodeJwt, isIasToken } from './jwt'; +import { IdentityServiceToken, type IdentityService } from '@sap/xssec'; +import { decodeJwt, isXsuaaToken } from './jwt'; import { resolveServiceBinding, getIdentityServiceInstanceFromCredentials } from './environment-accessor'; -import type { IdentityService } from '@sap/xssec'; import type { DestinationOptions, IasOptions, @@ -26,11 +26,27 @@ export interface IasClientCredentialsResponse extends ClientCredentialsResponse /** * Audience claim from the JWT token. */ - aud?: string | string[]; + aud: string | string[]; /** - * IAS API resources. Present when resource parameter is specified in the token request. + * IAS API resources. Empty when no resource parameter is specified in the token request. */ - ias_apis?: string[]; + ias_apis: string[]; + /** + * The SCIM ID of the user (not present for technical user tokens). + */ + scimId?: string; + /** + * Custom issuer claim from the JWT token. + */ + custom_iss?: string; + /** + * Application tenant ID claim from the JWT token. + */ + app_tid?: string; + /** + * IAS tokens don't have scope property. + */ + scope: ''; } /** @@ -44,7 +60,7 @@ export function shouldExchangeToken(options: DestinationOptions): boolean { return ( options.iasToXsuaaTokenExchange === true && !!options.jwt && - isIasToken(decodeJwt(options.jwt)) + !isXsuaaToken(decodeJwt(options.jwt)) ); } @@ -52,19 +68,6 @@ type IasParameters = { serviceCredentials: ServiceCredentials; } & IasOptions; -type FirstArg = T extends (arg1: infer U, ...args: any[]) => any ? U : never; -type UnwrapPromise = T extends Promise ? U : T; - -// xssec does not properly export these -type IdTokenFetchOptions = FirstArg< - InstanceType['fetchClientCredentialsToken'] ->; -type TokenFetchResponse = UnwrapPromise< - ReturnType< - InstanceType['fetchClientCredentialsToken'] - > ->; - /** * Make a client credentials request against the IAS OAuth2 endpoint. * Supports both certificate-based (mTLS) and client secret authentication. @@ -118,9 +121,9 @@ function convertResourceToUrn(resource: IasResource): string { return `urn:sap:identity:application:provider:name:${resource.name}`; } - let urn = `urn:sap:identity:application:provider:clientid:${resource.clientId}`; - if (resource.tenantId) { - urn += `:tenantid:${resource.tenantId}`; + let urn = `urn:sap:identity:application:provider:clientid:${resource.providerClientId}`; + if (resource.providerTenantId) { + urn += `:tenantid:${resource.providerTenantId}`; } return urn; } @@ -141,7 +144,8 @@ async function getIasClientCredentialsTokenImpl( const authenticationType = arg.authenticationType || 'OAuth2ClientCredentials'; - const tokenOptions: IdTokenFetchOptions = { + const tokenOptions: IdentityService.TokenFetchOptions & + IdentityService.IdentityServiceTokenFetchOptions = { token_format: 'jwt' }; @@ -159,7 +163,7 @@ async function getIasClientCredentialsTokenImpl( Object.assign(tokenOptions, arg.extraParams); } - let response: undefined | TokenFetchResponse; + let response: undefined | IdentityService.TokenFetchResponse; if (authenticationType === 'OAuth2JWTBearer') { // JWT bearer grant for business user propagation @@ -184,7 +188,7 @@ async function getIasClientCredentialsTokenImpl( response = await identityService.fetchClientCredentialsToken(tokenOptions); } - const decodedJwt = decodeJwt(response.access_token); + const decodedJwt = new IdentityServiceToken(response.access_token); return { access_token: response.access_token, @@ -192,9 +196,13 @@ async function getIasClientCredentialsTokenImpl( expires_in: response.expires_in, // IAS tokens don't have scope property scope: '', - jti: decodedJwt.jti ?? '', - aud: decodedJwt.aud, + jti: decodedJwt.getPayload()?.jti ?? '', + // `decodedJwt.audiences` always returns an array, preserve original type + aud: decodedJwt.getPayload()?.aud ?? [], + app_tid: decodedJwt.appTid, + scimId: decodedJwt.scimId, // Added if resource parameter was specified - ias_apis: decodedJwt?.ias_apis + ias_apis: decodedJwt?.consumedApis, + custom_iss: decodedJwt.customIssuer ?? undefined }; } diff --git a/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts b/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts index 71716767b9..8e9d949b6a 100644 --- a/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts +++ b/packages/connectivity/src/scp-cf/jwt/jwt.spec.ts @@ -18,12 +18,12 @@ describe('jwt', () => { expect(userId(iasPayload)).toBe('ias-user-uuid-456'); }); - it('prefers user_uuid over user_id when both are present', () => { + it('prefers user_id over user_uuid when both are present', () => { const mixedPayload = { user_uuid: 'ias-user-uuid-456', user_id: 'xsuaa-user-123' }; - expect(userId(mixedPayload)).toBe('ias-user-uuid-456'); + expect(userId(mixedPayload)).toBe('xsuaa-user-123'); }); }); diff --git a/packages/connectivity/src/scp-cf/jwt/jwt.ts b/packages/connectivity/src/scp-cf/jwt/jwt.ts index 67d55bec81..069f415db9 100644 --- a/packages/connectivity/src/scp-cf/jwt/jwt.ts +++ b/packages/connectivity/src/scp-cf/jwt/jwt.ts @@ -34,9 +34,9 @@ function makeArray(val: string | string[] | undefined): string[] { */ export function userId(jwtPayload: JwtPayload): string { // IAS tokens use user_uuid, XSUAA tokens use user_id - const id = jwtPayload.user_uuid || jwtPayload.user_id; + const id = jwtPayload.user_id || jwtPayload.user_uuid; logger.debug( - `JWT user identifier is: ${id} (from ${jwtPayload.user_uuid ? 'user_uuid (IAS)' : 'user_id (XSUAA)'}).` + `JWT user identifier is: ${id} (from ${jwtPayload.user_id ? 'user_id (XSUAA)' : 'user_uuid (IAS)'}).` ); return id; } @@ -54,7 +54,7 @@ export function getDefaultTenantId(): string { } /** - * Get the tenant ID of a decoded JWT, based on its `zid` or if not available `app_tid` property. + * Get the tenant ID of a decoded JWT, based on its `zid` or if not available `app_tid` or `zone_uuid` (legacy) property. * @param jwt - Token to read the tenant ID from. * @returns The tenant ID, if available. */ @@ -63,9 +63,11 @@ export function getTenantId( ): string | undefined { const decodedJwt = jwt ? decodeJwt(jwt) : {}; logger.debug( - `JWT zid is: ${decodedJwt.zid}, app_tid is: ${decodedJwt.app_tid}.` + `JWT zid is: ${decodedJwt.zid}, app_tid is: ${decodedJwt.app_tid}, zone_uuid is: ${decodedJwt.zone_uuid}.` + ); + return ( + decodedJwt.zid || decodedJwt.app_tid || decodedJwt.zone_uuid || undefined ); - return decodedJwt.zid || decodedJwt.app_tid || undefined; } /** @@ -92,17 +94,6 @@ export function isIasToken(decodedJwt: JwtPayload): boolean { } } -/** - * Check if the given JWT is not an IAS token. - * Currently, there are only two domains for IAS tokens: - * `accounts.ondemand.com` and `accounts400.ondemand.com`. - * @param decodedJwt - The decoded JWT to check. - * @returns Whether the given JWT is not an IAS token. - */ -function isNotIasToken(decodedJwt: JwtPayload): boolean { - return !isIasToken(decodedJwt); -} - /** * @internal * Retrieve the subdomain from the decoded XSUAA JWT or ISS object. @@ -117,7 +108,7 @@ export function getSubdomain( const decodedJwt = jwt ? decodeJwt(jwt) : {}; return ( decodedJwt?.ext_attr?.zdn || - (isNotIasToken(decodedJwt) ? getIssuerSubdomain(decodedJwt) : undefined) + (isIasToken(decodedJwt) ? undefined : getIssuerSubdomain(decodedJwt)) ); } diff --git a/packages/connectivity/src/scp-cf/token-accessor.spec.ts b/packages/connectivity/src/scp-cf/token-accessor.spec.ts index f1739c04bf..5bb61874fc 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.spec.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.spec.ts @@ -30,6 +30,7 @@ import { import { clientCredentialsTokenCache } from './client-credentials-token-cache'; import { jwtBearerToken, serviceToken } from './token-accessor'; import { clearXsuaaServices } from './environment-accessor'; +import * as identityService from './identity-service'; import type { ClientCredentialsResponse } from './xsuaa-service-types'; describe('token accessor', () => { @@ -376,5 +377,144 @@ describe('token accessor', () => { '"Could not find service binding of type \'destination\'."' ); }); + + describe('IAS/identity service handling', () => { + const mockIasService = { + name: 'my-identity', + label: 'identity', + tags: ['identity'], + credentials: { + url: 'https://tenant.accounts.ondemand.com', + clientid: 'ias-client-id', + clientsecret: 'ias-secret', + app_tid: 'ias-tenant-id' + } + }; + + const mockIasToken = { + access_token: signedJwt({ jti: 'ias-jti', ias_apis: ['test'] }), + token_type: 'Bearer', + expires_in: 3600, + scope: '' as const, + jti: 'ias-jti', + aud: [], + ias_apis: ['test'] + }; + + beforeEach(() => { + process.env.VCAP_SERVICES = JSON.stringify({ + identity: [mockIasService] + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('uses getIasClientCredentialsToken for identity service', async () => { + const getIasTokenSpy = jest + .spyOn(identityService, 'getIasClientCredentialsToken') + .mockResolvedValue(mockIasToken); + + const token = await serviceToken('identity'); + + expect(token).toBe(mockIasToken.access_token); + expect(getIasTokenSpy).toHaveBeenCalledWith(mockIasService, { + authenticationType: 'OAuth2ClientCredentials', + appTid: mockIasService.credentials.app_tid + }); + }); + + it('forwards iasOptions (resource and extraParams) to getIasClientCredentialsToken', async () => { + const getIasTokenSpy = jest + .spyOn(identityService, 'getIasClientCredentialsToken') + .mockResolvedValue(mockIasToken); + + const iasOptions = { + resource: { providerClientId: 'target-app-client-id' }, + extraParams: { custom_param: 'custom_value' } + }; + + await serviceToken('identity', { iasOptions }); + + expect(getIasTokenSpy).toHaveBeenCalledWith( + mockIasService, + expect.objectContaining({ + resource: iasOptions.resource, + extraParams: iasOptions.extraParams, + authenticationType: 'OAuth2ClientCredentials', + appTid: mockIasService.credentials.app_tid + }) + ); + }); + + it('uses tenant from JWT as appTid when JWT is provided', async () => { + const getIasTokenSpy = jest + .spyOn(identityService, 'getIasClientCredentialsToken') + .mockResolvedValue(mockIasToken); + + const jwt = signedXsuaaJwt({ + zid: 'subscriber-tenant-id' + }); + + await serviceToken('identity', { jwt }); + + expect(getIasTokenSpy).toHaveBeenCalledWith( + mockIasService, + expect.objectContaining({ + appTid: 'subscriber-tenant-id' + }) + ); + }); + + it('caches IAS tokens with resource parameter', async () => { + jest + .spyOn(identityService, 'getIasClientCredentialsToken') + .mockResolvedValue(mockIasToken); + + const iasOptions = { + resource: { providerClientId: 'target-app-client-id' } + }; + + const first = await serviceToken('identity', { iasOptions }); + const second = await serviceToken('identity', { iasOptions }); + + expect(first).toBe(mockIasToken.access_token); + expect(second).toBe(mockIasToken.access_token); + + const cached = clientCredentialsTokenCache.getToken( + mockIasService.credentials.app_tid, + mockIasService.credentials.clientid, + iasOptions.resource + ); + + expect(cached?.access_token).toBe(mockIasToken.access_token); + }); + + it('isolates cache by IAS resource parameter', async () => { + jest + .spyOn(identityService, 'getIasClientCredentialsToken') + .mockResolvedValue(mockIasToken); + + const resource1 = { providerClientId: 'app-1' }; + const resource2 = { providerClientId: 'app-2' }; + + await serviceToken('identity', { iasOptions: { resource: resource1 } }); + + const cached1 = clientCredentialsTokenCache.getToken( + mockIasService.credentials.app_tid, + mockIasService.credentials.clientid, + resource1 + ); + const cached2 = clientCredentialsTokenCache.getToken( + mockIasService.credentials.app_tid, + mockIasService.credentials.clientid, + resource2 + ); + + expect(cached1).toBeDefined(); + expect(cached2).toBeUndefined(); + }); + }); }); }); diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index 901ccaaefa..5a3cffc57b 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -92,40 +92,15 @@ export async function serviceToken( * Returns a JWT bearer token that can be used to call the given service. * The token is fetched via a JWT bearer token grant using the user token + client credentials. * - * This function automatically detects the service type (XSUAA or IAS) based on the label - * and uses the appropriate authentication flow. - * For IAS services, you can pass IAS-specific options like `resource` and `appTid` in `options.iasOptions`. - * * Throws an error if there is no instance of the given service type. * @param jwt - The JWT of the user for whom the access token should be fetched. * @param service - The type of the service or an instance of {@link Service}. - * @param options - Optional options to modify token fetching behaviour. - * @param options.iasOptions - Options to change IAS token fetching (see {@link IasOptions}). * @returns A JWT bearer token. */ export async function jwtBearerToken( jwt: string, - service: string | Service, - options?: { - iasOptions?: Omit< - IasOptions, - 'authenticationType' | 'assertion' | 'destinationUrl' - >; - } + service: string | Service ): Promise { const resolvedService = resolveServiceBinding(service); - - // Detect if this is an IAS service - if (resolvedService.label === 'identity') { - return ( - await getIasClientCredentialsToken(resolvedService, { - ...(options?.iasOptions ?? {}), - authenticationType: 'OAuth2JWTBearer', - assertion: jwt - }) - ).access_token; - } - - // XSUAA flow return getUserToken(resolvedService, jwt); } From ac48d3ab508b7188aba081e1244f8b81d1b5f2bc Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 22 Dec 2025 16:07:20 +0100 Subject: [PATCH 34/58] fix `convertResourceToUrn` `providerTenantId` handling --- packages/connectivity/src/scp-cf/identity-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 429f68100b..298b5ff7f4 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -123,7 +123,7 @@ function convertResourceToUrn(resource: IasResource): string { let urn = `urn:sap:identity:application:provider:clientid:${resource.providerClientId}`; if (resource.providerTenantId) { - urn += `:tenantid:${resource.providerTenantId}`; + urn += `:apptid:${resource.providerTenantId}`; } return urn; } From 8dbc0439934ab6b8035cdf6806b80f6f22b5446e Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 23 Dec 2025 11:47:20 +0100 Subject: [PATCH 35/58] extract subdomain from user assertion --- .../scp-cf/environment-accessor/ias.spec.ts | 1 + .../src/scp-cf/environment-accessor/ias.ts | 50 ++++- .../src/scp-cf/identity-service.spec.ts | 212 +++++++++++++++++- .../src/scp-cf/identity-service.ts | 19 +- .../src/scp-cf/token-accessor.spec.ts | 51 +++++ .../connectivity/src/scp-cf/token-accessor.ts | 35 ++- 6 files changed, 345 insertions(+), 23 deletions(-) diff --git a/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts b/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts index 978ca8d9c9..b9636ded0e 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts @@ -40,6 +40,7 @@ describe('ias', () => { ).not.toBe( getIdentityServiceInstanceFromCredentials( createServiceCredentials(), + undefined, true ) ); diff --git a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts index 858fb02b94..9bae71f49d 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts @@ -1,8 +1,6 @@ -import { IdentityService } from '@sap/xssec'; -import type { - ServiceCredentials, - IdentityServiceCredentials -} from './environment-accessor-types'; +import { IdentityService, IdentityServiceToken } from '@sap/xssec'; +import { ErrorWithCause } from '@sap-cloud-sdk/util'; +import type { IdentityServiceCredentials } from './environment-accessor-types'; const identityServices: Record = {}; @@ -15,14 +13,24 @@ export function clearIdentityServices(): void { Object.keys(identityServices).forEach(key => delete identityServices[key]); } +function tryParseUrl(url: string, name: string): URL { + try { + return new URL(url); + } catch (err) { + throw new ErrorWithCause(`Could not parse ${name} URL: ${url}`, err); + } +} + /** * @internal * @param credentials - Identity service credentials extracted from a service binding or re-use service. Required to create the xssec IdentityService instance. + * @param assertion - Optional JWT assertion to extract the issuer URL for bearer assertion flows. * @param disableCache - Value to enable or disable JWKS cache in xssec library. Defaults to false. * @returns An instance of {@code @sap/xssec/IdentityService} for the provided credentials. */ export function getIdentityServiceInstanceFromCredentials( - credentials: ServiceCredentials, + credentials: IdentityServiceCredentials, + assertion?: string, disableCache: boolean = false ): IdentityService { const serviceConfig = disableCache @@ -36,11 +44,37 @@ export function getIdentityServiceInstanceFromCredentials( } : undefined; - const cacheKey = `${credentials.clientid}:${disableCache}`; + let subdomain: string | undefined; + if (assertion) { + const decodedJwt = new IdentityServiceToken(assertion); + const issuer = decodedJwt.issuer; + const issuerUrl = tryParseUrl(issuer, 'JWT assertion issuer'); + subdomain = issuerUrl.hostname.split('.')[0]; + // Replace subdomain in the URL from the service binding + // Reason: We don't want to blindly trust the URL in the assertion + const credentialsUrl = tryParseUrl(credentials.url, 'Identity Service'); + const credentialsSplit = credentialsUrl.hostname.split('.'); + credentialsUrl.hostname = [subdomain, ...credentialsSplit.slice(1)].join( + '.' + ); + let normalizedUrl = credentialsUrl.toString(); + if (normalizedUrl.endsWith('/')) { + normalizedUrl = normalizedUrl.slice(0, -1); + } + credentials = { + ...credentials, + url: normalizedUrl + }; + } + + subdomain = + subdomain ?? + tryParseUrl(credentials.url, 'Identity Service').hostname.split('.')[0]; + const cacheKey = `${credentials.clientid}:${subdomain}:${disableCache}`; if (!identityServices[cacheKey]) { identityServices[cacheKey] = new IdentityService( - credentials as IdentityServiceCredentials, + credentials, serviceConfig ); } diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 122686a720..17ec7789d1 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -9,13 +9,32 @@ import type { Service } from './environment-accessor'; const mockFetchClientCredentialsToken = jest.fn(); const mockFetchJwtBearerToken = jest.fn(); -jest.mock('@sap/xssec', () => ({ - ...jest.requireActual('@sap/xssec'), - IdentityService: jest.fn().mockImplementation(() => ({ +jest.mock('@sap/xssec', () => { + const mockGetSafeUrlFromTokenIssuer = jest.fn(); + const mockIdentityService: any = jest.fn().mockImplementation(() => ({ fetchClientCredentialsToken: mockFetchClientCredentialsToken, fetchJwtBearerToken: mockFetchJwtBearerToken - })) -})); + })); + mockIdentityService.getSafeUrlFromTokenIssuer = mockGetSafeUrlFromTokenIssuer; + + return { + ...jest.requireActual('@sap/xssec'), + IdentityService: mockIdentityService, + IdentityServiceToken: jest.fn().mockImplementation((jwt: string) => { + const payload = JSON.parse( + Buffer.from(jwt.split('.')[1], 'base64').toString() + ); + return { + getPayload: () => payload, + appTid: payload.app_tid ?? payload.zone_uuid, + scimId: payload.scim_id, + consumedApis: payload.ias_apis, + customIssuer: payload.iss, + issuer: payload.iss + }; + }) + }; +}); describe('shouldExchangeToken', () => { it('should not exchange token from XSUAA', async () => { @@ -203,7 +222,17 @@ describe('getIasClientCredentialsToken', () => { ); await expect(getIasClientCredentialsToken(mockIasService)).rejects.toThrow( - 'Could not fetch IAS client credentials token for service of type identity' + 'Could not fetch IAS client credentials token for service "my-identity-service" of type identity: Network error' + ); + }); + + it('adds multi-tenant hint for 401 errors', async () => { + const error: any = new Error('Unauthorized'); + error.response = { status: 401 }; + mockFetchClientCredentialsToken.mockRejectedValue(error); + + await expect(getIasClientCredentialsToken(mockIasService)).rejects.toThrow( + /ensure that the service instance is declared as dependency to SaaS Provisioning Service or Subscription Manager/ ); }); @@ -232,6 +261,7 @@ describe('getIasClientCredentialsToken', () => { mockFetchJwtBearerToken.mockResolvedValue(mockTokenResponse); const userAssertion = signedJwt({ + iss: 'https://tenant.accounts.ondemand.com', user_uuid: 'user-123', app_tid: 'tenant-456' }); @@ -261,6 +291,7 @@ describe('getIasClientCredentialsToken', () => { mockFetchJwtBearerToken.mockResolvedValue(mockTokenResponse); const userAssertion = signedJwt({ + iss: 'https://tenant.accounts.ondemand.com', user_uuid: 'user-123', app_tid: 'tenant-456' }); @@ -280,4 +311,173 @@ describe('getIasClientCredentialsToken', () => { }); }); }); + + describe('multi-tenant subscriber routing', () => { + const providerUrl = 'https://provider.accounts.ondemand.com'; + const subscriberUrl = 'https://subscriber.accounts.ondemand.com'; + + const providerService: Service = { + name: 'provider-ias', + label: 'identity', + tags: ['identity'], + credentials: { + url: providerUrl, + clientid: 'test-client-id', + clientsecret: 'test-secret' + } + }; + + beforeEach(() => { + jest.clearAllMocks(); + clearIdentityServices(); + mockFetchJwtBearerToken.mockResolvedValue(mockTokenResponse); + }); + + it('uses provider IdentityService when JWT issuer matches provider URL', async () => { + const assertion = signedJwt({ + iss: providerUrl, + user_uuid: 'user-123' + }); + + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion + }); + + expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(assertion, { + token_format: 'jwt' + }); + }); + + it('creates subscriber IdentityService when JWT issuer differs from provider', async () => { + const assertion = signedJwt({ + iss: subscriberUrl, + user_uuid: 'user-123' + }); + + const { IdentityService } = jest.requireMock('@sap/xssec'); + + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion + }); + + // Verify subscriber instance created with subscriber URL + expect(IdentityService).toHaveBeenCalledWith( + expect.objectContaining({ + url: subscriberUrl + }), + undefined + ); + + expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(assertion, { + token_format: 'jwt' + }); + }); + + it('does not route for client credentials flow', async () => { + const { IdentityService } = jest.requireMock('@sap/xssec'); + + mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); + + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2ClientCredentials' + }); + + // Should only create provider instance + expect(IdentityService).toHaveBeenCalledTimes(1); + expect(IdentityService).toHaveBeenCalledWith( + expect.objectContaining({ + url: providerUrl + }), + undefined + ); + }); + + it('throws error when JWT issuer extraction fails', async () => { + const assertion = signedJwt({ + // Missing issuer field to trigger error + user_uuid: 'user-123' + }); + + await expect( + getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion + }) + ).rejects.toThrow('Could not parse JWT assertion issuer URL: undefined'); + }); + + it('caches subscriber instances per URL', async () => { + const subscriber1Url = 'https://subscriber1.accounts.ondemand.com'; + const subscriber2Url = 'https://subscriber2.accounts.ondemand.com'; + + const assertion1 = signedJwt({ + iss: subscriber1Url, + user_uuid: 'user-123' + }); + + const assertion2 = signedJwt({ + iss: subscriber2Url, + user_uuid: 'user-456' + }); + + const { IdentityService } = jest.requireMock('@sap/xssec'); + + // First call with subscriber1 + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion: assertion1 + }); + + const callsAfterFirst = IdentityService.mock.calls.length; + + // Second call with same subscriber1 - should use cached instance + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion: assertion1 + }); + + // Should not create new instance (cached) + expect(IdentityService.mock.calls.length).toBe(callsAfterFirst); + + // Third call with different subscriber2 - should create new instance + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion: assertion2 + }); + + // Should create new instance for subscriber2 + expect(IdentityService.mock.calls.length).toBeGreaterThan( + callsAfterFirst + ); + }); + + it('handles URLs with trailing slashes correctly', async () => { + const providerWithSlash = 'https://provider.accounts.ondemand.com/'; + + const serviceWithSlash: Service = { + ...providerService, + credentials: { + ...providerService.credentials, + url: providerWithSlash + } + }; + + const assertion = signedJwt({ + iss: 'https://provider.accounts.ondemand.com', + user_uuid: 'user-123' + }); + + const { IdentityService } = jest.requireMock('@sap/xssec'); + + await getIasClientCredentialsToken(serviceWithSlash, { + authenticationType: 'OAuth2JWTBearer', + assertion + }); + + // Should handle URLs with and without trailing slashes + expect(IdentityService).toHaveBeenCalled(); + }); + }); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 298b5ff7f4..5f11f4ece3 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -1,6 +1,7 @@ import { executeWithMiddleware } from '@sap-cloud-sdk/resilience/internal'; import { resilience } from '@sap-cloud-sdk/resilience'; import { IdentityServiceToken, type IdentityService } from '@sap/xssec'; +import { ErrorWithCause } from '@sap-cloud-sdk/util'; import { decodeJwt, isXsuaaToken } from './jwt'; import { resolveServiceBinding, @@ -99,8 +100,19 @@ export async function getIasClientCredentialsToken( tenantId: fnArgument.serviceCredentials.tenantid } }).catch(err => { - throw new Error( - `Could not fetch IAS client credentials token for service of type ${resolvedService.label}: ${err.message}` + const serviceName = + typeof service === 'string' ? service : service.name || 'unknown'; + let message = `Could not fetch IAS client credentials token for service "${serviceName}" of type ${resolvedService.label}`; + + // Add contextual hints based on error status code (similar to Java SDK) + if (err.response?.status === 401) { + message += + '. In case you are accessing a multi-tenant BTP service on behalf of a subscriber tenant, ensure that the service instance is declared as dependency to SaaS Provisioning Service or Subscription Manager (SMS) and subscribed for the current tenant'; + } + + throw new ErrorWithCause( + message + (err.message ? `: ${err.message}` : '.'), + err ); }); return token; @@ -138,7 +150,8 @@ async function getIasClientCredentialsTokenImpl( arg: IasParameters ): Promise { const identityService = getIdentityServiceInstanceFromCredentials( - arg.serviceCredentials + arg.serviceCredentials, + arg.assertion ); const authenticationType = diff --git a/packages/connectivity/src/scp-cf/token-accessor.spec.ts b/packages/connectivity/src/scp-cf/token-accessor.spec.ts index 5bb61874fc..df16b909b9 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.spec.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.spec.ts @@ -1,5 +1,6 @@ import nock from 'nock'; import * as resilience from '@sap-cloud-sdk/resilience'; +import { createLogger } from '@sap-cloud-sdk/util'; import { destinationBindingClientSecretMock, destinationBindingCertMock, @@ -467,6 +468,56 @@ describe('token accessor', () => { ); }); + it('logs warning when using IAS service with XSUAA JWT', async () => { + const logger = createLogger({ + package: 'connectivity', + messageContext: 'token-accessor' + }); + const warnSpy = jest.spyOn(logger, 'warn'); + + jest + .spyOn(identityService, 'getIasClientCredentialsToken') + .mockResolvedValue(mockIasToken); + + const xsuaaJwt = signedXsuaaJwt({ + zid: 'unique-subscriber-tenant-id', + ext_attr: { enhancer: 'XSUAA' } + }); + + await serviceToken('identity', { jwt: xsuaaJwt, useCache: false }); + + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'Requesting token for IAS service with a XSUAA JWT' + ) + ); + + warnSpy.mockRestore(); + }); + + it('uses explicitly provided appTid over automatically determined tenant', async () => { + const getIasTokenSpy = jest + .spyOn(identityService, 'getIasClientCredentialsToken') + .mockResolvedValue(mockIasToken); + + const explicitAppTid = 'explicit-tenant-id'; + const jwt = signedXsuaaJwt({ + zid: 'jwt-tenant-id' + }); + + await serviceToken('identity', { + jwt, + iasOptions: { appTid: explicitAppTid } + }); + + expect(getIasTokenSpy).toHaveBeenCalledWith( + mockIasService, + expect.objectContaining({ + appTid: explicitAppTid // Should use explicit value, not jwt-tenant-id + }) + ); + }); + it('caches IAS tokens with resource parameter', async () => { jest .spyOn(identityService, 'getIasClientCredentialsToken') diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index 5a3cffc57b..447fe0ac44 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -1,9 +1,11 @@ -import { ErrorWithCause } from '@sap-cloud-sdk/util'; +import { createLogger, ErrorWithCause } from '@sap-cloud-sdk/util'; import { + decodeJwt, getDefaultTenantId, getSubdomain, getTenantId, - getTenantIdFromBinding + getTenantIdFromBinding, + isXsuaaToken } from './jwt'; import { clientCredentialsTokenCache } from './client-credentials-token-cache'; import { resolveServiceBinding } from './environment-accessor'; @@ -14,6 +16,11 @@ import type { CachingOptions } from './cache'; import type { JwtPayload } from './jsonwebtoken-type'; import type { IasOptions } from './destination'; +const logger = createLogger({ + package: 'connectivity', + messageContext: 'token-accessor' +}); + /** * Returns an access token that can be used to call the given service. The token is fetched via a client credentials grant with the credentials of the given service. * If multiple instances of the provided service exist, the first instance will be selected. @@ -32,7 +39,7 @@ export async function serviceToken( jwt?: string | JwtPayload; iasOptions?: Omit< IasOptions, - 'authenticationType' | 'assertion' | 'destinationUrl' | 'appTid' + 'authenticationType' | 'assertion' | 'destinationUrl' >; } ): Promise { @@ -44,9 +51,13 @@ export async function serviceToken( const serviceBinding = resolveServiceBinding(service); const serviceCredentials = serviceBinding.credentials; - const tenantForCaching = options?.jwt - ? getTenantId(options.jwt) || getSubdomain(options.jwt) - : getTenantIdFromBinding() || getDefaultTenantId(); + // User-provided appTid takes precedence over automatically determined tenant for IAS + const tenantForCaching = + serviceBinding.label === 'identity' && options?.iasOptions?.appTid + ? options.iasOptions.appTid + : options?.jwt + ? getTenantId(options.jwt) || getSubdomain(options.jwt) + : getTenantIdFromBinding() || getDefaultTenantId(); const resourceForCaching = options?.iasOptions?.resource; if (opts.useCache) { @@ -61,6 +72,18 @@ export async function serviceToken( } try { + // Warn if using IAS service with XSUAA jwt (should be more reliable than the IAS check) + if (serviceBinding.label === 'identity' && options?.jwt) { + const decodedJwt = decodeJwt(options.jwt); + if (isXsuaaToken(decodedJwt)) { + logger.warn( + 'Requesting token for IAS service with a XSUAA JWT. ' + + 'The tenant information from the XSUAA token may not be compatible with IAS. ' + + 'Consider using an IAS JWT or omitting the JWT to use the provider tenant.' + ); + } + } + const token = serviceBinding.label === 'identity' ? await getIasClientCredentialsToken(serviceBinding, { From 005e875903f2833b7f4480717c080ca8e3b4e340 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 30 Dec 2025 14:34:32 +0100 Subject: [PATCH 36/58] address review comments --- .changeset/slow-cars-lie.md | 2 +- packages/connectivity/src/index.ts | 1 + .../destination/destination-accessor-types.ts | 2 +- .../destination/destination-from-vcap.spec.ts | 118 ++++++++++++++++++ .../destination/destination-from-vcap.ts | 101 +-------------- .../src/scp-cf/destination/ias-types.ts | 93 ++++++++++++++ .../src/scp-cf/destination/index.ts | 1 + .../src/scp-cf/identity-service.ts | 1 + .../src/scp-cf/token-accessor.spec.ts | 36 +++--- .../connectivity/src/scp-cf/token-accessor.ts | 97 +++++++++++--- 10 files changed, 312 insertions(+), 140 deletions(-) create mode 100644 packages/connectivity/src/scp-cf/destination/ias-types.ts diff --git a/.changeset/slow-cars-lie.md b/.changeset/slow-cars-lie.md index 2e6ea4225c..e4efe3efb9 100644 --- a/.changeset/slow-cars-lie.md +++ b/.changeset/slow-cars-lie.md @@ -2,4 +2,4 @@ '@sap-cloud-sdk/connectivity': minor --- -[New Functionality] Support IAS (App-to-App) authentication (experimental) +[New Functionality] Support IAS (App-to-App) authentication (experimental). Use `transformServiceBindingToDestination` and `getDestinationFromServiceBinding` to create a destination from an IAS service binding. diff --git a/packages/connectivity/src/index.ts b/packages/connectivity/src/index.ts index fc13a95dd0..8076b6c875 100644 --- a/packages/connectivity/src/index.ts +++ b/packages/connectivity/src/index.ts @@ -17,6 +17,7 @@ export { retrieveJwt, jwtBearerToken, serviceToken, + serviceTokenIas, isHttpDestination, assertHttpDestination, DestinationSelectionStrategies, diff --git a/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts b/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts index 4dc555bdec..e8d90186ee 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts @@ -51,7 +51,7 @@ export interface DestinationAccessorOptions { * ATTENTION: The property is mandatory in the following cases: * - User-dependent authentication flow is used, e.g., `OAuth2UserTokenExchange`, `OAuth2JWTBearer`, `OAuth2SAMLBearerAssertion`, `SAMLAssertion` or `PrincipalPropagation`. * - Multi-tenant scenarios with destinations maintained in the subscriber account. This case is implied if the `selectionStrategy` is set to `alwaysSubscriber`. - * - IAS token exchange with `iasOptions.authenticationType` set to `'OAuth2JWTBearer'`. In this case, the JWT is automatically used as the assertion for IAS token exchange. + * - IAS business user authentication (OAuth2JWTBearer `authenticationType`). In this case, the JWT is used as the assertion for IAS token exchange. The `authenticationType` is set via the `iasOptions` parameter when used with {@link getDestinationFromServiceBinding}. */ jwt?: string; diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts index 8f1cd9e137..8f5218888e 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts @@ -4,6 +4,7 @@ import { signedJwt } from '../../../../../test-resources/test/test-util'; import * as xsuaaService from '../xsuaa-service'; +import * as identityService from '../identity-service'; import { clientCredentialsTokenCache } from '../client-credentials-token-cache'; import { getDestination } from './destination-accessor'; import { getDestinationFromServiceBinding } from './destination-from-vcap'; @@ -287,6 +288,111 @@ describe('vcap-service-destination', () => { expect.objectContaining({ iasOptions }) ); }); + + describe('IAS service binding', () => { + function mockIasClientCredentialsToken(aud: string) { + const token = signedJwt({ jti: 'some-jti', ias_apis: [], aud }); + const spy = jest + .spyOn(identityService, 'getIasClientCredentialsToken') + .mockResolvedValue({ + access_token: token, + expires_in: 3600, + token_type: 'bearer', + aud, + scope: '' as const, + ias_apis: [], + jti: 'some-jti' + }); + return { token, spy }; + } + + it('creates a destination for IAS service with App2App authentication using resource name', async () => { + const { token: mockToken, spy: getIasTokenSpy } = + mockIasClientCredentialsToken('target-app-name'); + + const destination = await getDestinationFromServiceBinding({ + destinationName: 'my-identity-service', + iasOptions: { + resource: { name: 'target-app-name' }, + targetUrl: 'https://target-app.example.com' + } + }); + + expect(destination).toMatchObject({ + url: 'https://target-app.example.com', + name: 'my-identity-service', + authentication: 'OAuth2ClientCredentials', + authTokens: [ + expect.objectContaining({ + value: mockToken, + type: 'bearer' + }) + ] + }); + + expect(getIasTokenSpy).toHaveBeenCalledWith( + expect.objectContaining({ + name: 'my-identity-service', + label: 'identity' + }), + expect.objectContaining({ + resource: { name: 'target-app-name' } + }) + ); + }); + + it('creates a destination for IAS service with App2App authentication using providerClientId', async () => { + const { token: mockToken, spy: getIasTokenSpy } = + mockIasClientCredentialsToken('target-app-name'); + + const destination = await getDestinationFromServiceBinding({ + destinationName: 'my-identity-service', + iasOptions: { + resource: { + providerClientId: 'provider-client-id', + providerTenantId: 'provider-tenant-id' + }, + targetUrl: 'https://target-app.example.com' + } + }); + + expect(destination).toMatchObject({ + url: 'https://target-app.example.com', + name: 'my-identity-service', + authentication: 'OAuth2ClientCredentials', + authTokens: [ + expect.objectContaining({ + value: mockToken + }) + ] + }); + + expect(getIasTokenSpy).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + resource: { + providerClientId: 'provider-client-id', + providerTenantId: 'provider-tenant-id' + } + }) + ); + }); + + it('uses IAS service URL when targetUrl is not provided', async () => { + mockIasClientCredentialsToken('my-identity-service'); + + const destination = await getDestinationFromServiceBinding({ + destinationName: 'my-identity-service', + iasOptions: { + resource: { name: 'target-app-name' } + } + }); + + expect(destination.url).toBe( + 'https://my-identity-service.accounts.ondemand.com' + ); + }); + }); }); function mockServiceBindings() { @@ -411,5 +517,17 @@ const serviceBindings = { apiurl: 'https://api.authentication.sap.hana.ondemand.com' } } + ], + identity: [ + { + label: 'identity', + name: 'my-identity-service', + tags: ['identity'], + credentials: { + clientid: 'clientIdIdentity', + clientsecret: 'PASSWORD', + url: 'https://my-identity-service.accounts.ondemand.com' + } + } ] }; diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index e77228cfa4..0e22510997 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -8,15 +8,12 @@ import { import { isHttpDestination } from './destination-service-types'; import { serviceToDestinationTransformers } from './service-binding-to-destination'; import { setForwardedAuthTokenIfNeeded } from './forward-auth-token'; -import type { Xor } from '@sap-cloud-sdk/util'; import type { DestinationFetchOptions } from './destination-accessor-types'; -import type { - AuthenticationType, - Destination -} from './destination-service-types'; +import type { Destination } from './destination-service-types'; import type { CachingOptions } from '../cache'; import type { Service } from '../environment-accessor'; import type { JwtPayload } from '../jsonwebtoken-type'; +import type { IasOptions } from './ias-types'; const logger = createLogger({ package: 'connectivity', @@ -101,100 +98,6 @@ export interface DestinationFromServiceBindingOptions { serviceBindingTransformFn?: ServiceBindingTransformFunction; } -/** - * The application resource for which the token is requested for App-to-App communication. - * The token will only be usable to call the requested application. - * Either provide the app name (common case) or the provider client ID - * and tenant ID (optional). - */ -export type IasResource = Xor< - { - /** - * The name of the application resource. - */ - name: string; - }, - { - /** - * The client ID of the application resource. - */ - providerClientId: string; - /** - * The tenant ID of the application resource (Optional). - */ - providerTenantId?: string; - } ->; - -/** - * Base options shared by all IAS authentication modes. - */ -interface IasOptionsBase { - /** - * The target URL of the destination that the IAS token is requested for. - * It is recommended to provide this for App-to-App communication (when resource parameter is used), - * otherwise the destination will point to the identity service URL from the service binding, - * instead of the actual target application. This function is not able to to infer - * the target application from the information available. - * @default The (identity service) URL from the service binding. - */ - targetUrl?: string; - /** - * The application resource(s) for which the token is requested. - * The token will only be usable to call the requested application(s). - * Either provide the app name (common case) or the provider client ID - * and tenant ID (optional). - * - * It is recommended to also provide the targetUrl parameter, otherwise - * the destination will point to the identity service URL from the service bindingm, - * instead of the actual target application. This function is not able to to infer - * the target application from the information available. - */ - resource?: IasResource; - /** - * The consumer (BTP) tenant ID of the application. - * May be required for multi-tenant communication. - */ - appTid?: string; - /** - * Additional parameters to be sent along with the token request. - */ - extraParams?: Record; -} - -/** - * IAS options for technical user authentication (client credentials). - */ -type IasOptionsTechnical = IasOptionsBase & { - /** - * Authentication type. Use 'OAuth2ClientCredentials' for technical user (default). - */ - authenticationType?: Extract; - /** - * Assertion not used for technical user authentication. - */ - assertion?: never; -}; - -/** - * IAS options for business user authentication (JWT bearer). - */ -type IasOptionsBusinessUser = IasOptionsBase & { - /** - * Authentication type. Use 'OAuth2JWTBearer' for business user authentication. - */ - authenticationType: Extract; - /** - * The JWT assertion string to use for business user authentication (required). - */ - assertion: string; -}; - -/** - * Options for IAS token retrieval with type-safe authenticationType/assertion relationship. - */ -export type IasOptions = IasOptionsTechnical | IasOptionsBusinessUser; - /** * Represents options passed to the service binding transform function. */ diff --git a/packages/connectivity/src/scp-cf/destination/ias-types.ts b/packages/connectivity/src/scp-cf/destination/ias-types.ts new file mode 100644 index 0000000000..8d681d4933 --- /dev/null +++ b/packages/connectivity/src/scp-cf/destination/ias-types.ts @@ -0,0 +1,93 @@ +import type { Xor } from '@sap-cloud-sdk/util'; +import type { AuthenticationType } from './destination-service-types'; + +/** + * The application resource for which the token is requested for App-to-App communication. + * The token will only be usable to call the requested application. + * Either provide the app name (common case) or the provider client ID + * and tenant ID (optional). + */ +export type IasResource = Xor< + { + /** + * The name of the application resource. + */ + name: string; + }, + { + /** + * The client ID of the application resource. + */ + providerClientId: string; + /** + * The tenant ID of the application resource (Optional). + */ + providerTenantId?: string; + } +>; + +/** + * Base options shared by all IAS authentication modes. + */ +interface IasOptionsBase { + /** + * The target URL of the destination that the IAS token is requested for. + * It is recommended to provide this for App-to-App communication (when resource parameter is used), + * otherwise the destination will point to the identity service URL from the service binding. + * @default The (identity service) URL from the service binding. + */ + targetUrl?: string; + /** + * The application resource(s) for which the token is requested. + * The token will only be usable to call the requested application(s). + * Either provide the app name (common case) or the provider client ID + * and tenant ID (optional). + * + * It is recommended to also provide the targetUrl parameter, otherwise + * the destination will point to the identity service URL from the service binding, + * instead of the actual target application. + */ + resource?: IasResource; + /** + * The consumer (BTP) tenant ID of the application. + * May be required for multi-tenant communication. + */ + appTid?: string; + /** + * Additional parameters to be sent along with the token request. + */ + extraParams?: Record; +} + +/** + * IAS options for technical user authentication (client credentials). + */ +type IasOptionsTechnical = IasOptionsBase & { + /** + * Authentication type. Use 'OAuth2ClientCredentials' for technical user (default). + */ + authenticationType?: Extract; + /** + * Assertion not used for technical user authentication. + */ + assertion?: never; +}; + +/** + * IAS options for business user authentication (JWT bearer). + */ +type IasOptionsBusinessUser = IasOptionsBase & { + /** + * Authentication type. Use 'OAuth2JWTBearer' for business user authentication. + */ + authenticationType: Extract; + /** + * The JWT assertion string to use for business user authentication (required). + */ + assertion: string; +}; + +/** + * Options for IAS token retrieval with type-safe authenticationType/assertion relationship. + */ +export type IasOptions = IasOptionsTechnical | IasOptionsBusinessUser; diff --git a/packages/connectivity/src/scp-cf/destination/index.ts b/packages/connectivity/src/scp-cf/destination/index.ts index d5d6c17159..24b98cb764 100644 --- a/packages/connectivity/src/scp-cf/destination/index.ts +++ b/packages/connectivity/src/scp-cf/destination/index.ts @@ -14,5 +14,6 @@ export * from './forward-auth-token'; export * from './get-subscriber-token'; export * from './get-provider-token'; export * from './http-proxy-util'; +export * from './ias-types'; export * from './service-binding-to-destination'; export * from './register-destination-cache'; diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 5f11f4ece3..ca62bce470 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -76,6 +76,7 @@ type IasParameters = { * @param options - Options for token fetching, including authenticationType to specify authentication mode, optional resource parameter for app2app, appTid for multi-tenant scenarios, and extraParams for additional OAuth2 parameters. * @returns Client credentials token response. * @internal + * @experimental */ export async function getIasClientCredentialsToken( service: string | Service, diff --git a/packages/connectivity/src/scp-cf/token-accessor.spec.ts b/packages/connectivity/src/scp-cf/token-accessor.spec.ts index df16b909b9..973b0f5fb7 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.spec.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.spec.ts @@ -29,7 +29,11 @@ import { mockUserTokenGrantCall } from '../../../../test-resources/test/test-util/xsuaa-service-mocks'; import { clientCredentialsTokenCache } from './client-credentials-token-cache'; -import { jwtBearerToken, serviceToken } from './token-accessor'; +import { + jwtBearerToken, + serviceToken, + serviceTokenIas +} from './token-accessor'; import { clearXsuaaServices } from './environment-accessor'; import * as identityService from './identity-service'; import type { ClientCredentialsResponse } from './xsuaa-service-types'; @@ -412,20 +416,6 @@ describe('token accessor', () => { jest.restoreAllMocks(); }); - it('uses getIasClientCredentialsToken for identity service', async () => { - const getIasTokenSpy = jest - .spyOn(identityService, 'getIasClientCredentialsToken') - .mockResolvedValue(mockIasToken); - - const token = await serviceToken('identity'); - - expect(token).toBe(mockIasToken.access_token); - expect(getIasTokenSpy).toHaveBeenCalledWith(mockIasService, { - authenticationType: 'OAuth2ClientCredentials', - appTid: mockIasService.credentials.app_tid - }); - }); - it('forwards iasOptions (resource and extraParams) to getIasClientCredentialsToken', async () => { const getIasTokenSpy = jest .spyOn(identityService, 'getIasClientCredentialsToken') @@ -436,7 +426,7 @@ describe('token accessor', () => { extraParams: { custom_param: 'custom_value' } }; - await serviceToken('identity', { iasOptions }); + await serviceTokenIas('identity', { iasOptions }); expect(getIasTokenSpy).toHaveBeenCalledWith( mockIasService, @@ -458,7 +448,7 @@ describe('token accessor', () => { zid: 'subscriber-tenant-id' }); - await serviceToken('identity', { jwt }); + await serviceTokenIas('identity', { jwt }); expect(getIasTokenSpy).toHaveBeenCalledWith( mockIasService, @@ -484,7 +474,7 @@ describe('token accessor', () => { ext_attr: { enhancer: 'XSUAA' } }); - await serviceToken('identity', { jwt: xsuaaJwt, useCache: false }); + await serviceTokenIas('identity', { jwt: xsuaaJwt, useCache: false }); expect(warnSpy).toHaveBeenCalledWith( expect.stringContaining( @@ -505,7 +495,7 @@ describe('token accessor', () => { zid: 'jwt-tenant-id' }); - await serviceToken('identity', { + await serviceTokenIas('identity', { jwt, iasOptions: { appTid: explicitAppTid } }); @@ -527,8 +517,8 @@ describe('token accessor', () => { resource: { providerClientId: 'target-app-client-id' } }; - const first = await serviceToken('identity', { iasOptions }); - const second = await serviceToken('identity', { iasOptions }); + const first = await serviceTokenIas('identity', { iasOptions }); + const second = await serviceTokenIas('identity', { iasOptions }); expect(first).toBe(mockIasToken.access_token); expect(second).toBe(mockIasToken.access_token); @@ -550,7 +540,9 @@ describe('token accessor', () => { const resource1 = { providerClientId: 'app-1' }; const resource2 = { providerClientId: 'app-2' }; - await serviceToken('identity', { iasOptions: { resource: resource1 } }); + await serviceTokenIas('identity', { + iasOptions: { resource: resource1 } + }); const cached1 = clientCredentialsTokenCache.getToken( mockIasService.credentials.app_tid, diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index 447fe0ac44..eb60ea6a52 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -39,7 +39,74 @@ export async function serviceToken( jwt?: string | JwtPayload; iasOptions?: Omit< IasOptions, - 'authenticationType' | 'assertion' | 'destinationUrl' + 'authenticationType' | 'assertion' | 'targetUrl' + >; + } +): Promise { + const opts = { + useCache: true, + enableCircuitBreaker: true, + ...options + }; + + const serviceBinding = resolveServiceBinding(service); + + const serviceCredentials = serviceBinding.credentials; + const tenantForCaching = options?.jwt + ? getTenantId(options.jwt) || getSubdomain(options.jwt) + : getTenantIdFromBinding() || getDefaultTenantId(); + + if (opts.useCache) { + const cachedToken = clientCredentialsTokenCache.getToken( + tenantForCaching, + serviceCredentials.clientid, + undefined + ); + if (cachedToken) { + return cachedToken.access_token; + } + } + + try { + const token = await getClientCredentialsToken(serviceBinding, options?.jwt); + + if (opts.useCache) { + clientCredentialsTokenCache.cacheToken( + tenantForCaching, + serviceCredentials.clientid, + undefined, + token + ); + } + + return token.access_token; + } catch (err) { + throw new ErrorWithCause( + `Could not fetch client credentials token for service of type "${serviceBinding.label}".`, + err + ); + } +} + +/** + * Returns an access token for an IAS (Identity Authentication Service) service binding. + * The token is fetched via a client credentials grant with the credentials of the given service. + * If a JWT is passed, the tenant of the JWT will be used when performing the grant. + * When no JWT is passed, the grant will be performed using the provider tenant. + * + * Throws an error if there is no instance of the given service type or if the request to the IAS service fails. + * @param service - The type of the service or an instance of {@link Service}. + * @param options - Options to influence caching behavior (see {@link CachingOptions}) and IAS-specific options (see {@link IasOptions}). + * @returns Access token. + * @experimental This function is experimental and may change in future versions. + */ +export async function serviceTokenIas( + service: string | Service, + options?: CachingOptions & { + jwt?: string | JwtPayload; + iasOptions?: Omit< + IasOptions, + 'authenticationType' | 'assertion' | 'targetUrl' >; } ): Promise { @@ -52,12 +119,11 @@ export async function serviceToken( const serviceBinding = resolveServiceBinding(service); const serviceCredentials = serviceBinding.credentials; // User-provided appTid takes precedence over automatically determined tenant for IAS - const tenantForCaching = - serviceBinding.label === 'identity' && options?.iasOptions?.appTid - ? options.iasOptions.appTid - : options?.jwt - ? getTenantId(options.jwt) || getSubdomain(options.jwt) - : getTenantIdFromBinding() || getDefaultTenantId(); + const tenantForCaching = options?.iasOptions?.appTid + ? options.iasOptions.appTid + : options?.jwt + ? getTenantId(options.jwt) || getSubdomain(options.jwt) + : getTenantIdFromBinding() || getDefaultTenantId(); const resourceForCaching = options?.iasOptions?.resource; if (opts.useCache) { @@ -73,7 +139,7 @@ export async function serviceToken( try { // Warn if using IAS service with XSUAA jwt (should be more reliable than the IAS check) - if (serviceBinding.label === 'identity' && options?.jwt) { + if (options?.jwt) { const decodedJwt = decodeJwt(options.jwt); if (isXsuaaToken(decodedJwt)) { logger.warn( @@ -84,14 +150,11 @@ export async function serviceToken( } } - const token = - serviceBinding.label === 'identity' - ? await getIasClientCredentialsToken(serviceBinding, { - ...(options?.iasOptions ?? {}), - authenticationType: 'OAuth2ClientCredentials', - appTid: tenantForCaching - }) - : await getClientCredentialsToken(serviceBinding, options?.jwt); + const token = await getIasClientCredentialsToken(serviceBinding, { + ...(options?.iasOptions ?? {}), + authenticationType: 'OAuth2ClientCredentials', + appTid: tenantForCaching + }); if (opts.useCache) { clientCredentialsTokenCache.cacheToken( @@ -105,7 +168,7 @@ export async function serviceToken( return token.access_token; } catch (err) { throw new ErrorWithCause( - `Could not fetch client credentials token for service of type "${serviceBinding.label}".`, + 'Could not fetch client credentials token for IAS service.', err ); } From 5418c338292c0ebbc45bb0fb37e7334938d74941 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 30 Dec 2025 17:17:06 +0100 Subject: [PATCH 37/58] update changelog Co-authored-by: KavithaSiva <32287936+KavithaSiva@users.noreply.github.com> --- .changeset/slow-cars-lie.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/slow-cars-lie.md b/.changeset/slow-cars-lie.md index e4efe3efb9..ac0acd820d 100644 --- a/.changeset/slow-cars-lie.md +++ b/.changeset/slow-cars-lie.md @@ -2,4 +2,4 @@ '@sap-cloud-sdk/connectivity': minor --- -[New Functionality] Support IAS (App-to-App) authentication (experimental). Use `transformServiceBindingToDestination` and `getDestinationFromServiceBinding` to create a destination from an IAS service binding. +[New Functionality] Support IAS (App-to-App) authentication (experimental). Use `transformServiceBindingToDestination()` function or `getDestinationFromServiceBinding()` function to create a destination targeting an IAS application. From 0c67544135ce414da6c38f60196ac1d4d9421ffa Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 30 Dec 2025 17:24:10 +0100 Subject: [PATCH 38/58] address most recent review comments --- packages/connectivity/src/index.ts | 1 - .../client-credentials-token-cache.spec.ts | 172 +------------- .../scp-cf/client-credentials-token-cache.ts | 37 +-- .../src/scp-cf/destination/ias-types.ts | 5 + .../service-binding-to-destination.ts | 8 +- .../src/scp-cf/identity-service.ts | 13 +- .../src/scp-cf/token-accessor.spec.ts | 213 +----------------- .../connectivity/src/scp-cf/token-accessor.ts | 102 +-------- 8 files changed, 40 insertions(+), 511 deletions(-) diff --git a/packages/connectivity/src/index.ts b/packages/connectivity/src/index.ts index 8076b6c875..fc13a95dd0 100644 --- a/packages/connectivity/src/index.ts +++ b/packages/connectivity/src/index.ts @@ -17,7 +17,6 @@ export { retrieveJwt, jwtBearerToken, serviceToken, - serviceTokenIas, isHttpDestination, assertHttpDestination, DestinationSelectionStrategies, diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts index d4843452fb..a9184bbf3d 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts @@ -1,8 +1,5 @@ import { createLogger } from '@sap-cloud-sdk/util'; -import { - clientCredentialsTokenCache, - getCacheKey -} from './client-credentials-token-cache'; +import { clientCredentialsTokenCache } from './client-credentials-token-cache'; const oneHourInSeconds = 60 * 60; @@ -21,7 +18,6 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', - undefined, validToken ); @@ -29,8 +25,7 @@ describe('ClientCredentialsTokenCache', () => { const valid = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - 'clientid', - undefined + 'clientid' ); expect(valid).toEqual(validToken); @@ -50,15 +45,13 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', - undefined, expiredToken ); jest.advanceTimersByTime(oneHourInSeconds * 2 * 1000); const expired = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - 'clientid', - undefined + 'clientid' ); expect(expired).toBeUndefined(); @@ -79,7 +72,6 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', - undefined, validToken ); @@ -87,8 +79,7 @@ describe('ClientCredentialsTokenCache', () => { const invalid = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - undefined as any, - undefined + undefined as any ); expect(invalid).toBeUndefined(); @@ -96,159 +87,4 @@ describe('ClientCredentialsTokenCache', () => { 'Cannot create cache key for client credentials token cache. The given client ID is undefined.' ); }); - - describe('IAS resource parameter support', () => { - const validToken = { - access_token: '1234567890', - token_type: 'Bearer', - expires_in: oneHourInSeconds * 3, - jti: '', - scope: '' - }; - - beforeEach(() => { - clientCredentialsTokenCache.clear(); - }); - - it('should cache and retrieve token with resource name', () => { - const resource = { name: 'my-app' }; - - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resource, - validToken - ); - - const cached = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource - ); - - expect(cached).toEqual(validToken); - }); - - it('should cache and retrieve token with resource clientId', () => { - const resource = { providerClientId: 'resource-client-123' }; - - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resource, - validToken - ); - - const cached = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource - ); - - expect(cached).toEqual(validToken); - }); - - it('should cache and retrieve token with resource clientId and tenantId', () => { - const resource = { - providerClientId: 'resource-client-123', - providerTenantId: 'tenant-456' - }; - - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resource, - validToken - ); - - const cached = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource - ); - - expect(cached).toEqual(validToken); - }); - - it('should isolate cache by resource name', () => { - const resource1 = { name: 'app-1' }; - const resource2 = { name: 'app-2' }; - - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resource1, - validToken - ); - - const cached1 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource1 - ); - const cached2 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource2 - ); - - expect(cached1).toEqual(validToken); - expect(cached2).toBeUndefined(); - }); - - it('should isolate cache by resource clientId', () => { - const resource1 = { providerClientId: 'client-1' }; - const resource2 = { providerClientId: 'client-2' }; - - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resource1, - validToken - ); - - const cached1 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource1 - ); - const cached2 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource2 - ); - - expect(cached1).toEqual(validToken); - expect(cached2).toBeUndefined(); - }); - - it('should generate correct cache key with resource name', () => { - const key = getCacheKey('tenant-123', 'client-id', { name: 'my-app' }); - expect(key).toBe('tenant-123:client-id:name=my-app'); - }); - - it('should generate correct cache key with resource clientId only', () => { - const key = getCacheKey('tenant-123', 'client-id', { - providerClientId: 'resource-client-123' - }); - expect(key).toBe( - 'tenant-123:client-id:provider-clientId=resource-client-123' - ); - }); - - it('should generate correct cache key with resource clientId and tenantId', () => { - const key = getCacheKey('tenant-123', 'client-id', { - providerClientId: 'resource-client-123', - providerTenantId: 'tenant-456' - }); - expect(key).toBe( - 'tenant-123:client-id:provider-clientId=resource-client-123:provider-tenantId=tenant-456' - ); - }); - - it('should generate cache key without resource when not provided', () => { - const key = getCacheKey('tenant-123', 'client-id'); - expect(key).toBe('tenant-123:client-id'); - }); - }); }); diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts index d69cac2d30..398beb70cf 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts @@ -1,7 +1,6 @@ import { createLogger } from '@sap-cloud-sdk/util'; import { Cache } from './cache'; import type { ClientCredentialsResponse } from './xsuaa-service-types'; -import type { IasResource } from './destination'; const logger = createLogger({ package: 'connectivity', @@ -13,18 +12,16 @@ const ClientCredentialsTokenCache = ( ) => ({ getToken: ( tenantId: string | undefined, - clientId: string, - resource?: IasResource + clientId: string ): ClientCredentialsResponse | undefined => - cache.get(getCacheKey(tenantId, clientId, resource)), + cache.get(getCacheKey(tenantId, clientId)), cacheToken: ( tenantId: string | undefined, clientId: string, - resource: IasResource | undefined, token: ClientCredentialsResponse ): void => { - cache.set(getCacheKey(tenantId, clientId, resource), { + cache.set(getCacheKey(tenantId, clientId), { entry: token, expires: token.expires_in ? Date.now() + token.expires_in * 1000 @@ -37,27 +34,6 @@ const ClientCredentialsTokenCache = ( getCacheInstance: () => cache }); -/** - * Normalizes the IAS resource parameter to a consistent string format for cache key. - * @param resource - The resource parameter from iasOptions. - * @returns Normalized resource string or empty string if not provided. - * @internal - */ -function normalizeResource(resource?: IasResource): string | undefined { - if (!resource) { - return undefined; - } - if ('name' in resource) { - return `name=${resource.name}`; - } - - let normalized = `provider-clientId=${resource.providerClientId}`; - if (resource.providerTenantId) { - normalized += `:provider-tenantId=${resource.providerTenantId}`; - } - return normalized; -} - /** * * @internal * @param tenantId - The ID of the tenant to cache the token for. @@ -67,8 +43,7 @@ function normalizeResource(resource?: IasResource): string | undefined { */ export function getCacheKey( tenantId: string | undefined, - clientId: string, - resource?: IasResource + clientId: string ): string | undefined { if (!tenantId) { logger.warn( @@ -83,10 +58,6 @@ export function getCacheKey( return; } const parts = [tenantId, clientId]; - const resourceStr = normalizeResource(resource); - if (resourceStr) { - parts.push(resourceStr); - } return parts.join(':'); } diff --git a/packages/connectivity/src/scp-cf/destination/ias-types.ts b/packages/connectivity/src/scp-cf/destination/ias-types.ts index 8d681d4933..77fa6c9c81 100644 --- a/packages/connectivity/src/scp-cf/destination/ias-types.ts +++ b/packages/connectivity/src/scp-cf/destination/ias-types.ts @@ -53,6 +53,11 @@ interface IasOptionsBase { * May be required for multi-tenant communication. */ appTid?: string; + /** + * Specifies whether the token request is made in the context of the current tenant or the provider tenant. + * @default 'current-tenant' + */ + requestAs?: 'current-tenant' | 'provider-tenant'; /** * Additional parameters to be sent along with the token request. */ diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index 744566bffb..8bc0f96d76 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -186,10 +186,10 @@ async function iasBindingToDestination( service: Service, options?: ServiceBindingTransformOptions ): Promise { - const { access_token } = await getIasClientCredentialsToken( - service, - options?.iasOptions ?? {} - ); + const { access_token } = await getIasClientCredentialsToken(service, { + jwt: options?.jwt, + ...(options?.iasOptions || {}) + }); const destination = buildClientCredentialsDestination( access_token, options?.iasOptions?.targetUrl ?? service.credentials.url, diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index ca62bce470..94bb4cc536 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -15,6 +15,7 @@ import type { import type { MiddlewareContext } from '@sap-cloud-sdk/resilience'; import type { Service, ServiceCredentials } from './environment-accessor'; import type { ClientCredentialsResponse } from './xsuaa-service-types'; +import type { JwtPayload } from './jsonwebtoken-type'; export { clearIdentityServices } from './environment-accessor'; @@ -66,6 +67,7 @@ export function shouldExchangeToken(options: DestinationOptions): boolean { } type IasParameters = { + jwt?: JwtPayload; serviceCredentials: ServiceCredentials; } & IasOptions; @@ -80,7 +82,7 @@ type IasParameters = { */ export async function getIasClientCredentialsToken( service: string | Service, - options: IasOptions = {} + options: IasOptions & { jwt?: JwtPayload } = {} ): Promise { const resolvedService = resolveServiceBinding(service); @@ -198,6 +200,15 @@ async function getIasClientCredentialsTokenImpl( tokenOptions ); } else { + if (!arg.appTid) { + const requestAs = arg?.requestAs ?? 'current-tenant'; + if (requestAs === 'provider-tenant') { + tokenOptions.app_tid = arg.serviceCredentials.tenantid; + } else if (requestAs === 'current-tenant') { + tokenOptions.app_tid = arg.jwt?.app_tid; + } + } + // Client credentials for technical users response = await identityService.fetchClientCredentialsToken(tokenOptions); } diff --git a/packages/connectivity/src/scp-cf/token-accessor.spec.ts b/packages/connectivity/src/scp-cf/token-accessor.spec.ts index 973b0f5fb7..deb0fd0dfe 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.spec.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.spec.ts @@ -1,6 +1,5 @@ import nock from 'nock'; import * as resilience from '@sap-cloud-sdk/resilience'; -import { createLogger } from '@sap-cloud-sdk/util'; import { destinationBindingClientSecretMock, destinationBindingCertMock, @@ -29,13 +28,8 @@ import { mockUserTokenGrantCall } from '../../../../test-resources/test/test-util/xsuaa-service-mocks'; import { clientCredentialsTokenCache } from './client-credentials-token-cache'; -import { - jwtBearerToken, - serviceToken, - serviceTokenIas -} from './token-accessor'; +import { jwtBearerToken, serviceToken } from './token-accessor'; import { clearXsuaaServices } from './environment-accessor'; -import * as identityService from './identity-service'; import type { ClientCredentialsResponse } from './xsuaa-service-types'; describe('token accessor', () => { @@ -211,13 +205,11 @@ describe('token accessor', () => { const providerTokenFromCache = clientCredentialsTokenCache.getToken( providerUserPayload.zid, - destinationBindingClientSecretMock.credentials.clientid, - undefined + destinationBindingClientSecretMock.credentials.clientid ); const subscriberTokenFromCache = clientCredentialsTokenCache.getToken( subscriberUserPayload.zid, - destinationBindingClientSecretMock.credentials.clientid, - undefined + destinationBindingClientSecretMock.credentials.clientid ); expect(providerTokenFromCache?.access_token).toEqual(providerToken); @@ -226,33 +218,23 @@ describe('token accessor', () => { expect( clientCredentialsTokenCache.getToken( 'https://doesnotexist.example.com', - destinationBindingClientSecretMock.credentials.clientid, - undefined + destinationBindingClientSecretMock.credentials.clientid ) ).toBeUndefined(); expect( clientCredentialsTokenCache.getToken( 'https://doesnotexist.example.com', - 'schmusername', - undefined + 'schmusername' ) ).toBeUndefined(); expect( - clientCredentialsTokenCache.getToken( - providerXsuaaUrl, - 'schmusername', - undefined - ) + clientCredentialsTokenCache.getToken(providerXsuaaUrl, 'schmusername') ).toBeUndefined(); expect( - clientCredentialsTokenCache.getToken( - subscriberXsuaaUrl, - 'schmusername', - undefined - ) + clientCredentialsTokenCache.getToken(subscriberXsuaaUrl, 'schmusername') ).toBeUndefined(); }); @@ -338,7 +320,6 @@ describe('token accessor', () => { clientCredentialsTokenCache.cacheToken( destinationBindingClientSecretMock.credentials.tenantid, destinationBindingClientSecretMock.credentials.clientid, - undefined, { access_token: token } as ClientCredentialsResponse ); @@ -367,8 +348,7 @@ describe('token accessor', () => { expect( clientCredentialsTokenCache.getToken( destinationBindingClientSecretMock.credentials.tenantid, - destinationBindingClientSecretMock.credentials.clientid, - undefined + destinationBindingClientSecretMock.credentials.clientid ) ).toEqual(token); }); @@ -382,182 +362,5 @@ describe('token accessor', () => { '"Could not find service binding of type \'destination\'."' ); }); - - describe('IAS/identity service handling', () => { - const mockIasService = { - name: 'my-identity', - label: 'identity', - tags: ['identity'], - credentials: { - url: 'https://tenant.accounts.ondemand.com', - clientid: 'ias-client-id', - clientsecret: 'ias-secret', - app_tid: 'ias-tenant-id' - } - }; - - const mockIasToken = { - access_token: signedJwt({ jti: 'ias-jti', ias_apis: ['test'] }), - token_type: 'Bearer', - expires_in: 3600, - scope: '' as const, - jti: 'ias-jti', - aud: [], - ias_apis: ['test'] - }; - - beforeEach(() => { - process.env.VCAP_SERVICES = JSON.stringify({ - identity: [mockIasService] - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('forwards iasOptions (resource and extraParams) to getIasClientCredentialsToken', async () => { - const getIasTokenSpy = jest - .spyOn(identityService, 'getIasClientCredentialsToken') - .mockResolvedValue(mockIasToken); - - const iasOptions = { - resource: { providerClientId: 'target-app-client-id' }, - extraParams: { custom_param: 'custom_value' } - }; - - await serviceTokenIas('identity', { iasOptions }); - - expect(getIasTokenSpy).toHaveBeenCalledWith( - mockIasService, - expect.objectContaining({ - resource: iasOptions.resource, - extraParams: iasOptions.extraParams, - authenticationType: 'OAuth2ClientCredentials', - appTid: mockIasService.credentials.app_tid - }) - ); - }); - - it('uses tenant from JWT as appTid when JWT is provided', async () => { - const getIasTokenSpy = jest - .spyOn(identityService, 'getIasClientCredentialsToken') - .mockResolvedValue(mockIasToken); - - const jwt = signedXsuaaJwt({ - zid: 'subscriber-tenant-id' - }); - - await serviceTokenIas('identity', { jwt }); - - expect(getIasTokenSpy).toHaveBeenCalledWith( - mockIasService, - expect.objectContaining({ - appTid: 'subscriber-tenant-id' - }) - ); - }); - - it('logs warning when using IAS service with XSUAA JWT', async () => { - const logger = createLogger({ - package: 'connectivity', - messageContext: 'token-accessor' - }); - const warnSpy = jest.spyOn(logger, 'warn'); - - jest - .spyOn(identityService, 'getIasClientCredentialsToken') - .mockResolvedValue(mockIasToken); - - const xsuaaJwt = signedXsuaaJwt({ - zid: 'unique-subscriber-tenant-id', - ext_attr: { enhancer: 'XSUAA' } - }); - - await serviceTokenIas('identity', { jwt: xsuaaJwt, useCache: false }); - - expect(warnSpy).toHaveBeenCalledWith( - expect.stringContaining( - 'Requesting token for IAS service with a XSUAA JWT' - ) - ); - - warnSpy.mockRestore(); - }); - - it('uses explicitly provided appTid over automatically determined tenant', async () => { - const getIasTokenSpy = jest - .spyOn(identityService, 'getIasClientCredentialsToken') - .mockResolvedValue(mockIasToken); - - const explicitAppTid = 'explicit-tenant-id'; - const jwt = signedXsuaaJwt({ - zid: 'jwt-tenant-id' - }); - - await serviceTokenIas('identity', { - jwt, - iasOptions: { appTid: explicitAppTid } - }); - - expect(getIasTokenSpy).toHaveBeenCalledWith( - mockIasService, - expect.objectContaining({ - appTid: explicitAppTid // Should use explicit value, not jwt-tenant-id - }) - ); - }); - - it('caches IAS tokens with resource parameter', async () => { - jest - .spyOn(identityService, 'getIasClientCredentialsToken') - .mockResolvedValue(mockIasToken); - - const iasOptions = { - resource: { providerClientId: 'target-app-client-id' } - }; - - const first = await serviceTokenIas('identity', { iasOptions }); - const second = await serviceTokenIas('identity', { iasOptions }); - - expect(first).toBe(mockIasToken.access_token); - expect(second).toBe(mockIasToken.access_token); - - const cached = clientCredentialsTokenCache.getToken( - mockIasService.credentials.app_tid, - mockIasService.credentials.clientid, - iasOptions.resource - ); - - expect(cached?.access_token).toBe(mockIasToken.access_token); - }); - - it('isolates cache by IAS resource parameter', async () => { - jest - .spyOn(identityService, 'getIasClientCredentialsToken') - .mockResolvedValue(mockIasToken); - - const resource1 = { providerClientId: 'app-1' }; - const resource2 = { providerClientId: 'app-2' }; - - await serviceTokenIas('identity', { - iasOptions: { resource: resource1 } - }); - - const cached1 = clientCredentialsTokenCache.getToken( - mockIasService.credentials.app_tid, - mockIasService.credentials.clientid, - resource1 - ); - const cached2 = clientCredentialsTokenCache.getToken( - mockIasService.credentials.app_tid, - mockIasService.credentials.clientid, - resource2 - ); - - expect(cached1).toBeDefined(); - expect(cached2).toBeUndefined(); - }); - }); }); }); diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index eb60ea6a52..b022817f86 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -1,26 +1,18 @@ -import { createLogger, ErrorWithCause } from '@sap-cloud-sdk/util'; +import { ErrorWithCause } from '@sap-cloud-sdk/util'; import { - decodeJwt, getDefaultTenantId, getSubdomain, getTenantId, - getTenantIdFromBinding, - isXsuaaToken + getTenantIdFromBinding } from './jwt'; import { clientCredentialsTokenCache } from './client-credentials-token-cache'; import { resolveServiceBinding } from './environment-accessor'; import { getClientCredentialsToken, getUserToken } from './xsuaa-service'; -import { getIasClientCredentialsToken } from './identity-service'; import type { Service } from './environment-accessor'; import type { CachingOptions } from './cache'; import type { JwtPayload } from './jsonwebtoken-type'; import type { IasOptions } from './destination'; -const logger = createLogger({ - package: 'connectivity', - messageContext: 'token-accessor' -}); - /** * Returns an access token that can be used to call the given service. The token is fetched via a client credentials grant with the credentials of the given service. * If multiple instances of the provided service exist, the first instance will be selected. @@ -59,8 +51,7 @@ export async function serviceToken( if (opts.useCache) { const cachedToken = clientCredentialsTokenCache.getToken( tenantForCaching, - serviceCredentials.clientid, - undefined + serviceCredentials.clientid ); if (cachedToken) { return cachedToken.access_token; @@ -74,7 +65,6 @@ export async function serviceToken( clientCredentialsTokenCache.cacheToken( tenantForCaching, serviceCredentials.clientid, - undefined, token ); } @@ -88,92 +78,6 @@ export async function serviceToken( } } -/** - * Returns an access token for an IAS (Identity Authentication Service) service binding. - * The token is fetched via a client credentials grant with the credentials of the given service. - * If a JWT is passed, the tenant of the JWT will be used when performing the grant. - * When no JWT is passed, the grant will be performed using the provider tenant. - * - * Throws an error if there is no instance of the given service type or if the request to the IAS service fails. - * @param service - The type of the service or an instance of {@link Service}. - * @param options - Options to influence caching behavior (see {@link CachingOptions}) and IAS-specific options (see {@link IasOptions}). - * @returns Access token. - * @experimental This function is experimental and may change in future versions. - */ -export async function serviceTokenIas( - service: string | Service, - options?: CachingOptions & { - jwt?: string | JwtPayload; - iasOptions?: Omit< - IasOptions, - 'authenticationType' | 'assertion' | 'targetUrl' - >; - } -): Promise { - const opts = { - useCache: true, - enableCircuitBreaker: true, - ...options - }; - - const serviceBinding = resolveServiceBinding(service); - const serviceCredentials = serviceBinding.credentials; - // User-provided appTid takes precedence over automatically determined tenant for IAS - const tenantForCaching = options?.iasOptions?.appTid - ? options.iasOptions.appTid - : options?.jwt - ? getTenantId(options.jwt) || getSubdomain(options.jwt) - : getTenantIdFromBinding() || getDefaultTenantId(); - const resourceForCaching = options?.iasOptions?.resource; - - if (opts.useCache) { - const cachedToken = clientCredentialsTokenCache.getToken( - tenantForCaching, - serviceCredentials.clientid, - resourceForCaching - ); - if (cachedToken) { - return cachedToken.access_token; - } - } - - try { - // Warn if using IAS service with XSUAA jwt (should be more reliable than the IAS check) - if (options?.jwt) { - const decodedJwt = decodeJwt(options.jwt); - if (isXsuaaToken(decodedJwt)) { - logger.warn( - 'Requesting token for IAS service with a XSUAA JWT. ' + - 'The tenant information from the XSUAA token may not be compatible with IAS. ' + - 'Consider using an IAS JWT or omitting the JWT to use the provider tenant.' - ); - } - } - - const token = await getIasClientCredentialsToken(serviceBinding, { - ...(options?.iasOptions ?? {}), - authenticationType: 'OAuth2ClientCredentials', - appTid: tenantForCaching - }); - - if (opts.useCache) { - clientCredentialsTokenCache.cacheToken( - tenantForCaching, - serviceCredentials.clientid, - resourceForCaching, - token - ); - } - - return token.access_token; - } catch (err) { - throw new ErrorWithCause( - 'Could not fetch client credentials token for IAS service.', - err - ); - } -} - /** * Returns a JWT bearer token that can be used to call the given service. * The token is fetched via a JWT bearer token grant using the user token + client credentials. From 189a52987de45cd1e22fba2c2e6ca04ab9f6e705 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 5 Jan 2026 13:24:31 +0100 Subject: [PATCH 39/58] Apply suggestions from code review Co-authored-by: Marika Marszalkowski --- .../destination/destination-accessor-types.ts | 2 +- .../src/scp-cf/destination/ias-types.ts | 14 +++++++------- .../src/scp-cf/environment-accessor/ias.ts | 6 +++--- .../connectivity/src/scp-cf/identity-service.ts | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts b/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts index e8d90186ee..633fa1ed4a 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts @@ -51,7 +51,7 @@ export interface DestinationAccessorOptions { * ATTENTION: The property is mandatory in the following cases: * - User-dependent authentication flow is used, e.g., `OAuth2UserTokenExchange`, `OAuth2JWTBearer`, `OAuth2SAMLBearerAssertion`, `SAMLAssertion` or `PrincipalPropagation`. * - Multi-tenant scenarios with destinations maintained in the subscriber account. This case is implied if the `selectionStrategy` is set to `alwaysSubscriber`. - * - IAS business user authentication (OAuth2JWTBearer `authenticationType`). In this case, the JWT is used as the assertion for IAS token exchange. The `authenticationType` is set via the `iasOptions` parameter when used with {@link getDestinationFromServiceBinding}. + * - IAS business user authentication (OAuth2JWTBearer). In this case, the JWT is used as the assertion for the IAS token exchange. The authentication type is set via the `iasOptions` parameter when used with {@link getDestinationFromServiceBinding}. */ jwt?: string; diff --git a/packages/connectivity/src/scp-cf/destination/ias-types.ts b/packages/connectivity/src/scp-cf/destination/ias-types.ts index 77fa6c9c81..b3d2ccf289 100644 --- a/packages/connectivity/src/scp-cf/destination/ias-types.ts +++ b/packages/connectivity/src/scp-cf/destination/ias-types.ts @@ -5,7 +5,7 @@ import type { AuthenticationType } from './destination-service-types'; * The application resource for which the token is requested for App-to-App communication. * The token will only be usable to call the requested application. * Either provide the app name (common case) or the provider client ID - * and tenant ID (optional). + * and tenant ID. */ export type IasResource = Xor< { @@ -20,7 +20,7 @@ export type IasResource = Xor< */ providerClientId: string; /** - * The tenant ID of the application resource (Optional). + * The tenant ID of the application resource. */ providerTenantId?: string; } @@ -41,9 +41,9 @@ interface IasOptionsBase { * The application resource(s) for which the token is requested. * The token will only be usable to call the requested application(s). * Either provide the app name (common case) or the provider client ID - * and tenant ID (optional). + * and tenant ID. * - * It is recommended to also provide the targetUrl parameter, otherwise + * It is recommended to also provide the `targetUrl` parameter, otherwise * the destination will point to the identity service URL from the service binding, * instead of the actual target application. */ @@ -55,7 +55,7 @@ interface IasOptionsBase { appTid?: string; /** * Specifies whether the token request is made in the context of the current tenant or the provider tenant. - * @default 'current-tenant' + * @defaultValue 'current-tenant' */ requestAs?: 'current-tenant' | 'provider-tenant'; /** @@ -87,12 +87,12 @@ type IasOptionsBusinessUser = IasOptionsBase & { */ authenticationType: Extract; /** - * The JWT assertion string to use for business user authentication (required). + * The JWT assertion string to use for business user authentication. */ assertion: string; }; /** - * Options for IAS token retrieval with type-safe authenticationType/assertion relationship. + * Options for IAS token retrieval with type-safe authentication type/assertion relationship. */ export type IasOptions = IasOptionsTechnical | IasOptionsBusinessUser; diff --git a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts index 9bae71f49d..d47da0f744 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts @@ -6,7 +6,7 @@ const identityServices: Record = {}; /** * @internal - * Clears the cache of Identity services. + * Clears the cache of identity services. * Should only be used for testing purposes. */ export function clearIdentityServices(): void { @@ -23,9 +23,9 @@ function tryParseUrl(url: string, name: string): URL { /** * @internal - * @param credentials - Identity service credentials extracted from a service binding or re-use service. Required to create the xssec IdentityService instance. + * @param credentials - Identity service credentials extracted from a service binding or re-use service. Required to create the xssec `IdentityService` instance. * @param assertion - Optional JWT assertion to extract the issuer URL for bearer assertion flows. - * @param disableCache - Value to enable or disable JWKS cache in xssec library. Defaults to false. + * @param disableCache - Value to enable or disable JWKS cache in the xssec library. Defaults to false. * @returns An instance of {@code @sap/xssec/IdentityService} for the provided credentials. */ export function getIdentityServiceInstanceFromCredentials( diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 94bb4cc536..56d5b73875 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -122,7 +122,7 @@ export async function getIasClientCredentialsToken( } /** - * Converts an IasResource to the URN format expected by @sap/xssec. + * Converts an IAS resource to the URN format expected by @sap/xssec. * @param resource - The IAS resource to convert. * @returns The resource in URN format. * @internal From 74e8bbf0dbf2d82af25d05e012b6ada98732553c Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 5 Jan 2026 17:38:42 +0100 Subject: [PATCH 40/58] address review comments --- packages/connectivity/src/index.ts | 3 + .../scp-cf/client-credentials-token-cache.ts | 3 +- ...ccessor-provider-subscriber-lookup.spec.ts | 2 + .../destination/destination-accessor-types.ts | 2 +- .../destination/destination-from-vcap.ts | 1 - .../src/scp-cf/destination/ias-types.ts | 15 ++-- .../scp-cf/environment-accessor/ias.spec.ts | 4 +- .../src/scp-cf/environment-accessor/ias.ts | 73 ++++++++----------- .../src/scp-cf/environment-accessor/xsuaa.ts | 2 +- .../src/scp-cf/identity-service.spec.ts | 26 ++++--- .../src/scp-cf/identity-service.ts | 6 +- .../src/scp-cf/subdomain-replacer.ts | 44 +++++++++-- .../connectivity/src/scp-cf/token-accessor.ts | 6 -- packages/eslint-config/index.js | 11 ++- 14 files changed, 115 insertions(+), 83 deletions(-) diff --git a/packages/connectivity/src/index.ts b/packages/connectivity/src/index.ts index fc13a95dd0..1ca8bdbf23 100644 --- a/packages/connectivity/src/index.ts +++ b/packages/connectivity/src/index.ts @@ -60,6 +60,9 @@ export type { DestinationFromServiceBindingOptions, ServiceBindingTransformOptions, IasOptions, + IasOptionsBase, + IasOptionsBusinessUser, + IasOptionsTechnicalUser, IasResource } from './scp-cf'; diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts index 398beb70cf..575b6a1b94 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts @@ -37,8 +37,7 @@ const ClientCredentialsTokenCache = ( /** * * @internal * @param tenantId - The ID of the tenant to cache the token for. - * @param clientId - ClientId to fetch the token. - * @param resource - Optional resource parameter (for IAS app2app scenarios). + * @param clientId - Client ID to fetch the token. * @returns The cache key. */ export function getCacheKey( diff --git a/packages/connectivity/src/scp-cf/destination/destination-accessor-provider-subscriber-lookup.spec.ts b/packages/connectivity/src/scp-cf/destination/destination-accessor-provider-subscriber-lookup.spec.ts index c354a4fd47..072cef2e1a 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-accessor-provider-subscriber-lookup.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-accessor-provider-subscriber-lookup.spec.ts @@ -27,6 +27,7 @@ import { import { mockServiceToken } from '../../../../../test-resources/test/test-util/token-accessor-mocks'; import * as tokenAccessor from '../token-accessor'; import { decodeJwt } from '../jwt'; +import { identityServicesCache } from '../environment-accessor'; import { parseDestination } from './destination'; import { getAllDestinationsFromDestinationService, @@ -171,6 +172,7 @@ describe('JWT type and selection strategies', () => { afterEach(() => { nock.cleanAll(); jest.clearAllMocks(); + identityServicesCache.clear(); }); describe('user token', () => { diff --git a/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts b/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts index 633fa1ed4a..e5979a372d 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-accessor-types.ts @@ -51,7 +51,7 @@ export interface DestinationAccessorOptions { * ATTENTION: The property is mandatory in the following cases: * - User-dependent authentication flow is used, e.g., `OAuth2UserTokenExchange`, `OAuth2JWTBearer`, `OAuth2SAMLBearerAssertion`, `SAMLAssertion` or `PrincipalPropagation`. * - Multi-tenant scenarios with destinations maintained in the subscriber account. This case is implied if the `selectionStrategy` is set to `alwaysSubscriber`. - * - IAS business user authentication (OAuth2JWTBearer). In this case, the JWT is used as the assertion for the IAS token exchange. The authentication type is set via the `iasOptions` parameter when used with {@link getDestinationFromServiceBinding}. + * - IAS business user authentication (OAuth2JWTBearer). In this case, the JWT is used as the assertion for the IAS token exchange. The authentication type is set via the `iasOptions` parameter when used with {@link getDestinationFromServiceBinding}. */ jwt?: string; diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts index 0e22510997..b53b42f3b5 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts @@ -25,7 +25,6 @@ const logger = createLogger({ * Throws an error if no services are bound at all, no service with the given name can be found, or the service type is not supported. * The last error can be circumvent by using the second parameter to provide a custom function that transforms a service binding to a destination. * @param options - Options to customize the behavior of this function. - * @param options.iasOptions - Options for IAS token retrieval in case of IAS authentication. * @returns A destination. */ export async function getDestinationFromServiceBinding( diff --git a/packages/connectivity/src/scp-cf/destination/ias-types.ts b/packages/connectivity/src/scp-cf/destination/ias-types.ts index b3d2ccf289..a610c83109 100644 --- a/packages/connectivity/src/scp-cf/destination/ias-types.ts +++ b/packages/connectivity/src/scp-cf/destination/ias-types.ts @@ -29,7 +29,7 @@ export type IasResource = Xor< /** * Base options shared by all IAS authentication modes. */ -interface IasOptionsBase { +export interface IasOptionsBase { /** * The target URL of the destination that the IAS token is requested for. * It is recommended to provide this for App-to-App communication (when resource parameter is used), @@ -67,21 +67,22 @@ interface IasOptionsBase { /** * IAS options for technical user authentication (client credentials). */ -type IasOptionsTechnical = IasOptionsBase & { +export interface IasOptionsTechnicalUser extends IasOptionsBase { /** - * Authentication type. Use 'OAuth2ClientCredentials' for technical user (default). + * Authentication type. Use 'OAuth2ClientCredentials' for technical users. + * @defaultValue 'OAuth2ClientCredentials' */ authenticationType?: Extract; /** * Assertion not used for technical user authentication. */ assertion?: never; -}; +} /** * IAS options for business user authentication (JWT bearer). */ -type IasOptionsBusinessUser = IasOptionsBase & { +export interface IasOptionsBusinessUser extends IasOptionsBase { /** * Authentication type. Use 'OAuth2JWTBearer' for business user authentication. */ @@ -90,9 +91,9 @@ type IasOptionsBusinessUser = IasOptionsBase & { * The JWT assertion string to use for business user authentication. */ assertion: string; -}; +} /** * Options for IAS token retrieval with type-safe authentication type/assertion relationship. */ -export type IasOptions = IasOptionsTechnical | IasOptionsBusinessUser; +export type IasOptions = IasOptionsTechnicalUser | IasOptionsBusinessUser; diff --git a/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts b/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts index b9636ded0e..dbaff6ec40 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/ias.spec.ts @@ -1,5 +1,5 @@ import { - clearIdentityServices, + identityServicesCache, getIdentityServiceInstanceFromCredentials } from './ias'; import type { ServiceCredentials } from './environment-accessor-types'; @@ -7,7 +7,7 @@ import type { ServiceCredentials } from './environment-accessor-types'; describe('ias', () => { describe('getIdentityServiceInstanceFromCredentials()', () => { afterEach(() => { - clearIdentityServices(); + identityServicesCache.clear(); }); it('creates a new service instance', () => { diff --git a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts index d47da0f744..c6dcbd1ce2 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts @@ -1,32 +1,27 @@ import { IdentityService, IdentityServiceToken } from '@sap/xssec'; -import { ErrorWithCause } from '@sap-cloud-sdk/util'; +import { createLogger } from '@sap-cloud-sdk/util'; +import { getIssuerSubdomain, replaceSubdomain } from '../subdomain-replacer'; import type { IdentityServiceCredentials } from './environment-accessor-types'; +import type { JwtPayload } from '../jsonwebtoken-type'; -const identityServices: Record = {}; +const logger = createLogger({ + package: 'connectivity', + messageContext: 'ias' +}); /** * @internal - * Clears the cache of identity services. + * A cache for IdentityService instances. * Should only be used for testing purposes. */ -export function clearIdentityServices(): void { - Object.keys(identityServices).forEach(key => delete identityServices[key]); -} - -function tryParseUrl(url: string, name: string): URL { - try { - return new URL(url); - } catch (err) { - throw new ErrorWithCause(`Could not parse ${name} URL: ${url}`, err); - } -} +export const identityServicesCache: Map = new Map(); /** * @internal * @param credentials - Identity service credentials extracted from a service binding or re-use service. Required to create the xssec `IdentityService` instance. * @param assertion - Optional JWT assertion to extract the issuer URL for bearer assertion flows. * @param disableCache - Value to enable or disable JWKS cache in the xssec library. Defaults to false. - * @returns An instance of {@code @sap/xssec/IdentityService} for the provided credentials. + * @returns An instance of {@link @sap/xssec/IdentityService} for the provided credentials. */ export function getIdentityServiceInstanceFromCredentials( credentials: IdentityServiceCredentials, @@ -46,37 +41,33 @@ export function getIdentityServiceInstanceFromCredentials( let subdomain: string | undefined; if (assertion) { + // Use `IdentityServiceToken` to take advantage of xssec JWT-decoding cache const decodedJwt = new IdentityServiceToken(assertion); - const issuer = decodedJwt.issuer; - const issuerUrl = tryParseUrl(issuer, 'JWT assertion issuer'); - subdomain = issuerUrl.hostname.split('.')[0]; - // Replace subdomain in the URL from the service binding - // Reason: We don't want to blindly trust the URL in the assertion - const credentialsUrl = tryParseUrl(credentials.url, 'Identity Service'); - const credentialsSplit = credentialsUrl.hostname.split('.'); - credentialsUrl.hostname = [subdomain, ...credentialsSplit.slice(1)].join( - '.' - ); - let normalizedUrl = credentialsUrl.toString(); - if (normalizedUrl.endsWith('/')) { - normalizedUrl = normalizedUrl.slice(0, -1); + const payload = decodedJwt.payload satisfies JwtPayload; + // For IAS tokens, prefer ias_iss claim over standard iss claim + subdomain = getIssuerSubdomain(payload, true); + if (subdomain) { + // Replace subdomain in the URL from the service binding + // Reason: We don't want to blindly trust the URL in the assertion + credentials = { + ...credentials, + url: replaceSubdomain(credentials.url, subdomain) + }; + } else { + logger.warn( + 'Could not extract subdomain from JWT assertion issuer. Falling back to service binding URL.' + ); } - credentials = { - ...credentials, - url: normalizedUrl - }; } - subdomain = - subdomain ?? - tryParseUrl(credentials.url, 'Identity Service').hostname.split('.')[0]; + subdomain = subdomain ?? getIssuerSubdomain({ iss: credentials.url }); const cacheKey = `${credentials.clientid}:${subdomain}:${disableCache}`; - if (!identityServices[cacheKey]) { - identityServices[cacheKey] = new IdentityService( - credentials, - serviceConfig - ); + // TODO: Use Map.prototype.getOrInsertComputed() when available + let identityService = identityServicesCache.get(cacheKey); + if (identityService === undefined) { + identityService = new IdentityService(credentials, serviceConfig); + identityServicesCache.set(cacheKey, identityService); } - return identityServices[cacheKey]; + return identityService; } diff --git a/packages/connectivity/src/scp-cf/environment-accessor/xsuaa.ts b/packages/connectivity/src/scp-cf/environment-accessor/xsuaa.ts index 1e461510f3..87bb961948 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/xsuaa.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/xsuaa.ts @@ -19,7 +19,7 @@ export function clearXsuaaServices(): void { * @internal * @param credentials - Xsuaa credentials extracted from a re-use service like destination service. Required to create the xssec XSUAA instance. * @param disableCache - Value to enable or disable JWKS cache in xssec library. Defaults to false. - * @returns An instance of {@code @sap/xssec/XsuaaService} for the provided credentials. + * @returns An instance of {@link @sap/xssec/XsuaaService} for the provided credentials. */ export function getXsuaaInstanceFromServiceCredentials( credentials: ServiceCredentials, diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 17ec7789d1..8aa63fab7b 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -2,7 +2,7 @@ import { signedJwt } from '../../../../test-resources/test/test-util'; import { getIasClientCredentialsToken, shouldExchangeToken, - clearIdentityServices + identityServicesCache } from './identity-service'; import type { Service } from './environment-accessor'; @@ -25,7 +25,7 @@ jest.mock('@sap/xssec', () => { Buffer.from(jwt.split('.')[1], 'base64').toString() ); return { - getPayload: () => payload, + payload, appTid: payload.app_tid ?? payload.zone_uuid, scimId: payload.scim_id, consumedApis: payload.ias_apis, @@ -106,7 +106,7 @@ describe('getIasClientCredentialsToken', () => { beforeEach(() => { jest.clearAllMocks(); - clearIdentityServices(); + identityServicesCache.clear(); }); it('fetches IAS token with mTLS authentication', async () => { @@ -329,7 +329,7 @@ describe('getIasClientCredentialsToken', () => { beforeEach(() => { jest.clearAllMocks(); - clearIdentityServices(); + identityServicesCache.clear(); mockFetchJwtBearerToken.mockResolvedValue(mockTokenResponse); }); @@ -394,18 +394,20 @@ describe('getIasClientCredentialsToken', () => { ); }); - it('throws error when JWT issuer extraction fails', async () => { + it('handles JWT without issuer gracefully with fallback', async () => { const assertion = signedJwt({ - // Missing issuer field to trigger error + // Missing issuer field - should fall back to provider URL user_uuid: 'user-123' }); - await expect( - getIasClientCredentialsToken(providerService, { - authenticationType: 'OAuth2JWTBearer', - assertion - }) - ).rejects.toThrow('Could not parse JWT assertion issuer URL: undefined'); + const result = await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion + }); + + // Should succeed with fallback to provider credentials + expect(result.access_token).toBeDefined(); + expect(mockFetchJwtBearerToken).toHaveBeenCalled(); }); it('caches subscriber instances per URL', async () => { diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 56d5b73875..6ef459aa23 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -17,7 +17,7 @@ import type { Service, ServiceCredentials } from './environment-accessor'; import type { ClientCredentialsResponse } from './xsuaa-service-types'; import type { JwtPayload } from './jsonwebtoken-type'; -export { clearIdentityServices } from './environment-accessor'; +export { identityServicesCache } from './environment-accessor'; /** * @internal @@ -221,9 +221,9 @@ async function getIasClientCredentialsTokenImpl( expires_in: response.expires_in, // IAS tokens don't have scope property scope: '', - jti: decodedJwt.getPayload()?.jti ?? '', + jti: decodedJwt.payload?.jti ?? '', // `decodedJwt.audiences` always returns an array, preserve original type - aud: decodedJwt.getPayload()?.aud ?? [], + aud: decodedJwt.payload?.aud ?? [], app_tid: decodedJwt.appTid, scimId: decodedJwt.scimId, // Added if resource parameter was specified diff --git a/packages/connectivity/src/scp-cf/subdomain-replacer.ts b/packages/connectivity/src/scp-cf/subdomain-replacer.ts index 9567098a23..4b42223766 100644 --- a/packages/connectivity/src/scp-cf/subdomain-replacer.ts +++ b/packages/connectivity/src/scp-cf/subdomain-replacer.ts @@ -5,14 +5,18 @@ import type { JwtPayload } from './jsonwebtoken-type'; * @internal */ export function getIssuerSubdomain( - decodedJwt: JwtPayload | undefined + decodedJwt: JwtPayload | undefined, + isIasToken: boolean = false ): string | undefined { - const iss = decodedJwt?.iss; - if (iss) { - if (!isValidUrl(iss)) { - throw new Error(`Issuer URL in JWT is not a valid URL: "${iss}".`); + // For IAS tokens, prefer ias_iss claim over standard iss claim + const issuer = + isIasToken && decodedJwt?.ias_iss ? decodedJwt.ias_iss : decodedJwt?.iss; + + if (issuer) { + if (!isValidUrl(issuer)) { + throw new Error(`Issuer URL in JWT is not a valid URL: "${issuer}".`); } - return getHost(new URL(iss)).split('.')[0]; + return getHost(new URL(issuer)).split('.')[0]; } } @@ -34,3 +38,31 @@ function isValidUrl(url: string): boolean { return false; } } + +/** + * @internal + * Replaces the first part of the hostname (subdomain) in a URL. + * @param baseUrl - The URL whose subdomain should be replaced. + * @param newSubdomain - The new subdomain to use. + * @returns The URL with replaced subdomain, with trailing slash removed if present. + */ +export function replaceSubdomain( + baseUrl: string, + newSubdomain: string +): string { + if (!isValidUrl(baseUrl)) { + throw new Error(`Base URL is not a valid URL: "${baseUrl}".`); + } + + const url = new URL(baseUrl); + const hostParts = getHost(url).split('.'); + url.hostname = [newSubdomain, ...hostParts.slice(1)].join('.'); + + let result = url.toString(); + // Remove trailing slash for consistency + if (result.endsWith('/')) { + result = result.slice(0, -1); + } + + return result; +} diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index b022817f86..80d9d369c9 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -11,7 +11,6 @@ import { getClientCredentialsToken, getUserToken } from './xsuaa-service'; import type { Service } from './environment-accessor'; import type { CachingOptions } from './cache'; import type { JwtPayload } from './jsonwebtoken-type'; -import type { IasOptions } from './destination'; /** * Returns an access token that can be used to call the given service. The token is fetched via a client credentials grant with the credentials of the given service. @@ -22,17 +21,12 @@ import type { IasOptions } from './destination'; * Throws an error if there is no instance of the given service type or the XSUAA service, or if the request to the XSUAA service fails. * @param service - The type of the service or an instance of {@link Service}. * @param options - Options to influence caching behavior (see {@link CachingOptions}) and a JWT. By default, caching and usage of a circuit breaker are enabled. - * @param options.iasOptions - Options to change IAS token fetching (see {@link IasOptions}). * @returns Access token. */ export async function serviceToken( service: string | Service, options?: CachingOptions & { jwt?: string | JwtPayload; - iasOptions?: Omit< - IasOptions, - 'authenticationType' | 'assertion' | 'targetUrl' - >; } ): Promise { const opts = { diff --git a/packages/eslint-config/index.js b/packages/eslint-config/index.js index 4766b9b2a6..b5758600fd 100644 --- a/packages/eslint-config/index.js +++ b/packages/eslint-config/index.js @@ -220,7 +220,16 @@ module.exports = { 'jsdoc/check-param-names': 'error', 'jsdoc/check-tag-names': [ 'error', - { definedTags: ['packageDocumentation', 'typeParam', 'experimental'] } + { + definedTags: [ + 'packageDocumentation', + 'typeParam', + 'experimental', + 'defaultValue' + ], + // The other default-allowed tags are not supported by tsdoc + inlineTags: ['link'] + } ], 'jsdoc/check-syntax': 'error', 'jsdoc/multiline-blocks': 'error', From 63d8074af96574d234b21e15dabce8723d6ec44e Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 6 Jan 2026 08:40:03 +0100 Subject: [PATCH 41/58] chore: improve `identityServicesCache` comment --- packages/connectivity/src/scp-cf/environment-accessor/ias.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts index c6dcbd1ce2..f16c8b13b5 100644 --- a/packages/connectivity/src/scp-cf/environment-accessor/ias.ts +++ b/packages/connectivity/src/scp-cf/environment-accessor/ias.ts @@ -11,8 +11,8 @@ const logger = createLogger({ /** * @internal - * A cache for IdentityService instances. - * Should only be used for testing purposes. + * A cache for `IdentityService` instances. + * Direct access from outside this module outside tests is discouraged. */ export const identityServicesCache: Map = new Map(); From 9fb61ee251359cfea27c087ff6186f9b0a386120 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 6 Jan 2026 10:10:40 +0100 Subject: [PATCH 42/58] chore: add requestAs tests & minor changes --- .../src/scp-cf/identity-service.spec.ts | 92 ++++++++++++++++++- .../src/scp-cf/identity-service.ts | 8 +- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 8aa63fab7b..a8e3bace31 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -124,7 +124,7 @@ describe('getIasClientCredentialsToken', () => { app_tid: 'custom-tenant-id', custom_iss: 'https://tenant.accounts.ondemand.com', ias_apis: ['dummy'], - scimId: undefined + scim_id: undefined }); expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ token_format: 'jwt' @@ -312,6 +312,96 @@ describe('getIasClientCredentialsToken', () => { }); }); + describe('requestAs behavior', () => { + const providerTenantId = 'provider-tenant-id-123'; + + const serviceWithProviderTenant: Service = { + ...mockIasService, + credentials: { + ...mockIasService.credentials, + app_tid: providerTenantId + } + }; + + beforeEach(() => { + jest.clearAllMocks(); + identityServicesCache.clear(); + mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); + }); + + it('defaults to current tenant and forwards jwt.app_tid when provided', async () => { + await getIasClientCredentialsToken(serviceWithProviderTenant, { + jwt: { app_tid: 'current-tenant-app-tid' } + }); + + expect(mockFetchClientCredentialsToken).toHaveBeenCalled(); + const callArg = mockFetchClientCredentialsToken.mock.calls[0][0]; + expect(callArg).toEqual( + expect.objectContaining({ + token_format: 'jwt', + app_tid: 'current-tenant-app-tid' + }) + ); + }); + + it("uses provider tenant when requestAs is 'provider-tenant'", async () => { + await getIasClientCredentialsToken(serviceWithProviderTenant, { + requestAs: 'provider-tenant' + }); + + expect(mockFetchClientCredentialsToken).toHaveBeenCalled(); + const callArg = mockFetchClientCredentialsToken.mock.calls[0][0]; + expect(callArg).toEqual( + expect.objectContaining({ + token_format: 'jwt', + app_tid: providerTenantId + }) + ); + }); + + it('prioritizes explicit appTid over requestAs', async () => { + await getIasClientCredentialsToken(serviceWithProviderTenant, { + requestAs: 'provider-tenant', + appTid: 'explicit-tenant-789' + }); + + const callArg = mockFetchClientCredentialsToken.mock.calls[0][0]; + expect(callArg).toEqual( + expect.objectContaining({ + token_format: 'jwt', + app_tid: 'explicit-tenant-789' + }) + ); + }); + + it('ignores requestAs for JWT bearer flow', async () => { + mockFetchJwtBearerToken.mockResolvedValue(mockTokenResponse); + + const userAssertion = signedJwt({ + iss: 'https://tenant.accounts.ondemand.com', + user_uuid: 'user-123' + }); + + await getIasClientCredentialsToken(mockIasService, { + authenticationType: 'OAuth2JWTBearer', + assertion: userAssertion, + requestAs: 'provider-tenant' + }); + + expect(mockFetchJwtBearerToken).toHaveBeenCalled(); + const [, tokenOptions] = mockFetchJwtBearerToken.mock.calls[0]; + expect(tokenOptions).toEqual({ token_format: 'jwt' }); + }); + + it("does not set app_tid when requestAs is 'current-tenant' and jwt is missing", async () => { + await getIasClientCredentialsToken(serviceWithProviderTenant, { + requestAs: 'current-tenant' + }); + + const callArg = mockFetchClientCredentialsToken.mock.calls[0][0]; + expect(callArg).toEqual({ token_format: 'jwt' }); + }); + }); describe('multi-tenant subscriber routing', () => { const providerUrl = 'https://provider.accounts.ondemand.com'; const subscriberUrl = 'https://subscriber.accounts.ondemand.com'; diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 6ef459aa23..abd85a7d67 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -36,7 +36,7 @@ export interface IasClientCredentialsResponse extends ClientCredentialsResponse /** * The SCIM ID of the user (not present for technical user tokens). */ - scimId?: string; + scim_id?: string; /** * Custom issuer claim from the JWT token. */ @@ -100,7 +100,7 @@ export async function getIasClientCredentialsToken( fnArgument, context: { uri: fnArgument.serviceCredentials.url, - tenantId: fnArgument.serviceCredentials.tenantid + tenantId: fnArgument.serviceCredentials.app_tid } }).catch(err => { const serviceName = @@ -203,7 +203,7 @@ async function getIasClientCredentialsTokenImpl( if (!arg.appTid) { const requestAs = arg?.requestAs ?? 'current-tenant'; if (requestAs === 'provider-tenant') { - tokenOptions.app_tid = arg.serviceCredentials.tenantid; + tokenOptions.app_tid = arg.serviceCredentials.app_tid; } else if (requestAs === 'current-tenant') { tokenOptions.app_tid = arg.jwt?.app_tid; } @@ -225,7 +225,7 @@ async function getIasClientCredentialsTokenImpl( // `decodedJwt.audiences` always returns an array, preserve original type aud: decodedJwt.payload?.aud ?? [], app_tid: decodedJwt.appTid, - scimId: decodedJwt.scimId, + scim_id: decodedJwt.scimId, // Added if resource parameter was specified ias_apis: decodedJwt?.consumedApis, custom_iss: decodedJwt.customIssuer ?? undefined From 60105f17e6b879c94e2918bfb5c2bddaefdd5dac Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 6 Jan 2026 16:03:02 +0100 Subject: [PATCH 43/58] yarn generate --- .../Airlines.d.ts | 2 +- .../Airlines.js | 2 +- .../Airlines.ts | 2 +- .../AirlinesApi.d.ts | 2 +- .../AirlinesApi.js | 2 +- .../AirlinesApi.ts | 2 +- .../AirlinesRequestBuilder.d.ts | 2 +- .../AirlinesRequestBuilder.js | 2 +- .../AirlinesRequestBuilder.ts | 2 +- .../AirportLocation.d.ts | 2 +- .../AirportLocation.js | 2 +- .../AirportLocation.ts | 2 +- .../Airports.d.ts | 2 +- .../Airports.js | 2 +- .../Airports.ts | 2 +- .../AirportsApi.d.ts | 2 +- .../AirportsApi.js | 2 +- .../AirportsApi.ts | 2 +- .../AirportsRequestBuilder.d.ts | 2 +- .../AirportsRequestBuilder.js | 2 +- .../AirportsRequestBuilder.ts | 2 +- .../BatchRequest.d.ts | 2 +- .../BatchRequest.js | 2 +- .../BatchRequest.ts | 2 +- .../City.d.ts | 2 +- .../City.js | 2 +- .../City.ts | 2 +- .../EventLocation.d.ts | 2 +- .../EventLocation.js | 2 +- .../EventLocation.ts | 2 +- .../Location.d.ts | 2 +- .../Location.js | 2 +- .../Location.ts | 2 +- .../People.d.ts | 2 +- .../People.js | 2 +- .../People.ts | 2 +- .../PeopleApi.d.ts | 2 +- .../PeopleApi.js | 2 +- .../PeopleApi.ts | 2 +- .../PeopleRequestBuilder.d.ts | 2 +- .../PeopleRequestBuilder.js | 2 +- .../PeopleRequestBuilder.ts | 2 +- .../PersonGender.d.ts | 2 +- .../PersonGender.js | 2 +- .../PersonGender.ts | 2 +- .../Photos.d.ts | 2 +- .../Photos.js | 2 +- .../Photos.ts | 2 +- .../PhotosApi.d.ts | 2 +- .../PhotosApi.js | 2 +- .../PhotosApi.ts | 2 +- .../PhotosRequestBuilder.d.ts | 2 +- .../PhotosRequestBuilder.ts | 2 +- .../index.d.ts | 2 +- .../index.js | 2 +- .../index.ts | 2 +- .../operations.d.ts | 2 +- .../operations.js | 2 +- .../operations.ts | 2 +- .../service.d.ts | 2 +- .../service.js | 2 +- .../service.ts | 2 +- .../v4/test-service/BatchRequest.d.ts | 2 +- .../v4/test-service/BatchRequest.js | 2 +- .../v4/test-service/BatchRequest.ts | 2 +- .../v4/test-service/MyComplexReturnType.d.ts | 2 +- .../v4/test-service/MyComplexReturnType.js | 2 +- .../v4/test-service/MyComplexReturnType.ts | 2 +- .../v4/test-service/TestEntity.d.ts | 2 +- .../v4/test-service/TestEntity.js | 2 +- .../v4/test-service/TestEntity.ts | 2 +- .../v4/test-service/TestEntity50Prop.d.ts | 2 +- .../v4/test-service/TestEntity50Prop.js | 2 +- .../v4/test-service/TestEntity50Prop.ts | 2 +- .../v4/test-service/TestEntity50PropApi.d.ts | 2 +- .../v4/test-service/TestEntity50PropApi.js | 2 +- .../v4/test-service/TestEntity50PropApi.ts | 2 +- .../TestEntity50PropRequestBuilder.d.ts | 2 +- .../TestEntity50PropRequestBuilder.js | 2 +- .../TestEntity50PropRequestBuilder.ts | 2 +- .../v4/test-service/TestEntityApi.d.ts | 2 +- .../v4/test-service/TestEntityApi.js | 2 +- .../v4/test-service/TestEntityApi.ts | 2 +- .../v4/test-service/TestEntityLink.d.ts | 2 +- .../v4/test-service/TestEntityLink.js | 2 +- .../v4/test-service/TestEntityLink.ts | 2 +- .../v4/test-service/TestEntityLinkApi.d.ts | 2 +- .../v4/test-service/TestEntityLinkApi.js | 2 +- .../v4/test-service/TestEntityLinkApi.ts | 2 +- .../TestEntityLinkRequestBuilder.d.ts | 2 +- .../test-service/TestEntityLinkRequestBuilder.js | 2 +- .../test-service/TestEntityLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityRequestBuilder.d.ts | 2 +- .../v4/test-service/TestEntityRequestBuilder.js | 2 +- .../v4/test-service/TestEntityRequestBuilder.ts | 2 +- .../test-service/TestEntityWithMultipleKeys.d.ts | 2 +- .../test-service/TestEntityWithMultipleKeys.js | 2 +- .../test-service/TestEntityWithMultipleKeys.ts | 2 +- .../TestEntityWithMultipleKeysApi.d.ts | 2 +- .../TestEntityWithMultipleKeysApi.js | 2 +- .../TestEntityWithMultipleKeysApi.ts | 2 +- ...TestEntityWithMultipleKeysRequestBuilder.d.ts | 2 +- .../TestEntityWithMultipleKeysRequestBuilder.js | 2 +- .../TestEntityWithMultipleKeysRequestBuilder.ts | 2 +- .../test-services-e2e/v4/test-service/index.d.ts | 2 +- .../test-services-e2e/v4/test-service/index.js | 2 +- .../test-services-e2e/v4/test-service/index.ts | 2 +- .../v4/test-service/operations.d.ts | 2 +- .../v4/test-service/operations.js | 2 +- .../v4/test-service/operations.ts | 2 +- .../v4/test-service/service.d.ts | 2 +- .../test-services-e2e/v4/test-service/service.js | 2 +- .../test-services-e2e/v4/test-service/service.ts | 2 +- .../test-services-odata-common/common-entity.ts | 16 ++++++++-------- .../multiple-schemas-service/BatchRequest.d.ts | 2 +- .../multiple-schemas-service/BatchRequest.js | 2 +- .../multiple-schemas-service/BatchRequest.ts | 2 +- .../MultiSchemaTestEntity.d.ts | 2 +- .../MultiSchemaTestEntity.js | 2 +- .../MultiSchemaTestEntity.ts | 2 +- .../MultiSchemaTestEntityApi.d.ts | 2 +- .../MultiSchemaTestEntityApi.js | 2 +- .../MultiSchemaTestEntityApi.ts | 2 +- .../MultiSchemaTestEntityRequestBuilder.d.ts | 2 +- .../MultiSchemaTestEntityRequestBuilder.js | 2 +- .../MultiSchemaTestEntityRequestBuilder.ts | 2 +- .../multiple-schemas-service/index.d.ts | 2 +- .../multiple-schemas-service/index.js | 2 +- .../multiple-schemas-service/index.ts | 2 +- .../multiple-schemas-service/service.d.ts | 2 +- .../multiple-schemas-service/service.js | 2 +- .../multiple-schemas-service/service.ts | 2 +- .../test-service/BatchRequest.d.ts | 2 +- .../test-service/BatchRequest.js | 2 +- .../test-service/BatchRequest.ts | 2 +- .../test-service/CaseTest.d.ts | 2 +- .../test-service/CaseTest.js | 2 +- .../test-service/CaseTest.ts | 2 +- .../test-service/CaseTestApi.d.ts | 2 +- .../test-service/CaseTestApi.js | 2 +- .../test-service/CaseTestApi.ts | 2 +- .../test-service/CaseTestRequestBuilder.d.ts | 2 +- .../test-service/CaseTestRequestBuilder.js | 2 +- .../test-service/CaseTestRequestBuilder.ts | 2 +- .../test-service/Casetest_1.d.ts | 2 +- .../test-service/Casetest_1.js | 2 +- .../test-service/Casetest_1.ts | 2 +- .../test-service/Casetest_1Api.d.ts | 2 +- .../test-service/Casetest_1Api.js | 2 +- .../test-service/Casetest_1Api.ts | 2 +- .../test-service/Casetest_1RequestBuilder.d.ts | 2 +- .../test-service/Casetest_1RequestBuilder.js | 2 +- .../test-service/Casetest_1RequestBuilder.ts | 2 +- .../test-service/TestComplexType.d.ts | 2 +- .../test-service/TestComplexType.js | 2 +- .../test-service/TestComplexType.ts | 2 +- .../test-service/TestEntity.d.ts | 2 +- .../test-service/TestEntity.js | 2 +- .../test-service/TestEntity.ts | 2 +- .../test-service/TestEntityApi.d.ts | 2 +- .../test-service/TestEntityApi.js | 2 +- .../test-service/TestEntityApi.ts | 2 +- .../TestEntityCircularLinkChild.d.ts | 2 +- .../test-service/TestEntityCircularLinkChild.js | 2 +- .../test-service/TestEntityCircularLinkChild.ts | 2 +- .../TestEntityCircularLinkChildApi.d.ts | 2 +- .../TestEntityCircularLinkChildApi.js | 2 +- .../TestEntityCircularLinkChildApi.ts | 2 +- ...estEntityCircularLinkChildRequestBuilder.d.ts | 2 +- .../TestEntityCircularLinkChildRequestBuilder.js | 2 +- .../TestEntityCircularLinkChildRequestBuilder.ts | 2 +- .../TestEntityCircularLinkParent.d.ts | 2 +- .../test-service/TestEntityCircularLinkParent.js | 2 +- .../test-service/TestEntityCircularLinkParent.ts | 2 +- .../TestEntityCircularLinkParentApi.d.ts | 2 +- .../TestEntityCircularLinkParentApi.js | 2 +- .../TestEntityCircularLinkParentApi.ts | 2 +- ...stEntityCircularLinkParentRequestBuilder.d.ts | 2 +- ...TestEntityCircularLinkParentRequestBuilder.js | 2 +- ...TestEntityCircularLinkParentRequestBuilder.ts | 2 +- .../test-service/TestEntityEndsWith.d.ts | 2 +- .../test-service/TestEntityEndsWith.js | 2 +- .../test-service/TestEntityEndsWith.ts | 2 +- .../test-service/TestEntityEndsWithApi.d.ts | 2 +- .../test-service/TestEntityEndsWithApi.js | 2 +- .../test-service/TestEntityEndsWithApi.ts | 2 +- .../TestEntityEndsWithRequestBuilder.d.ts | 2 +- .../TestEntityEndsWithRequestBuilder.js | 2 +- .../TestEntityEndsWithRequestBuilder.ts | 2 +- .../TestEntityEndsWithSomethingElse.d.ts | 2 +- .../TestEntityEndsWithSomethingElse.js | 2 +- .../TestEntityEndsWithSomethingElse.ts | 2 +- .../TestEntityEndsWithSomethingElseApi.d.ts | 2 +- .../TestEntityEndsWithSomethingElseApi.js | 2 +- .../TestEntityEndsWithSomethingElseApi.ts | 2 +- ...ntityEndsWithSomethingElseRequestBuilder.d.ts | 2 +- ...tEntityEndsWithSomethingElseRequestBuilder.js | 2 +- ...tEntityEndsWithSomethingElseRequestBuilder.ts | 2 +- .../test-service/TestEntityLvl2MultiLink.d.ts | 2 +- .../test-service/TestEntityLvl2MultiLink.js | 2 +- .../test-service/TestEntityLvl2MultiLink.ts | 2 +- .../test-service/TestEntityLvl2MultiLinkApi.d.ts | 2 +- .../test-service/TestEntityLvl2MultiLinkApi.js | 2 +- .../test-service/TestEntityLvl2MultiLinkApi.ts | 2 +- .../TestEntityLvl2MultiLinkRequestBuilder.d.ts | 2 +- .../TestEntityLvl2MultiLinkRequestBuilder.js | 2 +- .../TestEntityLvl2MultiLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityLvl2SingleLink.d.ts | 2 +- .../test-service/TestEntityLvl2SingleLink.js | 2 +- .../test-service/TestEntityLvl2SingleLink.ts | 2 +- .../TestEntityLvl2SingleLinkApi.d.ts | 2 +- .../test-service/TestEntityLvl2SingleLinkApi.js | 2 +- .../test-service/TestEntityLvl2SingleLinkApi.ts | 2 +- .../TestEntityLvl2SingleLinkRequestBuilder.d.ts | 2 +- .../TestEntityLvl2SingleLinkRequestBuilder.js | 2 +- .../TestEntityLvl2SingleLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityMultiLink.d.ts | 2 +- .../test-service/TestEntityMultiLink.js | 2 +- .../test-service/TestEntityMultiLink.ts | 2 +- .../test-service/TestEntityMultiLinkApi.d.ts | 2 +- .../test-service/TestEntityMultiLinkApi.js | 2 +- .../test-service/TestEntityMultiLinkApi.ts | 2 +- .../TestEntityMultiLinkRequestBuilder.d.ts | 2 +- .../TestEntityMultiLinkRequestBuilder.js | 2 +- .../TestEntityMultiLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityOtherMultiLink.d.ts | 2 +- .../test-service/TestEntityOtherMultiLink.js | 2 +- .../test-service/TestEntityOtherMultiLink.ts | 2 +- .../TestEntityOtherMultiLinkApi.d.ts | 2 +- .../test-service/TestEntityOtherMultiLinkApi.js | 2 +- .../test-service/TestEntityOtherMultiLinkApi.ts | 2 +- .../TestEntityOtherMultiLinkRequestBuilder.d.ts | 2 +- .../TestEntityOtherMultiLinkRequestBuilder.js | 2 +- .../TestEntityOtherMultiLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityRequestBuilder.d.ts | 2 +- .../test-service/TestEntityRequestBuilder.js | 2 +- .../test-service/TestEntityRequestBuilder.ts | 2 +- .../test-service/TestEntitySingleLink.d.ts | 2 +- .../test-service/TestEntitySingleLink.js | 2 +- .../test-service/TestEntitySingleLink.ts | 2 +- .../test-service/TestEntitySingleLinkApi.d.ts | 2 +- .../test-service/TestEntitySingleLinkApi.js | 2 +- .../test-service/TestEntitySingleLinkApi.ts | 2 +- .../TestEntitySingleLinkRequestBuilder.d.ts | 2 +- .../TestEntitySingleLinkRequestBuilder.js | 2 +- .../TestEntitySingleLinkRequestBuilder.ts | 2 +- .../TestEntityWithSharedEntityType1.d.ts | 2 +- .../TestEntityWithSharedEntityType1.js | 2 +- .../TestEntityWithSharedEntityType1.ts | 2 +- .../TestEntityWithSharedEntityType1Api.d.ts | 2 +- .../TestEntityWithSharedEntityType1Api.js | 2 +- .../TestEntityWithSharedEntityType1Api.ts | 2 +- ...ntityWithSharedEntityType1RequestBuilder.d.ts | 2 +- ...tEntityWithSharedEntityType1RequestBuilder.js | 2 +- ...tEntityWithSharedEntityType1RequestBuilder.ts | 2 +- .../TestEntityWithSharedEntityType2.d.ts | 2 +- .../TestEntityWithSharedEntityType2.js | 2 +- .../TestEntityWithSharedEntityType2.ts | 2 +- .../TestEntityWithSharedEntityType2Api.d.ts | 2 +- .../TestEntityWithSharedEntityType2Api.js | 2 +- .../TestEntityWithSharedEntityType2Api.ts | 2 +- ...ntityWithSharedEntityType2RequestBuilder.d.ts | 2 +- ...tEntityWithSharedEntityType2RequestBuilder.js | 2 +- ...tEntityWithSharedEntityType2RequestBuilder.ts | 2 +- .../test-service/TestLvl2NestedComplexType.d.ts | 2 +- .../test-service/TestLvl2NestedComplexType.js | 2 +- .../test-service/TestLvl2NestedComplexType.ts | 2 +- .../test-service/TestNestedComplexType.d.ts | 2 +- .../test-service/TestNestedComplexType.js | 2 +- .../test-service/TestNestedComplexType.ts | 2 +- .../test-service/index.d.ts | 2 +- .../test-services-odata-v2/test-service/index.js | 2 +- .../test-services-odata-v2/test-service/index.ts | 2 +- .../test-service/operations.d.ts | 2 +- .../test-service/operations.js | 2 +- .../test-service/operations.ts | 2 +- .../test-service/service.d.ts | 2 +- .../test-service/service.js | 2 +- .../test-service/service.ts | 2 +- .../multiple-schemas-service/BatchRequest.d.ts | 2 +- .../multiple-schemas-service/BatchRequest.js | 2 +- .../multiple-schemas-service/BatchRequest.ts | 2 +- .../TestComplexType1.d.ts | 2 +- .../multiple-schemas-service/TestComplexType1.js | 2 +- .../multiple-schemas-service/TestComplexType1.ts | 2 +- .../TestComplexType2.d.ts | 2 +- .../multiple-schemas-service/TestComplexType2.js | 2 +- .../multiple-schemas-service/TestComplexType2.ts | 2 +- .../multiple-schemas-service/TestEntity1.d.ts | 2 +- .../multiple-schemas-service/TestEntity1.js | 2 +- .../multiple-schemas-service/TestEntity1.ts | 2 +- .../multiple-schemas-service/TestEntity1Api.d.ts | 2 +- .../multiple-schemas-service/TestEntity1Api.js | 2 +- .../multiple-schemas-service/TestEntity1Api.ts | 2 +- .../TestEntity1RequestBuilder.d.ts | 2 +- .../TestEntity1RequestBuilder.js | 2 +- .../TestEntity1RequestBuilder.ts | 2 +- .../multiple-schemas-service/TestEntity2.d.ts | 2 +- .../multiple-schemas-service/TestEntity2.js | 2 +- .../multiple-schemas-service/TestEntity2.ts | 2 +- .../multiple-schemas-service/TestEntity2Api.d.ts | 2 +- .../multiple-schemas-service/TestEntity2Api.js | 2 +- .../multiple-schemas-service/TestEntity2Api.ts | 2 +- .../TestEntity2RequestBuilder.d.ts | 2 +- .../TestEntity2RequestBuilder.js | 2 +- .../TestEntity2RequestBuilder.ts | 2 +- .../multiple-schemas-service/TestEntity3.d.ts | 2 +- .../multiple-schemas-service/TestEntity3.js | 2 +- .../multiple-schemas-service/TestEntity3.ts | 2 +- .../multiple-schemas-service/TestEntity3Api.d.ts | 2 +- .../multiple-schemas-service/TestEntity3Api.js | 2 +- .../multiple-schemas-service/TestEntity3Api.ts | 2 +- .../TestEntity3RequestBuilder.d.ts | 2 +- .../TestEntity3RequestBuilder.js | 2 +- .../TestEntity3RequestBuilder.ts | 2 +- .../multiple-schemas-service/TestEntity4.d.ts | 2 +- .../multiple-schemas-service/TestEntity4.js | 2 +- .../multiple-schemas-service/TestEntity4.ts | 2 +- .../multiple-schemas-service/TestEntity4Api.d.ts | 2 +- .../multiple-schemas-service/TestEntity4Api.js | 2 +- .../multiple-schemas-service/TestEntity4Api.ts | 2 +- .../TestEntity4RequestBuilder.d.ts | 2 +- .../TestEntity4RequestBuilder.js | 2 +- .../TestEntity4RequestBuilder.ts | 2 +- .../multiple-schemas-service/TestEnumType1.d.ts | 2 +- .../multiple-schemas-service/TestEnumType1.js | 2 +- .../multiple-schemas-service/TestEnumType1.ts | 2 +- .../multiple-schemas-service/TestEnumType2.d.ts | 2 +- .../multiple-schemas-service/TestEnumType2.js | 2 +- .../multiple-schemas-service/TestEnumType2.ts | 2 +- .../multiple-schemas-service/index.d.ts | 2 +- .../multiple-schemas-service/index.js | 2 +- .../multiple-schemas-service/index.ts | 2 +- .../multiple-schemas-service/operations.d.ts | 2 +- .../multiple-schemas-service/operations.js | 2 +- .../multiple-schemas-service/operations.ts | 2 +- .../multiple-schemas-service/service.d.ts | 2 +- .../multiple-schemas-service/service.js | 2 +- .../multiple-schemas-service/service.ts | 2 +- .../test-service/BatchRequest.d.ts | 2 +- .../test-service/BatchRequest.js | 2 +- .../test-service/BatchRequest.ts | 2 +- .../test-service/TestComplexBaseType.d.ts | 2 +- .../test-service/TestComplexBaseType.js | 2 +- .../test-service/TestComplexBaseType.ts | 2 +- .../test-service/TestComplexType.d.ts | 2 +- .../test-service/TestComplexType.js | 2 +- .../test-service/TestComplexType.ts | 2 +- .../test-service/TestEntity.d.ts | 2 +- .../test-service/TestEntity.js | 2 +- .../test-service/TestEntity.ts | 2 +- .../test-service/TestEntityApi.d.ts | 2 +- .../test-service/TestEntityApi.js | 2 +- .../test-service/TestEntityApi.ts | 2 +- .../TestEntityCircularLinkChild.d.ts | 2 +- .../test-service/TestEntityCircularLinkChild.js | 2 +- .../test-service/TestEntityCircularLinkChild.ts | 2 +- .../TestEntityCircularLinkChildApi.d.ts | 2 +- .../TestEntityCircularLinkChildApi.js | 2 +- .../TestEntityCircularLinkChildApi.ts | 2 +- ...estEntityCircularLinkChildRequestBuilder.d.ts | 2 +- .../TestEntityCircularLinkChildRequestBuilder.js | 2 +- .../TestEntityCircularLinkChildRequestBuilder.ts | 2 +- .../TestEntityCircularLinkParent.d.ts | 2 +- .../test-service/TestEntityCircularLinkParent.js | 2 +- .../test-service/TestEntityCircularLinkParent.ts | 2 +- .../TestEntityCircularLinkParentApi.d.ts | 2 +- .../TestEntityCircularLinkParentApi.js | 2 +- .../TestEntityCircularLinkParentApi.ts | 2 +- ...stEntityCircularLinkParentRequestBuilder.d.ts | 2 +- ...TestEntityCircularLinkParentRequestBuilder.js | 2 +- ...TestEntityCircularLinkParentRequestBuilder.ts | 2 +- .../test-service/TestEntityEndsWith.d.ts | 2 +- .../test-service/TestEntityEndsWith.js | 2 +- .../test-service/TestEntityEndsWith.ts | 2 +- .../test-service/TestEntityEndsWithApi.d.ts | 2 +- .../test-service/TestEntityEndsWithApi.js | 2 +- .../test-service/TestEntityEndsWithApi.ts | 2 +- .../TestEntityEndsWithRequestBuilder.d.ts | 2 +- .../TestEntityEndsWithRequestBuilder.js | 2 +- .../TestEntityEndsWithRequestBuilder.ts | 2 +- .../TestEntityEndsWithSomethingElse.d.ts | 2 +- .../TestEntityEndsWithSomethingElse.js | 2 +- .../TestEntityEndsWithSomethingElse.ts | 2 +- .../TestEntityEndsWithSomethingElseApi.d.ts | 2 +- .../TestEntityEndsWithSomethingElseApi.js | 2 +- .../TestEntityEndsWithSomethingElseApi.ts | 2 +- ...ntityEndsWithSomethingElseRequestBuilder.d.ts | 2 +- ...tEntityEndsWithSomethingElseRequestBuilder.js | 2 +- ...tEntityEndsWithSomethingElseRequestBuilder.ts | 2 +- .../test-service/TestEntityLvl2MultiLink.d.ts | 2 +- .../test-service/TestEntityLvl2MultiLink.js | 2 +- .../test-service/TestEntityLvl2MultiLink.ts | 2 +- .../test-service/TestEntityLvl2MultiLinkApi.d.ts | 2 +- .../test-service/TestEntityLvl2MultiLinkApi.js | 2 +- .../test-service/TestEntityLvl2MultiLinkApi.ts | 2 +- .../TestEntityLvl2MultiLinkRequestBuilder.d.ts | 2 +- .../TestEntityLvl2MultiLinkRequestBuilder.js | 2 +- .../TestEntityLvl2MultiLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityLvl2SingleLink.d.ts | 2 +- .../test-service/TestEntityLvl2SingleLink.js | 2 +- .../test-service/TestEntityLvl2SingleLink.ts | 2 +- .../TestEntityLvl2SingleLinkApi.d.ts | 2 +- .../test-service/TestEntityLvl2SingleLinkApi.js | 2 +- .../test-service/TestEntityLvl2SingleLinkApi.ts | 2 +- .../TestEntityLvl2SingleLinkRequestBuilder.d.ts | 2 +- .../TestEntityLvl2SingleLinkRequestBuilder.js | 2 +- .../TestEntityLvl2SingleLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityLvl3MultiLink.d.ts | 2 +- .../test-service/TestEntityLvl3MultiLink.js | 2 +- .../test-service/TestEntityLvl3MultiLink.ts | 2 +- .../test-service/TestEntityLvl3MultiLinkApi.d.ts | 2 +- .../test-service/TestEntityLvl3MultiLinkApi.js | 2 +- .../test-service/TestEntityLvl3MultiLinkApi.ts | 2 +- .../TestEntityLvl3MultiLinkRequestBuilder.d.ts | 2 +- .../TestEntityLvl3MultiLinkRequestBuilder.js | 2 +- .../TestEntityLvl3MultiLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityMultiLink.d.ts | 2 +- .../test-service/TestEntityMultiLink.js | 2 +- .../test-service/TestEntityMultiLink.ts | 2 +- .../test-service/TestEntityMultiLinkApi.d.ts | 2 +- .../test-service/TestEntityMultiLinkApi.js | 2 +- .../test-service/TestEntityMultiLinkApi.ts | 2 +- .../TestEntityMultiLinkRequestBuilder.d.ts | 2 +- .../TestEntityMultiLinkRequestBuilder.js | 2 +- .../TestEntityMultiLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityOtherMultiLink.d.ts | 2 +- .../test-service/TestEntityOtherMultiLink.js | 2 +- .../test-service/TestEntityOtherMultiLink.ts | 2 +- .../TestEntityOtherMultiLinkApi.d.ts | 2 +- .../test-service/TestEntityOtherMultiLinkApi.js | 2 +- .../test-service/TestEntityOtherMultiLinkApi.ts | 2 +- .../TestEntityOtherMultiLinkRequestBuilder.d.ts | 2 +- .../TestEntityOtherMultiLinkRequestBuilder.js | 2 +- .../TestEntityOtherMultiLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityRequestBuilder.d.ts | 2 +- .../test-service/TestEntityRequestBuilder.ts | 2 +- .../test-service/TestEntitySingleLink.d.ts | 2 +- .../test-service/TestEntitySingleLink.js | 2 +- .../test-service/TestEntitySingleLink.ts | 2 +- .../test-service/TestEntitySingleLinkApi.d.ts | 2 +- .../test-service/TestEntitySingleLinkApi.js | 2 +- .../test-service/TestEntitySingleLinkApi.ts | 2 +- .../TestEntitySingleLinkRequestBuilder.d.ts | 2 +- .../TestEntitySingleLinkRequestBuilder.js | 2 +- .../TestEntitySingleLinkRequestBuilder.ts | 2 +- .../test-service/TestEntityWithEnumKey.d.ts | 2 +- .../test-service/TestEntityWithEnumKey.js | 2 +- .../test-service/TestEntityWithEnumKey.ts | 2 +- .../test-service/TestEntityWithEnumKeyApi.d.ts | 2 +- .../test-service/TestEntityWithEnumKeyApi.js | 2 +- .../test-service/TestEntityWithEnumKeyApi.ts | 2 +- .../TestEntityWithEnumKeyRequestBuilder.d.ts | 2 +- .../TestEntityWithEnumKeyRequestBuilder.js | 2 +- .../TestEntityWithEnumKeyRequestBuilder.ts | 2 +- .../test-service/TestEntityWithNoKeys.d.ts | 2 +- .../test-service/TestEntityWithNoKeys.js | 2 +- .../test-service/TestEntityWithNoKeys.ts | 2 +- .../test-service/TestEntityWithNoKeysApi.d.ts | 2 +- .../test-service/TestEntityWithNoKeysApi.js | 2 +- .../test-service/TestEntityWithNoKeysApi.ts | 2 +- .../TestEntityWithNoKeysRequestBuilder.d.ts | 2 +- .../TestEntityWithNoKeysRequestBuilder.js | 2 +- .../TestEntityWithNoKeysRequestBuilder.ts | 2 +- .../TestEntityWithSharedEntityType1.d.ts | 2 +- .../TestEntityWithSharedEntityType1.js | 2 +- .../TestEntityWithSharedEntityType1.ts | 2 +- .../TestEntityWithSharedEntityType1Api.d.ts | 2 +- .../TestEntityWithSharedEntityType1Api.js | 2 +- .../TestEntityWithSharedEntityType1Api.ts | 2 +- ...ntityWithSharedEntityType1RequestBuilder.d.ts | 2 +- ...tEntityWithSharedEntityType1RequestBuilder.js | 2 +- ...tEntityWithSharedEntityType1RequestBuilder.ts | 2 +- .../TestEntityWithSharedEntityType2.d.ts | 2 +- .../TestEntityWithSharedEntityType2.js | 2 +- .../TestEntityWithSharedEntityType2.ts | 2 +- .../TestEntityWithSharedEntityType2Api.d.ts | 2 +- .../TestEntityWithSharedEntityType2Api.js | 2 +- .../TestEntityWithSharedEntityType2Api.ts | 2 +- ...ntityWithSharedEntityType2RequestBuilder.d.ts | 2 +- ...tEntityWithSharedEntityType2RequestBuilder.js | 2 +- ...tEntityWithSharedEntityType2RequestBuilder.ts | 2 +- .../test-service/TestEnumType.d.ts | 2 +- .../test-service/TestEnumType.js | 2 +- .../test-service/TestEnumType.ts | 2 +- .../test-service/TestEnumTypeInt64.d.ts | 2 +- .../test-service/TestEnumTypeInt64.js | 2 +- .../test-service/TestEnumTypeInt64.ts | 2 +- .../test-service/TestEnumTypeWithOneMember.d.ts | 2 +- .../test-service/TestEnumTypeWithOneMember.js | 2 +- .../test-service/TestEnumTypeWithOneMember.ts | 2 +- .../test-service/TestLvl2NestedComplexType.d.ts | 2 +- .../test-service/TestLvl2NestedComplexType.js | 2 +- .../test-service/TestLvl2NestedComplexType.ts | 2 +- .../test-service/TestNestedComplexType.d.ts | 2 +- .../test-service/TestNestedComplexType.js | 2 +- .../test-service/TestNestedComplexType.ts | 2 +- .../test-service/index.d.ts | 2 +- .../test-services-odata-v4/test-service/index.js | 2 +- .../test-services-odata-v4/test-service/index.ts | 2 +- .../test-service/operations.d.ts | 2 +- .../test-service/operations.js | 2 +- .../test-service/operations.ts | 2 +- .../test-service/service.d.ts | 2 +- .../test-service/service.js | 2 +- .../test-service/service.ts | 2 +- .../no-schema-service/default-api.d.ts | 2 +- .../no-schema-service/default-api.js | 2 +- .../no-schema-service/default-api.ts | 2 +- .../no-schema-service/index.d.ts | 2 +- .../no-schema-service/index.js | 2 +- .../no-schema-service/index.ts | 2 +- .../swagger-yaml-service/default-api.d.ts | 2 +- .../swagger-yaml-service/default-api.js | 2 +- .../swagger-yaml-service/default-api.ts | 2 +- .../swagger-yaml-service/index.d.ts | 2 +- .../swagger-yaml-service/index.js | 2 +- .../swagger-yaml-service/index.ts | 2 +- .../schema/entity-x-of-inheritance.d.ts | 2 +- .../schema/entity-x-of-inheritance.ts | 2 +- .../entity-x-of-normalized-with-object.d.ts | 2 +- .../schema/entity-x-of-normalized-with-object.ts | 2 +- .../swagger-yaml-service/schema/index.d.ts | 2 +- .../swagger-yaml-service/schema/index.js | 2 +- .../swagger-yaml-service/schema/index.ts | 2 +- .../swagger-yaml-service/schema/test-entity.d.ts | 2 +- .../swagger-yaml-service/schema/test-entity.js | 2 +- .../swagger-yaml-service/schema/test-entity.ts | 2 +- .../test-service/default-api.d.ts | 2 +- .../test-service/default-api.js | 2 +- .../test-service/default-api.ts | 2 +- .../test-service/entity-api.d.ts | 2 +- .../test-service/entity-api.js | 2 +- .../test-service/entity-api.ts | 2 +- .../test-service/extension-api.d.ts | 2 +- .../test-service/extension-api.js | 2 +- .../test-service/extension-api.ts | 2 +- .../test-service/index.d.ts | 2 +- .../test-services-openapi/test-service/index.js | 2 +- .../test-services-openapi/test-service/index.ts | 2 +- .../test-service/schema/complex-test-entity.d.ts | 2 +- .../test-service/schema/complex-test-entity.ts | 2 +- .../test-service/schema/cyclic-child.d.ts | 2 +- .../test-service/schema/cyclic-child.ts | 2 +- .../test-service/schema/cyclic-parent.d.ts | 2 +- .../test-service/schema/cyclic-parent.ts | 2 +- .../schema/discriminator-entity-child-a.d.ts | 2 +- .../schema/discriminator-entity-child-a.js | 2 +- .../schema/discriminator-entity-child-a.ts | 2 +- .../schema/discriminator-entity-child-b.d.ts | 2 +- .../schema/discriminator-entity-child-b.js | 2 +- .../schema/discriminator-entity-child-b.ts | 2 +- .../discriminator-entity-with-mapping.d.ts | 2 +- .../schema/discriminator-entity-with-mapping.ts | 2 +- .../discriminator-entity-without-mapping.d.ts | 2 +- .../discriminator-entity-without-mapping.ts | 2 +- ...minator-object-entity-workaround-child-a.d.ts | 2 +- ...riminator-object-entity-workaround-child-a.js | 2 +- ...riminator-object-entity-workaround-child-a.ts | 2 +- ...minator-object-entity-workaround-child-b.d.ts | 2 +- ...riminator-object-entity-workaround-child-b.js | 2 +- ...riminator-object-entity-workaround-child-b.ts | 2 +- ...object-entity-workaround-child-only-type.d.ts | 2 +- ...r-object-entity-workaround-child-only-type.js | 2 +- ...r-object-entity-workaround-child-only-type.ts | 2 +- ...ator-object-entity-workaround-child-type.d.ts | 2 +- ...inator-object-entity-workaround-child-type.js | 2 +- ...inator-object-entity-workaround-child-type.ts | 2 +- ...bject-entity-workaround-parent-only-type.d.ts | 2 +- ...-object-entity-workaround-parent-only-type.ts | 2 +- ...iminator-object-entity-workaround-parent.d.ts | 2 +- ...criminator-object-entity-workaround-parent.ts | 2 +- .../test-service/schema/index.d.ts | 2 +- .../test-service/schema/index.js | 2 +- .../test-service/schema/index.ts | 2 +- .../schema/other-simple-test-entity.d.ts | 2 +- .../schema/other-simple-test-entity.ts | 2 +- .../test-service/schema/schema-123456.d.ts | 2 +- .../test-service/schema/schema-123456.js | 2 +- .../test-service/schema/schema-123456.ts | 2 +- .../simple-test-entity-with-symbols-1.d.ts | 2 +- .../schema/simple-test-entity-with-symbols-1.ts | 2 +- .../schema/simple-test-entity-with-symbols.d.ts | 2 +- .../schema/simple-test-entity-with-symbols.ts | 2 +- .../test-service/schema/simple-test-entity.d.ts | 2 +- .../test-service/schema/simple-test-entity.js | 2 +- .../test-service/schema/simple-test-entity.ts | 2 +- .../test-service/schema/test-entity.d.ts | 2 +- .../test-service/schema/test-entity.ts | 2 +- .../test-service/tag-dot-api.d.ts | 2 +- .../test-service/tag-dot-api.js | 2 +- .../test-service/tag-dot-api.ts | 2 +- .../test-service/tag-space-api.d.ts | 2 +- .../test-service/tag-space-api.js | 2 +- .../test-service/tag-space-api.ts | 2 +- .../test-service/test-case-api.d.ts | 2 +- .../test-service/test-case-api.js | 2 +- .../test-service/test-case-api.ts | 2 +- 598 files changed, 605 insertions(+), 605 deletions(-) diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.d.ts index baac996e66..fae9a5644b 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.js index 535aa27221..48ea8774d7 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.Airlines = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.ts index c10a12a380..76680f0151 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airlines.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.d.ts index 23a5413ec3..8f6f47471b 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.js index 3889d528ed..c7ab723e94 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.AirlinesApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.ts index 2baf84b178..84bb176b48 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.d.ts index d29bff422d..e1ddae125f 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.js index 69b2a64dda..ea42a37ba4 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.AirlinesRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.ts index af3529b491..fe5b4851f0 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirlinesRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.d.ts index a3901cee53..a64f7a5b87 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.js index be8845c150..c859038a8f 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.AirportLocation = exports.AirportLocationField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.ts index fa35ead1f8..272b32d7fb 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportLocation.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.d.ts index d12233140e..4083e48515 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.js index d7c414f21c..18850ce790 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.Airports = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.ts index fdfc88ce3e..3451bd2d55 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Airports.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.d.ts index f981497e4b..b6bafdd52d 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.js index 59e3147c9c..cb0ebed7a8 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.AirportsApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.ts index bedaa1366d..4b02fe7dd0 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.d.ts index cf910a12a3..c79c72a913 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.js index a6f7566e1b..1b1a1872d2 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.AirportsRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.ts index c335ba3688..f60a4f7fcf 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/AirportsRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.d.ts index edbb742073..e1c22c2a22 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.js index 93284af0ae..0b8d514cc0 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.js @@ -4,7 +4,7 @@ exports.defaultMicrosoftODataServiceSampleTrippinInMemoryModelsServicePath = voi exports.batch = batch; exports.changeset = changeset; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.ts index b18008e3fb..51771fd37e 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/BatchRequest.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.d.ts index e4582cbac3..82f16b26ef 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.js index f4060b7e48..db10514764 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.City = exports.CityField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.ts index 4e73029f1e..3190284eed 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/City.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.d.ts index 31c34606b1..2a43aeca80 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.js index 681f6b13a6..69462c253d 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.EventLocation = exports.EventLocationField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.ts index b534093834..c49c23028e 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/EventLocation.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.d.ts index b341bc40e7..54c52634a1 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.js index eb4b77dbbd..5c5f0f6159 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.Location = exports.LocationField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.ts index 2a3433c669..f34801be77 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Location.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.d.ts index 56d27e2732..d729832d54 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.js index 0199f39834..eab9398a0c 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.People = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.ts index 551da1b2d0..cdb6905f41 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/People.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.d.ts index f25833ea85..f1c2c39fec 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.js index 1d121d3f8a..4ca70e7549 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.PeopleApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.ts index c9de716231..c65401408a 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.d.ts index 0ad5c1903a..3cdd440a2f 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.js index 428dd0d8ab..e6aa42d116 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.PeopleRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.ts index 54e2992fb0..0728bc0e65 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PeopleRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.d.ts index 8f65754f27..ce323db5ba 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.js index ec645e7681..886fcbc678 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.PersonGender = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.ts index 5b280becbc..1bf292f5a1 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PersonGender.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.d.ts index 1d470a4a6e..6e917c9ff0 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.js index 6e7345207d..d55da1bf9f 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.Photos = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.ts index 21cbb527b4..565d4f6ddd 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/Photos.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.d.ts index c80ad2f007..f4df01f02a 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.js index 6cc4a89827..a88e1391e5 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.PhotosApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.ts index 6a982e6764..9da2c27880 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosRequestBuilder.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosRequestBuilder.d.ts index c4c7e6f6d3..5620f5e9fb 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosRequestBuilder.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosRequestBuilder.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosRequestBuilder.ts index aefece1703..1845f69294 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosRequestBuilder.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/PhotosRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.d.ts index f103175060..c4d6600740 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.js index 8b646324bc..c3e17d672e 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.ts index f103175060..c4d6600740 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.d.ts index f15109e08f..f1788333dc 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.js index cd32d2d59e..60b83053ec 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.js @@ -4,7 +4,7 @@ exports.operations = void 0; exports.getNearestAirport = getNearestAirport; exports.resetDataSource = resetDataSource; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.ts index 0812a7d517..f13a291da0 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/operations.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.d.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.d.ts index dc3878d65d..048bf643df 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.d.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.js b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.js index 2949d749ac..450b6ff82f 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.js +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.microsoftODataServiceSampleTrippinInMemoryModelsService = microsoftODataServiceSampleTrippinInMemoryModelsService; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.ts b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.ts index 7a942f231f..c329f7c580 100644 --- a/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.ts +++ b/test-packages/test-services-e2e/TripPin/microsoft-o-data-service-sample-trippin-in-memory-models-service/service.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/BatchRequest.d.ts b/test-packages/test-services-e2e/v4/test-service/BatchRequest.d.ts index 2bc9e618e5..6492a98572 100644 --- a/test-packages/test-services-e2e/v4/test-service/BatchRequest.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/BatchRequest.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/BatchRequest.js b/test-packages/test-services-e2e/v4/test-service/BatchRequest.js index 9d70cf36be..5f269664b1 100644 --- a/test-packages/test-services-e2e/v4/test-service/BatchRequest.js +++ b/test-packages/test-services-e2e/v4/test-service/BatchRequest.js @@ -4,7 +4,7 @@ exports.defaultTestServicePath = void 0; exports.batch = batch; exports.changeset = changeset; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/BatchRequest.ts b/test-packages/test-services-e2e/v4/test-service/BatchRequest.ts index 0214a59bfd..80c320c4d7 100644 --- a/test-packages/test-services-e2e/v4/test-service/BatchRequest.ts +++ b/test-packages/test-services-e2e/v4/test-service/BatchRequest.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.d.ts b/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.d.ts index 6e4d847b1f..b3de733626 100644 --- a/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.js b/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.js index 55df5faa43..a9a6665fd5 100644 --- a/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.js +++ b/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.MyComplexReturnType = exports.MyComplexReturnTypeField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.ts b/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.ts index 6855648ffc..f42f7821d1 100644 --- a/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.ts +++ b/test-packages/test-services-e2e/v4/test-service/MyComplexReturnType.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntity.d.ts index e3f61d5bc9..e41946d66c 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity.js b/test-packages/test-services-e2e/v4/test-service/TestEntity.js index 06d39e6c7b..28195310e8 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity.ts b/test-packages/test-services-e2e/v4/test-service/TestEntity.ts index cd5525b2bd..b504c433bc 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.d.ts index 6c84725450..9fcc8ef621 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.js b/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.js index 4caf6b987e..4cf503e101 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity50Prop = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.ts b/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.ts index 4597e8e097..67632d0fae 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity50Prop.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.d.ts index fcb3ea036b..d7d727332f 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.js b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.js index e22313316d..ce961dcacb 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity50PropApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.ts b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.ts index cbba2aace3..bbcd123c03 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.d.ts index 5001bbd1ab..f7dd5b094d 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.js b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.js index 7e10f08ceb..d16981b070 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity50PropRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.ts b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.ts index 57cbfa0b19..648f1aee8f 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntity50PropRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityApi.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityApi.d.ts index 4d58a5e397..0291e643f3 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityApi.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityApi.js b/test-packages/test-services-e2e/v4/test-service/TestEntityApi.js index 845f3a8ae2..09546e8b7c 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityApi.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityApi.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityApi.ts index ca7ca5802d..3cee686438 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityApi.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityLink.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityLink.d.ts index 6928bc8a24..1c07259bff 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityLink.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityLink.js b/test-packages/test-services-e2e/v4/test-service/TestEntityLink.js index 7a58fa00fc..49a35880d8 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityLink.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityLink.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityLink.ts index 5007812426..ce6c4d30e0 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityLink.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.d.ts index acab502096..7b8804451b 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.js b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.js index 5e722f6e17..1a01a1763f 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.ts index dced95a9e5..380ae3e793 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.d.ts index 7f2beb0a24..a009830f5c 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.js b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.js index 08f13fda09..c240959363 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.ts index 793a40d3fe..c4110a3be6 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.d.ts index dce2eb32d6..2a400cf096 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.js b/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.js index 74faa53476..978fe05125 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.ts index c87bce70a2..756824ce39 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.d.ts index 75d0373bb9..71c7b17003 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.js b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.js index 6d329c30b2..8942d1f076 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithMultipleKeys = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.ts index ad27d6f982..52292c048a 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeys.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.d.ts index 83d27bbab0..df2ea7ab6b 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.js b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.js index 4082195030..8433d2d939 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithMultipleKeysApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.ts index 0f82cee741..3df4cc820e 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.d.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.d.ts index 0ad75393b6..01e41bb4a2 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.js b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.js index ce1220113d..2404b4086b 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.js +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithMultipleKeysRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.ts b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.ts index 76867e4003..274abd35c1 100644 --- a/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.ts +++ b/test-packages/test-services-e2e/v4/test-service/TestEntityWithMultipleKeysRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/index.d.ts b/test-packages/test-services-e2e/v4/test-service/index.d.ts index 07fcfe516a..0ad26de25d 100644 --- a/test-packages/test-services-e2e/v4/test-service/index.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/index.js b/test-packages/test-services-e2e/v4/test-service/index.js index 50f61fdcf4..04c5c5e3a2 100644 --- a/test-packages/test-services-e2e/v4/test-service/index.js +++ b/test-packages/test-services-e2e/v4/test-service/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/index.ts b/test-packages/test-services-e2e/v4/test-service/index.ts index 07fcfe516a..0ad26de25d 100644 --- a/test-packages/test-services-e2e/v4/test-service/index.ts +++ b/test-packages/test-services-e2e/v4/test-service/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/operations.d.ts b/test-packages/test-services-e2e/v4/test-service/operations.d.ts index b550869698..17a585fa31 100644 --- a/test-packages/test-services-e2e/v4/test-service/operations.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/operations.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/operations.js b/test-packages/test-services-e2e/v4/test-service/operations.js index 973e991345..8f69fe1c3b 100644 --- a/test-packages/test-services-e2e/v4/test-service/operations.js +++ b/test-packages/test-services-e2e/v4/test-service/operations.js @@ -11,7 +11,7 @@ exports.returnSapCloudSdk = returnSapCloudSdk; exports.createTestEntityById = createTestEntityById; exports.createTestEntityByIdReturnId = createTestEntityByIdReturnId; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/operations.ts b/test-packages/test-services-e2e/v4/test-service/operations.ts index 6a52246f49..10f8f77c78 100644 --- a/test-packages/test-services-e2e/v4/test-service/operations.ts +++ b/test-packages/test-services-e2e/v4/test-service/operations.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/service.d.ts b/test-packages/test-services-e2e/v4/test-service/service.d.ts index 3ce84c6d07..4d12757064 100644 --- a/test-packages/test-services-e2e/v4/test-service/service.d.ts +++ b/test-packages/test-services-e2e/v4/test-service/service.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/service.js b/test-packages/test-services-e2e/v4/test-service/service.js index 7897f90f18..b9e8f556ae 100644 --- a/test-packages/test-services-e2e/v4/test-service/service.js +++ b/test-packages/test-services-e2e/v4/test-service/service.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.testService = testService; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-e2e/v4/test-service/service.ts b/test-packages/test-services-e2e/v4/test-service/service.ts index 70ab4c1af9..24a2aad916 100644 --- a/test-packages/test-services-e2e/v4/test-service/service.ts +++ b/test-packages/test-services-e2e/v4/test-service/service.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-common/common-entity.ts b/test-packages/test-services-odata-common/common-entity.ts index 0e3de5e524..1f6eb51b9e 100644 --- a/test-packages/test-services-odata-common/common-entity.ts +++ b/test-packages/test-services-odata-common/common-entity.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ @@ -39,7 +39,7 @@ import { import { customTestDeSerializers } from '../../test-resources/test/test-util'; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ @@ -138,7 +138,7 @@ export namespace CommonComplexType { } /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ @@ -199,7 +199,7 @@ export namespace NestedComplexType { } /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ @@ -232,7 +232,7 @@ export interface CommonEntitySingleLinkType< } /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ @@ -351,7 +351,7 @@ export class CommonEntitySingleLinkApi< } /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ @@ -392,7 +392,7 @@ export interface CommonEntityType< } /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ @@ -560,7 +560,7 @@ export class CommonEntityApi< } /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.d.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.d.ts index 2f77958cb1..3e47410b1a 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.d.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.js b/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.js index f62a9067f9..cad27ae4a7 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.js +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.js @@ -4,7 +4,7 @@ exports.defaultMultipleSchemasServicePath = void 0; exports.batch = batch; exports.changeset = changeset; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.ts index f888ed2631..10a12153d1 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/BatchRequest.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.d.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.d.ts index 5a0327b0da..13a141373c 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.d.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.js b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.js index 492e2018fc..56e62e8863 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.js +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiSchemaTestEntity = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.ts index c0af997542..6f433731f0 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntity.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.d.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.d.ts index 90b8599155..74888eff07 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.d.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.js b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.js index f33472a4f7..bdf7c9d209 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.js +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiSchemaTestEntityApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.ts index d62826d70a..d8052d04c0 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.d.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.d.ts index 662ffa3d5e..da3fbc65a6 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.js b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.js index 6486009b9f..d5c567cbf2 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.js +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiSchemaTestEntityRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.ts index 23be48528a..970230e79a 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/MultiSchemaTestEntityRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/index.d.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/index.d.ts index 7414f7d7d9..b9b76bdbe0 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/index.d.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/index.js b/test-packages/test-services-odata-v2/multiple-schemas-service/index.js index dffb2ebe29..bfba65f94c 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/index.js +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/index.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/index.ts index 7414f7d7d9..b9b76bdbe0 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/index.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/service.d.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/service.d.ts index f25d819c39..4aac822d47 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/service.d.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/service.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/service.js b/test-packages/test-services-odata-v2/multiple-schemas-service/service.js index 8b10d473f8..f52b5a84cd 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/service.js +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/service.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.multipleSchemasService = multipleSchemasService; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/multiple-schemas-service/service.ts b/test-packages/test-services-odata-v2/multiple-schemas-service/service.ts index b0df923e8b..20aed9c6df 100644 --- a/test-packages/test-services-odata-v2/multiple-schemas-service/service.ts +++ b/test-packages/test-services-odata-v2/multiple-schemas-service/service.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/BatchRequest.d.ts b/test-packages/test-services-odata-v2/test-service/BatchRequest.d.ts index 9873c73550..33b3615de3 100644 --- a/test-packages/test-services-odata-v2/test-service/BatchRequest.d.ts +++ b/test-packages/test-services-odata-v2/test-service/BatchRequest.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/BatchRequest.js b/test-packages/test-services-odata-v2/test-service/BatchRequest.js index 0c28e82fb4..1fb986c332 100644 --- a/test-packages/test-services-odata-v2/test-service/BatchRequest.js +++ b/test-packages/test-services-odata-v2/test-service/BatchRequest.js @@ -4,7 +4,7 @@ exports.defaultTestServicePath = void 0; exports.batch = batch; exports.changeset = changeset; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/BatchRequest.ts b/test-packages/test-services-odata-v2/test-service/BatchRequest.ts index d440bc76ba..9ec174248f 100644 --- a/test-packages/test-services-odata-v2/test-service/BatchRequest.ts +++ b/test-packages/test-services-odata-v2/test-service/BatchRequest.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/CaseTest.d.ts b/test-packages/test-services-odata-v2/test-service/CaseTest.d.ts index 00aea74e47..ee2818cc57 100644 --- a/test-packages/test-services-odata-v2/test-service/CaseTest.d.ts +++ b/test-packages/test-services-odata-v2/test-service/CaseTest.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/CaseTest.js b/test-packages/test-services-odata-v2/test-service/CaseTest.js index 58f36fa92c..92c3fe1931 100644 --- a/test-packages/test-services-odata-v2/test-service/CaseTest.js +++ b/test-packages/test-services-odata-v2/test-service/CaseTest.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.CaseTest = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/CaseTest.ts b/test-packages/test-services-odata-v2/test-service/CaseTest.ts index 6f2836f39d..8ad47b31d9 100644 --- a/test-packages/test-services-odata-v2/test-service/CaseTest.ts +++ b/test-packages/test-services-odata-v2/test-service/CaseTest.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/CaseTestApi.d.ts b/test-packages/test-services-odata-v2/test-service/CaseTestApi.d.ts index 3ef462afa9..cc315faf73 100644 --- a/test-packages/test-services-odata-v2/test-service/CaseTestApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/CaseTestApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/CaseTestApi.js b/test-packages/test-services-odata-v2/test-service/CaseTestApi.js index f4fec751b2..5ea898aa2e 100644 --- a/test-packages/test-services-odata-v2/test-service/CaseTestApi.js +++ b/test-packages/test-services-odata-v2/test-service/CaseTestApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.CaseTestApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/CaseTestApi.ts b/test-packages/test-services-odata-v2/test-service/CaseTestApi.ts index 27a5672596..4058b0ecd7 100644 --- a/test-packages/test-services-odata-v2/test-service/CaseTestApi.ts +++ b/test-packages/test-services-odata-v2/test-service/CaseTestApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.d.ts index c4be2b6900..6b932126f8 100644 --- a/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.js index 7f41842ea0..521c6aafac 100644 --- a/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.CaseTestRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.ts index f54fd5d77d..5cb77efb79 100644 --- a/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/CaseTestRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/Casetest_1.d.ts b/test-packages/test-services-odata-v2/test-service/Casetest_1.d.ts index a1e94aa7c4..042db1ea71 100644 --- a/test-packages/test-services-odata-v2/test-service/Casetest_1.d.ts +++ b/test-packages/test-services-odata-v2/test-service/Casetest_1.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/Casetest_1.js b/test-packages/test-services-odata-v2/test-service/Casetest_1.js index bddf6b6446..22631d646e 100644 --- a/test-packages/test-services-odata-v2/test-service/Casetest_1.js +++ b/test-packages/test-services-odata-v2/test-service/Casetest_1.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.Casetest_1 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/Casetest_1.ts b/test-packages/test-services-odata-v2/test-service/Casetest_1.ts index edd6bebee0..244f8c1502 100644 --- a/test-packages/test-services-odata-v2/test-service/Casetest_1.ts +++ b/test-packages/test-services-odata-v2/test-service/Casetest_1.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/Casetest_1Api.d.ts b/test-packages/test-services-odata-v2/test-service/Casetest_1Api.d.ts index d45bcbaa64..f870dc895e 100644 --- a/test-packages/test-services-odata-v2/test-service/Casetest_1Api.d.ts +++ b/test-packages/test-services-odata-v2/test-service/Casetest_1Api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/Casetest_1Api.js b/test-packages/test-services-odata-v2/test-service/Casetest_1Api.js index 6c16deb485..2cec4f704c 100644 --- a/test-packages/test-services-odata-v2/test-service/Casetest_1Api.js +++ b/test-packages/test-services-odata-v2/test-service/Casetest_1Api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.Casetest_1Api = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/Casetest_1Api.ts b/test-packages/test-services-odata-v2/test-service/Casetest_1Api.ts index de316daff2..b3d8f27c15 100644 --- a/test-packages/test-services-odata-v2/test-service/Casetest_1Api.ts +++ b/test-packages/test-services-odata-v2/test-service/Casetest_1Api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.d.ts index f92d4fb2b9..a61353d0aa 100644 --- a/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.js b/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.js index 937648c257..b8338f997d 100644 --- a/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.Casetest_1RequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.ts index dd3e0d2663..3959c74bc3 100644 --- a/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/Casetest_1RequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestComplexType.d.ts b/test-packages/test-services-odata-v2/test-service/TestComplexType.d.ts index c744c38917..1651a468b7 100644 --- a/test-packages/test-services-odata-v2/test-service/TestComplexType.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestComplexType.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestComplexType.js b/test-packages/test-services-odata-v2/test-service/TestComplexType.js index 27aafffe6c..3dd31d4436 100644 --- a/test-packages/test-services-odata-v2/test-service/TestComplexType.js +++ b/test-packages/test-services-odata-v2/test-service/TestComplexType.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestComplexType = exports.TestComplexTypeField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestComplexType.ts b/test-packages/test-services-odata-v2/test-service/TestComplexType.ts index 088fdd2a5d..987c770aff 100644 --- a/test-packages/test-services-odata-v2/test-service/TestComplexType.ts +++ b/test-packages/test-services-odata-v2/test-service/TestComplexType.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntity.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntity.d.ts index 2ca6f556f8..e68a94bc6b 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntity.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntity.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntity.js b/test-packages/test-services-odata-v2/test-service/TestEntity.js index f990ed38aa..1feb9e6655 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntity.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntity.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntity.ts b/test-packages/test-services-odata-v2/test-service/TestEntity.ts index c4fa8f799d..f8b6fe7109 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntity.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntity.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityApi.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityApi.d.ts index 4a4c6d14d4..4900242e2a 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityApi.js b/test-packages/test-services-odata-v2/test-service/TestEntityApi.js index 9b69165cce..985949ded0 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityApi.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityApi.ts b/test-packages/test-services-odata-v2/test-service/TestEntityApi.ts index 833e0a50ba..9e6c9d2a48 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityApi.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.d.ts index faa08ffc48..2aada69766 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.js b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.js index f67604d734..fb80d9f7c5 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkChild = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.ts index 7abbd3eae9..6e067cd31b 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChild.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.d.ts index 98b5d45f74..971f03206e 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.js b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.js index 2efc58987a..53bb7be191 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkChildApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.ts index e889b559bd..a6124e4062 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.d.ts index 8aa01886cd..43ee233142 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.js index cc362edf83..5e9d53438e 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkChildRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.ts index fbdb1ff824..e42688d0bd 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkChildRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.d.ts index 3aeba82c75..ac2abd5f82 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.js b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.js index dc49386a25..26eb743f1e 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkParent = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.ts index c8e2144699..849a587e15 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParent.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.d.ts index 6c335ee3e4..e62c76e675 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.js b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.js index c57bdd18de..166c6f4ab3 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkParentApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.ts index b7abba025e..ae43231aa8 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.d.ts index c64d410851..db0efd844e 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.js index 53ef41a167..79ae41b74a 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkParentRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.ts index c9ef494ce2..fdb67c015a 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityCircularLinkParentRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.d.ts index dee434bb1e..c9160cb237 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.js b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.js index 0c6f4ca1cf..ad1c0dc059 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWith = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.ts index dc0154be81..ea693b5e1d 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWith.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.d.ts index e638a3a227..b500837466 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.js b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.js index b5f445d961..426a30b40f 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWithApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.ts index 0bc7c84bca..edfd55ac63 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.d.ts index 2c0237cde5..f934443651 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.js index 31e52bc189..f0eab6688b 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWithRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.ts index 2ffbe9c945..f7a958eb6e 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.d.ts index e4776f226f..43bc643d68 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.js b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.js index 7717fa31cb..d0dcc55c2a 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWithSomethingElse = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.ts index b737766a2e..026221b673 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElse.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.d.ts index 242fa08d03..75d184cabe 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.js b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.js index dc1fbb1122..c1af35e9be 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWithSomethingElseApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.ts index 24248f5b8f..2cd71eff4f 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.d.ts index ab255ab76e..d4eff52e77 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.js index fc55661893..3d0338540c 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWithSomethingElseRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.ts index a20b20c1ef..054a395dc5 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityEndsWithSomethingElseRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.d.ts index b5c6052c80..260ce45c5f 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.js b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.js index 308d25e8cd..acabd202f0 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2MultiLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.ts index 171edaf110..6e23f0c809 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.d.ts index 3ffb74784d..f79b4a7726 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.js b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.js index 0657c5422c..ac0ad35ae6 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2MultiLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.ts index 9584c8af7b..2376da16be 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.d.ts index 9523b3d5d6..d8d19526fb 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.js index b45e48d709..73563c8703 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2MultiLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.ts index 68671d8490..856eb27e8a 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2MultiLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.d.ts index cb8382f717..17cb72a0e8 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.js b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.js index 825b839597..003ff634aa 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2SingleLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.ts index d4734caa92..c3385556ad 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.d.ts index 59ef487db1..9826fcf4b0 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.js b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.js index e07d42fe2d..8de0c82eee 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2SingleLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.ts index 6f3a1d7615..e47b56aa36 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.d.ts index 3f9befbb79..1575853c0f 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.js index 8fd0a0e4bc..c688848d51 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2SingleLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.ts index f456b62c40..ab2786d4c9 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityLvl2SingleLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.d.ts index 564fb0dd6c..922e7f0891 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.js b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.js index 6745d99b54..b1c6ec56dd 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityMultiLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.ts b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.ts index 246c79b638..41ea22bb0e 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.d.ts index 5b221f84f8..7aeaad3be1 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.js b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.js index 846391d8f9..e82be07689 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityMultiLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.ts b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.ts index 30d1f93fef..ceaa31acda 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.d.ts index 4937551be0..a8b2dbedbf 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.js index 8ba6b9fb17..608adc2795 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityMultiLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.ts index 25a77dae26..4879145964 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityMultiLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.d.ts index 393a175680..9d977b2e76 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.js b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.js index 2c940c409a..abadcb775a 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityOtherMultiLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.ts b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.ts index ad98112749..4fedb93f04 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.d.ts index db14059263..6fac5a857c 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.js b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.js index e7c8e0c9cc..087d8e7ce7 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityOtherMultiLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.ts b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.ts index cadc7fdd55..b5846cfefb 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.d.ts index 744e1c15d0..db64faa786 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.js index bdca9740e8..3a4a47a3bd 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityOtherMultiLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.ts index 6d44ac5df7..36434f0dae 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityOtherMultiLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.d.ts index fadc271d38..bc69af56f2 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.js index 8c6f3a0285..fedd2c088a 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.ts index 9beb4d4cff..c1a4a997a9 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.d.ts index bed696f74d..dd9f63e039 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.js b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.js index 6534d6906e..6737652081 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntitySingleLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.ts b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.ts index f63bf4a94b..566586ebcc 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.d.ts index c587f96292..97e3514a38 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.js b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.js index 75d7a2b160..7ab68463dd 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntitySingleLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.ts b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.ts index ef63e00299..eb61c16795 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.d.ts index 9f37d95b39..9c53c4b20b 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.js index aec2cf436d..aa53a318a2 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntitySingleLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.ts index da788da815..f5d126ded8 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntitySingleLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.d.ts index a5608a3e0c..24c1d31ee7 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.js b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.js index 58214d2e95..31d14f697b 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType1 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.ts index 2273d8a1a0..afd95b0b38 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.d.ts index 046f206116..7c0c8c7976 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.js b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.js index 39bf3f2bca..ec45c0f9c1 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType1Api = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.ts index 3a52560b5e..ff2d1697ae 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1Api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.d.ts index 886366ffae..3ee88818b4 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.js index 22384eb1cf..71aa51065e 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType1RequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.ts index 1c7d492a6c..d66c9e97ae 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType1RequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.d.ts index b802774449..6ab6f329bb 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.js b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.js index 24007f8ba9..5502bb4f5d 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType2 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.ts index 6824d3c703..1ff909cd2c 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.d.ts index 68000e699f..3fd336e199 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.js b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.js index b46cfca061..8d44b48900 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType2Api = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.ts index 5dc494404a..cab01a8a38 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2Api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.d.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.d.ts index f9fe5d3c76..af3b448658 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.js b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.js index de33d6f5f3..f60ff98816 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.js +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType2RequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.ts b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.ts index 909817ec12..d4cec709a6 100644 --- a/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.ts +++ b/test-packages/test-services-odata-v2/test-service/TestEntityWithSharedEntityType2RequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.d.ts b/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.d.ts index b3dfb9eb96..d24f62a611 100644 --- a/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.js b/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.js index ce24bbe5ad..83c115137c 100644 --- a/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.js +++ b/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestLvl2NestedComplexType = exports.TestLvl2NestedComplexTypeField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.ts b/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.ts index adc8e16a40..c3dfc1d230 100644 --- a/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.ts +++ b/test-packages/test-services-odata-v2/test-service/TestLvl2NestedComplexType.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.d.ts b/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.d.ts index 46dc594299..2df1ccd8dc 100644 --- a/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.d.ts +++ b/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.js b/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.js index 2216114b42..af7a9d55f2 100644 --- a/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.js +++ b/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestNestedComplexType = exports.TestNestedComplexTypeField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.ts b/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.ts index ac4e8929a6..2f43c5f9be 100644 --- a/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.ts +++ b/test-packages/test-services-odata-v2/test-service/TestNestedComplexType.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/index.d.ts b/test-packages/test-services-odata-v2/test-service/index.d.ts index 39526fad9c..cb46260da7 100644 --- a/test-packages/test-services-odata-v2/test-service/index.d.ts +++ b/test-packages/test-services-odata-v2/test-service/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/index.js b/test-packages/test-services-odata-v2/test-service/index.js index 834a13d29f..a42c95ce52 100644 --- a/test-packages/test-services-odata-v2/test-service/index.js +++ b/test-packages/test-services-odata-v2/test-service/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/index.ts b/test-packages/test-services-odata-v2/test-service/index.ts index 39526fad9c..cb46260da7 100644 --- a/test-packages/test-services-odata-v2/test-service/index.ts +++ b/test-packages/test-services-odata-v2/test-service/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/operations.d.ts b/test-packages/test-services-odata-v2/test-service/operations.d.ts index 375384d3be..c775dc3e8d 100644 --- a/test-packages/test-services-odata-v2/test-service/operations.d.ts +++ b/test-packages/test-services-odata-v2/test-service/operations.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/operations.js b/test-packages/test-services-odata-v2/test-service/operations.js index ea55088ce3..d480c4428f 100644 --- a/test-packages/test-services-odata-v2/test-service/operations.js +++ b/test-packages/test-services-odata-v2/test-service/operations.js @@ -17,7 +17,7 @@ exports.testFunctionImportMultipleParams = testFunctionImportMultipleParams; exports.createTestComplexType = createTestComplexType; exports.fContinue = fContinue; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/operations.ts b/test-packages/test-services-odata-v2/test-service/operations.ts index 5651001bb8..13a71f2ff7 100644 --- a/test-packages/test-services-odata-v2/test-service/operations.ts +++ b/test-packages/test-services-odata-v2/test-service/operations.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/service.d.ts b/test-packages/test-services-odata-v2/test-service/service.d.ts index cc8cceea33..9a98be610b 100644 --- a/test-packages/test-services-odata-v2/test-service/service.d.ts +++ b/test-packages/test-services-odata-v2/test-service/service.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/service.js b/test-packages/test-services-odata-v2/test-service/service.js index f65f677d62..ebcc1b47c7 100644 --- a/test-packages/test-services-odata-v2/test-service/service.js +++ b/test-packages/test-services-odata-v2/test-service/service.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.testService = testService; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v2/test-service/service.ts b/test-packages/test-services-odata-v2/test-service/service.ts index d64be6ae81..6714cd702d 100644 --- a/test-packages/test-services-odata-v2/test-service/service.ts +++ b/test-packages/test-services-odata-v2/test-service/service.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.d.ts index 2ad771b58d..b95261f7ec 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.js b/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.js index a0d942060a..210729ebf0 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.js @@ -4,7 +4,7 @@ exports.defaultMultipleSchemasServicePath = void 0; exports.batch = batch; exports.changeset = changeset; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.ts index dd85cf8154..359057891c 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/BatchRequest.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.d.ts index 9ae9168ae4..84eff0bd5e 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.js index 32f4948576..eecac93efe 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestComplexType1 = exports.TestComplexType1Field = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.ts index f2f7da7769..4e7ca8b6c1 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType1.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.d.ts index ea65febb2a..6837a89996 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.js index 56966a9505..73937d3156 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestComplexType2 = exports.TestComplexType2Field = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.ts index 593560cc68..11a8354620 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestComplexType2.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.d.ts index 22d6fd98ce..3abb0b41ce 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.js index 7dd312d1fc..0efe54015a 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity1 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.ts index c9e28caf06..8e6d141e0d 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.d.ts index 850d46741c..74eab1fe30 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.js index 96d69f8a9b..002f3118e9 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity1Api = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.ts index ef8e2df82f..daef1d598b 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1Api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.d.ts index 3a819c8de4..fe1c359678 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.js index 2f91dba428..d8bc12099c 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity1RequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.ts index 5560cbc78e..b05825d407 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity1RequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.d.ts index c93ee6448f..5836654840 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.js index 38c92ad37e..698e0d5033 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity2 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.ts index a49754cc94..bf77d8f3ab 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.d.ts index dc6cb1a971..9dbfd61e2d 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.js index 4e3ab1945f..1072afea3d 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity2Api = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.ts index e8981f6d3e..9a2fb6bba0 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2Api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.d.ts index 2f9c384cd1..61f47bf6ee 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.js index 48a1efa54c..4634e9583b 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity2RequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.ts index 3967073058..daf7d89886 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity2RequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.d.ts index cd6a227e8e..03f8727036 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.js index d3dd408040..f86575e13d 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity3 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.ts index 51ae2c0d17..7409a6f989 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.d.ts index 4d10e373c2..995f1b4a89 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.js index 8db22cd5aa..f14eea51ef 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity3Api = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.ts index d9033dd4df..d54aa680a6 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3Api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.d.ts index 63435bea3d..31ba6a4aee 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.js index 30d67dfdef..f91b2a4f6b 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity3RequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.ts index d8b7bfa420..9f836a6fbc 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity3RequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.d.ts index 9036d51d8a..7933cca593 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.js index 8a2e353199..019e7428f5 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity4 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.ts index 28064f6a1e..90b3d1a086 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.d.ts index 19922c2b26..99ef8078c7 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.js index d23a3afbf7..cb0eed22bb 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity4Api = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.ts index 096a3429f7..2ca3a84dcd 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4Api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.d.ts index 1d1fa40157..99e2efd6de 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.js index 2e9a5ae941..d27e572d27 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity4RequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.ts index fe0991394e..3d6f362a3c 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEntity4RequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.d.ts index 7984a342e7..043e7efbce 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.js index 9e0631fb5b..c002833218 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEnumType1 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.ts index 2db86d904e..9f2953f536 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType1.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.d.ts index bbb14673cc..6d5d35a0b5 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.js b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.js index 711dd7bc3f..b2c3412121 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEnumType2 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.ts index af19451230..df6d35539f 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/TestEnumType2.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/index.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/index.d.ts index 45334b56da..22c7393503 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/index.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/index.js b/test-packages/test-services-odata-v4/multiple-schemas-service/index.js index 6e4162d755..7aa504020c 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/index.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/index.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/index.ts index 45334b56da..22c7393503 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/index.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/operations.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/operations.d.ts index f94cc0ff4b..e627867c15 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/operations.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/operations.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/operations.js b/test-packages/test-services-odata-v4/multiple-schemas-service/operations.js index 6fc6fc18a6..ef9f5e0a2c 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/operations.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/operations.js @@ -6,7 +6,7 @@ exports.testFunctionImportEntityReturnType2 = testFunctionImportEntityReturnType exports.testActionImportNoParameterComplexReturnType1 = testActionImportNoParameterComplexReturnType1; exports.testActionImportNoParameterComplexReturnType2 = testActionImportNoParameterComplexReturnType2; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/operations.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/operations.ts index d9102eb12b..7cd5753f42 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/operations.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/operations.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/service.d.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/service.d.ts index 151615f9a8..d93cc540cb 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/service.d.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/service.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/service.js b/test-packages/test-services-odata-v4/multiple-schemas-service/service.js index 68b7c797b0..32ec1892ce 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/service.js +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/service.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.multipleSchemasService = multipleSchemasService; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/multiple-schemas-service/service.ts b/test-packages/test-services-odata-v4/multiple-schemas-service/service.ts index 993b599212..332eafd1d1 100644 --- a/test-packages/test-services-odata-v4/multiple-schemas-service/service.ts +++ b/test-packages/test-services-odata-v4/multiple-schemas-service/service.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/BatchRequest.d.ts b/test-packages/test-services-odata-v4/test-service/BatchRequest.d.ts index 5d2801d3c6..9d8503f2ed 100644 --- a/test-packages/test-services-odata-v4/test-service/BatchRequest.d.ts +++ b/test-packages/test-services-odata-v4/test-service/BatchRequest.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/BatchRequest.js b/test-packages/test-services-odata-v4/test-service/BatchRequest.js index a576dfcd73..5ff6d1cb3f 100644 --- a/test-packages/test-services-odata-v4/test-service/BatchRequest.js +++ b/test-packages/test-services-odata-v4/test-service/BatchRequest.js @@ -4,7 +4,7 @@ exports.defaultTestServicePath = void 0; exports.batch = batch; exports.changeset = changeset; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/BatchRequest.ts b/test-packages/test-services-odata-v4/test-service/BatchRequest.ts index d326e10435..5da5a9dbae 100644 --- a/test-packages/test-services-odata-v4/test-service/BatchRequest.ts +++ b/test-packages/test-services-odata-v4/test-service/BatchRequest.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.d.ts b/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.d.ts index e42f1b488c..d1ad4887f2 100644 --- a/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.js b/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.js index b8afbd473a..cb988bf44e 100644 --- a/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.js +++ b/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestComplexBaseType = exports.TestComplexBaseTypeField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.ts b/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.ts index f4f466c1e5..e243b7e113 100644 --- a/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.ts +++ b/test-packages/test-services-odata-v4/test-service/TestComplexBaseType.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestComplexType.d.ts b/test-packages/test-services-odata-v4/test-service/TestComplexType.d.ts index c90c1dd4c8..5ec52ac3e2 100644 --- a/test-packages/test-services-odata-v4/test-service/TestComplexType.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestComplexType.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestComplexType.js b/test-packages/test-services-odata-v4/test-service/TestComplexType.js index 7e3df7d319..67b22e2f15 100644 --- a/test-packages/test-services-odata-v4/test-service/TestComplexType.js +++ b/test-packages/test-services-odata-v4/test-service/TestComplexType.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestComplexType = exports.TestComplexTypeField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestComplexType.ts b/test-packages/test-services-odata-v4/test-service/TestComplexType.ts index 352eb5cb26..ddbb45e8ff 100644 --- a/test-packages/test-services-odata-v4/test-service/TestComplexType.ts +++ b/test-packages/test-services-odata-v4/test-service/TestComplexType.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntity.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntity.d.ts index 50986dd6d3..ab3b4ec337 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntity.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntity.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntity.js b/test-packages/test-services-odata-v4/test-service/TestEntity.js index 88372cc240..0600edc7f0 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntity.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntity.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntity = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntity.ts b/test-packages/test-services-odata-v4/test-service/TestEntity.ts index a4269f4471..239c522b91 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntity.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntity.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityApi.d.ts index 3ae7f3808a..d85d3738c9 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityApi.js index f46e6c2148..d0a8fd8988 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityApi.ts index a35eb59c7a..ae5556adbb 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.d.ts index 6f928c6e39..29be2e0884 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.js b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.js index 4cb5e6e972..1ac58635d5 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkChild = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.ts index 63692a4c67..4ebfb6fac3 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChild.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.d.ts index 5350392498..bf61bd1e47 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.js index 1eb141fbca..8438fdd049 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkChildApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.ts index dda353ecd1..1f4baaff25 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.d.ts index eb179a64db..930cd9055b 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.js index 9c56f696e6..c0847cbb65 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkChildRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.ts index 94f293a36e..2e6db5ab39 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkChildRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.d.ts index 5ba09db0a3..b09196dbf5 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.js b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.js index 71d7e237de..fa212b4a02 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkParent = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.ts index f9b9029fe0..a90c7223ea 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParent.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.d.ts index eca261b9ee..61f56a170e 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.js index 7622b8785f..67d878ff41 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkParentApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.ts index 0f81f55491..19c547ed3c 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.d.ts index 62b9d087f8..6ee7288056 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.js index 3251b0ca2e..644907e718 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityCircularLinkParentRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.ts index 7d2ed9004f..9f8bb31907 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityCircularLinkParentRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.d.ts index f788379f97..a12577facd 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.js b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.js index 96b7cfbc57..d92b0f3057 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWith = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.ts index 101ee80632..3e134b35dc 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWith.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.d.ts index 5dbe5d9ec0..b3164e1af9 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.js index 28e876647d..ffc0d45d2c 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWithApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.ts index 71604d2d58..1b32f42ef0 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.d.ts index 561f9c0be0..f35436afa0 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.js index b341ecc924..99a823ae8b 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWithRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.ts index 9ea80580db..5f4e6fa56f 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.d.ts index ee5220ca84..ed369dee26 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.js b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.js index 7be5c04037..fc79279e82 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWithSomethingElse = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.ts index 2a30b1f4cf..ab1f208101 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElse.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.d.ts index daac4701af..13f7b14252 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.js index c4470e7d20..de1b8962f5 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWithSomethingElseApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.ts index 1387f2e1e5..84261e4e09 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.d.ts index 9088c92119..574d198cc8 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.js index c7df5ed595..14e986dfe5 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityEndsWithSomethingElseRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.ts index 2e02655ed1..180ecb4796 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityEndsWithSomethingElseRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.d.ts index 62869a4dfa..ce22a3baf5 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.js b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.js index 60e7db9d0e..3ed7e2af77 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2MultiLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.ts index abdfbf854b..d532dbbd1f 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.d.ts index 7fae25611e..aef872f86b 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.js index 3596a89f32..284863b19e 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2MultiLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.ts index 47b6188565..c3adbed1bc 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.d.ts index 7451005c14..6d98a45f99 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.js index 3b2bf37bdb..1f6a89f416 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2MultiLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.ts index 4951c8a69a..3f45a76f5a 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2MultiLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.d.ts index 88b19178b4..bf1bcd9e84 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.js b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.js index 8d10b2c9e1..0619d64129 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2SingleLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.ts index 94d9f9daf5..a7edc389ed 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.d.ts index c8b3892d4c..5c1556f22c 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.js index ef8ea06c4f..282e82cd94 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2SingleLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.ts index 91f7306c01..837c8cad27 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.d.ts index 1a4d3d4da2..c08c56b7c1 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.js index 3a8bb019f4..fdd42e1807 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl2SingleLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.ts index ec6ac6a6e4..d00782c9f3 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl2SingleLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.d.ts index 3ee78ffa8d..fb5d132570 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.js b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.js index 1188bc10c4..08ca59307b 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl3MultiLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.ts index 2f28d3b248..5d1c36fb71 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.d.ts index dd34f7c7b2..0c5555eeda 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.js index e9d4825842..7a150892ff 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl3MultiLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.ts index c554ec1c93..5919ebf2d3 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.d.ts index cc679615ac..afc5d8df3d 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.js index e6cd6f33e5..93c1194c69 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityLvl3MultiLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.ts index 691885ad80..f2522504da 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityLvl3MultiLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.d.ts index 9f7159d7d1..2fa87d95df 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.js b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.js index b1fca1cf62..0dc6db0f2f 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityMultiLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.ts b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.ts index f3be85f1d5..b45ce58351 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.d.ts index f8c80ed08b..4448e7b9bf 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.js index 7c51787753..56ec5a4212 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityMultiLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.ts index 1ac914e3dd..a4e3c46a0c 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.d.ts index 62c6e5e5c8..0d764da327 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.js index 99256f88e5..3edc762a54 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityMultiLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.ts index 0da6c8e1d2..6d0176cafd 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityMultiLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.d.ts index 03a02bcfe3..e9423776c6 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.js b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.js index ed60aba269..ba04b7c536 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityOtherMultiLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.ts b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.ts index aeaaf59280..01024860b2 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.d.ts index 731574c70a..27190b2362 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.js index 947dc697de..4249df2431 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityOtherMultiLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.ts index d64f6b5482..7c6e04cc9b 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.d.ts index 91bffdc775..7cb5b36c32 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.js index 8f8b51bae6..582428aad5 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityOtherMultiLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.ts index e7da0bc867..9f3c98cc38 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityOtherMultiLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityRequestBuilder.d.ts index fcca83e398..26eaadd707 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityRequestBuilder.ts index 8b697fa5d6..40a7a35c5b 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.d.ts index ae6c7c4257..08869a3c86 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.js b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.js index 8865154f55..28bb087904 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntitySingleLink = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.ts b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.ts index 30694edb48..a76cb4c8f0 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLink.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.d.ts index 2d5792fd9d..37fa01e8eb 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.js b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.js index 8d6d2ffd0b..9ae568c3b4 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntitySingleLinkApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.ts index d97812b41c..77a86a341c 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.d.ts index 8a8480948e..bf5f79c6fa 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.js index 44716228f5..faac7b6bf4 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntitySingleLinkRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.ts index 8a6a83bc5a..96979eceaf 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntitySingleLinkRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.d.ts index cc297c2447..016ae38ff1 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.js index 2fc3784636..99efdc97c1 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithEnumKey = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.ts index 588f0d3250..c249ad64b0 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKey.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.d.ts index 735b10e1ce..fb19d8b80c 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.js index 2303bc4bed..6bd00b11f4 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithEnumKeyApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.ts index 197b97596d..f3e03fcee3 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.d.ts index cfc64b988f..35d4bd0d21 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.js index 9102f48fb4..cba4b64a3e 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithEnumKeyRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.ts index bda6fa9170..7313c07688 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithEnumKeyRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.d.ts index 34514af378..a658eba025 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.js index 8d06608058..c77f2400ac 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithNoKeys = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.ts index bb35738861..7f09c19b2f 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeys.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.d.ts index 070f221dfe..3c64870f33 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.js index 32daf8d89d..c541142e1c 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithNoKeysApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.ts index a3fbc484b9..aad60b64aa 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysApi.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.d.ts index f862ca342d..a4ff8d56a6 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.js index a03b6a7881..c19f2846a9 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithNoKeysRequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.ts index 5c958b0664..3cbc983528 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithNoKeysRequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.d.ts index 5dcc84a5fe..ebe6442ff6 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.js index 895368953d..24803738a6 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType1 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.ts index 8a2ede7460..d1ab255997 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.d.ts index 365a5ccfaa..a70d594e42 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.js index 386940aa0f..9885b0e47e 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType1Api = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.ts index 232a94bc60..f32498bb16 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1Api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.d.ts index 9d38c7548a..805a6fd2e0 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.js index 21b61a2fea..f4fdb54220 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType1RequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.ts index d6e6ae031f..cece0c489a 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType1RequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.d.ts index f768ef3751..573d20172d 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.js index 76503b1c42..0083778a96 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType2 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.ts index fc7303ba16..18ae35ff5d 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.d.ts index 74f36d02fe..8cc0d6d6ce 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.js index d6e41c4b36..8a92cf11e3 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType2Api = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.ts index 883df4034e..959de01a0c 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2Api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.d.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.d.ts index 905a6b7069..e8924b97e9 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.js b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.js index b2ccc3fa3d..68f81749d7 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.js +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEntityWithSharedEntityType2RequestBuilder = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.ts b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.ts index 1a5c5067ca..54c1acf1c3 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEntityWithSharedEntityType2RequestBuilder.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEnumType.d.ts b/test-packages/test-services-odata-v4/test-service/TestEnumType.d.ts index 5a313117f5..dbf56a5505 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEnumType.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEnumType.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEnumType.js b/test-packages/test-services-odata-v4/test-service/TestEnumType.js index 20c143fdda..e905c21f16 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEnumType.js +++ b/test-packages/test-services-odata-v4/test-service/TestEnumType.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEnumType = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEnumType.ts b/test-packages/test-services-odata-v4/test-service/TestEnumType.ts index 96a053cb93..64fd2acacd 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEnumType.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEnumType.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.d.ts b/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.d.ts index 619ab17f72..eaef157bb6 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.js b/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.js index c607475728..2d07ddd0c5 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.js +++ b/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEnumTypeInt64 = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.ts b/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.ts index 63639acdac..c61fa0370d 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEnumTypeInt64.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.d.ts b/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.d.ts index 6d492b124e..6ef3e2aeaa 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.js b/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.js index 8c22c4c00f..26118e8abf 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.js +++ b/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestEnumTypeWithOneMember = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.ts b/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.ts index 99da8e9d02..7e56dcd1d6 100644 --- a/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.ts +++ b/test-packages/test-services-odata-v4/test-service/TestEnumTypeWithOneMember.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.d.ts b/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.d.ts index 5a17272713..9222da0aec 100644 --- a/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.js b/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.js index f0efde91ad..f9fc2306d6 100644 --- a/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.js +++ b/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestLvl2NestedComplexType = exports.TestLvl2NestedComplexTypeField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.ts b/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.ts index 6afe341fa1..ed1f02d91e 100644 --- a/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.ts +++ b/test-packages/test-services-odata-v4/test-service/TestLvl2NestedComplexType.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.d.ts b/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.d.ts index c14680badb..200bc84949 100644 --- a/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.d.ts +++ b/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.js b/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.js index 7e812d1cc1..7acb81b743 100644 --- a/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.js +++ b/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestNestedComplexType = exports.TestNestedComplexTypeField = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.ts b/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.ts index 55116572e4..bfbf9a9868 100644 --- a/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.ts +++ b/test-packages/test-services-odata-v4/test-service/TestNestedComplexType.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/index.d.ts b/test-packages/test-services-odata-v4/test-service/index.d.ts index 6b915d74f5..0bf5ec8e2e 100644 --- a/test-packages/test-services-odata-v4/test-service/index.d.ts +++ b/test-packages/test-services-odata-v4/test-service/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/index.js b/test-packages/test-services-odata-v4/test-service/index.js index aeea35cb1c..98a0392ee3 100644 --- a/test-packages/test-services-odata-v4/test-service/index.js +++ b/test-packages/test-services-odata-v4/test-service/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/index.ts b/test-packages/test-services-odata-v4/test-service/index.ts index 6b915d74f5..0bf5ec8e2e 100644 --- a/test-packages/test-services-odata-v4/test-service/index.ts +++ b/test-packages/test-services-odata-v4/test-service/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/operations.d.ts b/test-packages/test-services-odata-v4/test-service/operations.d.ts index bf47cb4df0..5e7359b586 100644 --- a/test-packages/test-services-odata-v4/test-service/operations.d.ts +++ b/test-packages/test-services-odata-v4/test-service/operations.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/operations.js b/test-packages/test-services-odata-v4/test-service/operations.js index ef89a88aff..55d02a0acd 100644 --- a/test-packages/test-services-odata-v4/test-service/operations.js +++ b/test-packages/test-services-odata-v4/test-service/operations.js @@ -20,7 +20,7 @@ exports.testActionImportSharedEntityReturnType = testActionImportSharedEntityRet exports.testActionImportSharedEntityReturnTypeCollection = testActionImportSharedEntityReturnTypeCollection; exports.testActionImportNullableTest = testActionImportNullableTest; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/operations.ts b/test-packages/test-services-odata-v4/test-service/operations.ts index a5d19b288e..df79ed767d 100644 --- a/test-packages/test-services-odata-v4/test-service/operations.ts +++ b/test-packages/test-services-odata-v4/test-service/operations.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/service.d.ts b/test-packages/test-services-odata-v4/test-service/service.d.ts index 4b39967ccf..8733591310 100644 --- a/test-packages/test-services-odata-v4/test-service/service.d.ts +++ b/test-packages/test-services-odata-v4/test-service/service.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/service.js b/test-packages/test-services-odata-v4/test-service/service.js index d2df8e82c6..c1ee26ccb4 100644 --- a/test-packages/test-services-odata-v4/test-service/service.js +++ b/test-packages/test-services-odata-v4/test-service/service.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.testService = testService; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-odata-v4/test-service/service.ts b/test-packages/test-services-odata-v4/test-service/service.ts index f70aea2c52..7f65ff64c0 100644 --- a/test-packages/test-services-odata-v4/test-service/service.ts +++ b/test-packages/test-services-odata-v4/test-service/service.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/no-schema-service/default-api.d.ts b/test-packages/test-services-openapi/no-schema-service/default-api.d.ts index b36ffe43ce..50cfe4014f 100644 --- a/test-packages/test-services-openapi/no-schema-service/default-api.d.ts +++ b/test-packages/test-services-openapi/no-schema-service/default-api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/no-schema-service/default-api.js b/test-packages/test-services-openapi/no-schema-service/default-api.js index 18bd02e4fb..4ebf5ba415 100644 --- a/test-packages/test-services-openapi/no-schema-service/default-api.js +++ b/test-packages/test-services-openapi/no-schema-service/default-api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/no-schema-service/default-api.ts b/test-packages/test-services-openapi/no-schema-service/default-api.ts index a0542a3e37..38688883bd 100644 --- a/test-packages/test-services-openapi/no-schema-service/default-api.ts +++ b/test-packages/test-services-openapi/no-schema-service/default-api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/no-schema-service/index.d.ts b/test-packages/test-services-openapi/no-schema-service/index.d.ts index 1f67cae49e..f3fb4b948d 100644 --- a/test-packages/test-services-openapi/no-schema-service/index.d.ts +++ b/test-packages/test-services-openapi/no-schema-service/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/no-schema-service/index.js b/test-packages/test-services-openapi/no-schema-service/index.js index 503edc6a65..fe552827a7 100644 --- a/test-packages/test-services-openapi/no-schema-service/index.js +++ b/test-packages/test-services-openapi/no-schema-service/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/no-schema-service/index.ts b/test-packages/test-services-openapi/no-schema-service/index.ts index 1f67cae49e..f3fb4b948d 100644 --- a/test-packages/test-services-openapi/no-schema-service/index.ts +++ b/test-packages/test-services-openapi/no-schema-service/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/default-api.d.ts b/test-packages/test-services-openapi/swagger-yaml-service/default-api.d.ts index 6c28f334cb..feb938fa57 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/default-api.d.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/default-api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/default-api.js b/test-packages/test-services-openapi/swagger-yaml-service/default-api.js index 79cc911351..ea2bbdfd9f 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/default-api.js +++ b/test-packages/test-services-openapi/swagger-yaml-service/default-api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/default-api.ts b/test-packages/test-services-openapi/swagger-yaml-service/default-api.ts index 69c43b6f0a..0919846b86 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/default-api.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/default-api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/index.d.ts b/test-packages/test-services-openapi/swagger-yaml-service/index.d.ts index 282140d398..7102579b7a 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/index.d.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/index.js b/test-packages/test-services-openapi/swagger-yaml-service/index.js index e0bf4fabc8..273f4db1d3 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/index.js +++ b/test-packages/test-services-openapi/swagger-yaml-service/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/index.ts b/test-packages/test-services-openapi/swagger-yaml-service/index.ts index 282140d398..7102579b7a 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/index.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-inheritance.d.ts b/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-inheritance.d.ts index 40600b8e77..3fee495dc2 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-inheritance.d.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-inheritance.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-inheritance.ts b/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-inheritance.ts index 40600b8e77..3fee495dc2 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-inheritance.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-inheritance.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-normalized-with-object.d.ts b/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-normalized-with-object.d.ts index 90b81a8825..9e13fe980f 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-normalized-with-object.d.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-normalized-with-object.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-normalized-with-object.ts b/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-normalized-with-object.ts index 90b81a8825..9e13fe980f 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-normalized-with-object.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/schema/entity-x-of-normalized-with-object.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/schema/index.d.ts b/test-packages/test-services-openapi/swagger-yaml-service/schema/index.d.ts index dd2e06123f..e988daeaee 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/schema/index.d.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/schema/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/schema/index.js b/test-packages/test-services-openapi/swagger-yaml-service/schema/index.js index ca9ec549d2..3daf1ae2b5 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/schema/index.js +++ b/test-packages/test-services-openapi/swagger-yaml-service/schema/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/schema/index.ts b/test-packages/test-services-openapi/swagger-yaml-service/schema/index.ts index dd2e06123f..e988daeaee 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/schema/index.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/schema/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.d.ts b/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.d.ts index 17ba8c106e..40152276c6 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.d.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.js b/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.js index 8456df24b5..82640c16c3 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.js +++ b/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.js @@ -1,6 +1,6 @@ "use strict"; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.ts b/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.ts index 4018e26903..d4e9d8c345 100644 --- a/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.ts +++ b/test-packages/test-services-openapi/swagger-yaml-service/schema/test-entity.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/default-api.d.ts b/test-packages/test-services-openapi/test-service/default-api.d.ts index f5a5668f15..de46f4d3b9 100644 --- a/test-packages/test-services-openapi/test-service/default-api.d.ts +++ b/test-packages/test-services-openapi/test-service/default-api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/default-api.js b/test-packages/test-services-openapi/test-service/default-api.js index a50ef0645d..597bfb1eff 100644 --- a/test-packages/test-services-openapi/test-service/default-api.js +++ b/test-packages/test-services-openapi/test-service/default-api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/default-api.ts b/test-packages/test-services-openapi/test-service/default-api.ts index 44032dc102..0d02b3c5d8 100644 --- a/test-packages/test-services-openapi/test-service/default-api.ts +++ b/test-packages/test-services-openapi/test-service/default-api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/entity-api.d.ts b/test-packages/test-services-openapi/test-service/entity-api.d.ts index 8650fd354a..60d68adb6e 100644 --- a/test-packages/test-services-openapi/test-service/entity-api.d.ts +++ b/test-packages/test-services-openapi/test-service/entity-api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/entity-api.js b/test-packages/test-services-openapi/test-service/entity-api.js index aadef883a4..b59ab5f249 100644 --- a/test-packages/test-services-openapi/test-service/entity-api.js +++ b/test-packages/test-services-openapi/test-service/entity-api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.EntityApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/entity-api.ts b/test-packages/test-services-openapi/test-service/entity-api.ts index 29ff493995..11e8cd8155 100644 --- a/test-packages/test-services-openapi/test-service/entity-api.ts +++ b/test-packages/test-services-openapi/test-service/entity-api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/extension-api.d.ts b/test-packages/test-services-openapi/test-service/extension-api.d.ts index d607b2c0a8..758f1aeb1d 100644 --- a/test-packages/test-services-openapi/test-service/extension-api.d.ts +++ b/test-packages/test-services-openapi/test-service/extension-api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/extension-api.js b/test-packages/test-services-openapi/test-service/extension-api.js index 02638dba78..960fd55303 100644 --- a/test-packages/test-services-openapi/test-service/extension-api.js +++ b/test-packages/test-services-openapi/test-service/extension-api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.ExtensionApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/extension-api.ts b/test-packages/test-services-openapi/test-service/extension-api.ts index cd8cf97e87..c774a10fa9 100644 --- a/test-packages/test-services-openapi/test-service/extension-api.ts +++ b/test-packages/test-services-openapi/test-service/extension-api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/index.d.ts b/test-packages/test-services-openapi/test-service/index.d.ts index 72292e7f07..8bc7c8b4fc 100644 --- a/test-packages/test-services-openapi/test-service/index.d.ts +++ b/test-packages/test-services-openapi/test-service/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/index.js b/test-packages/test-services-openapi/test-service/index.js index 85b94eeada..f703c1f1e4 100644 --- a/test-packages/test-services-openapi/test-service/index.js +++ b/test-packages/test-services-openapi/test-service/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/index.ts b/test-packages/test-services-openapi/test-service/index.ts index 72292e7f07..8bc7c8b4fc 100644 --- a/test-packages/test-services-openapi/test-service/index.ts +++ b/test-packages/test-services-openapi/test-service/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/complex-test-entity.d.ts b/test-packages/test-services-openapi/test-service/schema/complex-test-entity.d.ts index ce503d2a53..72370a7707 100644 --- a/test-packages/test-services-openapi/test-service/schema/complex-test-entity.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/complex-test-entity.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/complex-test-entity.ts b/test-packages/test-services-openapi/test-service/schema/complex-test-entity.ts index ce503d2a53..72370a7707 100644 --- a/test-packages/test-services-openapi/test-service/schema/complex-test-entity.ts +++ b/test-packages/test-services-openapi/test-service/schema/complex-test-entity.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/cyclic-child.d.ts b/test-packages/test-services-openapi/test-service/schema/cyclic-child.d.ts index 1d97730964..61595c7109 100644 --- a/test-packages/test-services-openapi/test-service/schema/cyclic-child.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/cyclic-child.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/cyclic-child.ts b/test-packages/test-services-openapi/test-service/schema/cyclic-child.ts index 1d97730964..61595c7109 100644 --- a/test-packages/test-services-openapi/test-service/schema/cyclic-child.ts +++ b/test-packages/test-services-openapi/test-service/schema/cyclic-child.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/cyclic-parent.d.ts b/test-packages/test-services-openapi/test-service/schema/cyclic-parent.d.ts index 1257ca4b0e..96e8ce6ebe 100644 --- a/test-packages/test-services-openapi/test-service/schema/cyclic-parent.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/cyclic-parent.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/cyclic-parent.ts b/test-packages/test-services-openapi/test-service/schema/cyclic-parent.ts index 1257ca4b0e..96e8ce6ebe 100644 --- a/test-packages/test-services-openapi/test-service/schema/cyclic-parent.ts +++ b/test-packages/test-services-openapi/test-service/schema/cyclic-parent.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.d.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.d.ts index e29db50b4f..9649204c3f 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.js b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.js index dd37fe7614..ca7c74bfd5 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.js +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.js @@ -1,6 +1,6 @@ "use strict"; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.ts index 0b820958f7..bf6378880b 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-a.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.d.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.d.ts index 4492360be5..b5c9cb0277 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.js b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.js index 22c15b9276..72574116f5 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.js +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.js @@ -1,6 +1,6 @@ "use strict"; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.ts index e79adbea65..eac2883570 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-child-b.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-with-mapping.d.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-with-mapping.d.ts index 037777310c..52ced765fd 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-with-mapping.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-with-mapping.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-with-mapping.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-with-mapping.ts index 4c81ead0b9..ffc0fbdd09 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-with-mapping.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-with-mapping.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-without-mapping.d.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-without-mapping.d.ts index 4bce42914f..85489827ee 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-without-mapping.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-without-mapping.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-without-mapping.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-without-mapping.ts index 3cfb7b8de1..6e88faded9 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-entity-without-mapping.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-entity-without-mapping.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.d.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.d.ts index 78b2f9d394..b109699365 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.js b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.js index 7b99a2dc96..e6b7c32de9 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.js +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.js @@ -1,6 +1,6 @@ "use strict"; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.ts index e50f2f80e4..7b0969656b 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-a.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.d.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.d.ts index 34d1ccf07f..4c4df7de2a 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.js b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.js index 42b4b767e6..d64cdf6002 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.js +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.js @@ -1,6 +1,6 @@ "use strict"; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.ts index c05801894f..8310d2a088 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-b.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.d.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.d.ts index 546b06752b..7131a22369 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.js b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.js index 0a036c3377..6fae1c31b3 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.js +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.js @@ -1,6 +1,6 @@ "use strict"; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.ts index 1156978fcd..4677e25e77 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-only-type.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.d.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.d.ts index f4d5243e1a..abefae79d3 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.js b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.js index dc37eccf69..523d46eaa1 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.js +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.js @@ -1,6 +1,6 @@ "use strict"; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.ts index 083d15ba7c..b3cd76994c 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-child-type.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent-only-type.d.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent-only-type.d.ts index 5050fab445..1c6c351306 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent-only-type.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent-only-type.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent-only-type.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent-only-type.ts index 5050fab445..1c6c351306 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent-only-type.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent-only-type.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent.d.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent.d.ts index ba6330ea8c..d951d9e915 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent.ts b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent.ts index 6ddeddde02..ac63bef375 100644 --- a/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent.ts +++ b/test-packages/test-services-openapi/test-service/schema/discriminator-object-entity-workaround-parent.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/index.d.ts b/test-packages/test-services-openapi/test-service/schema/index.d.ts index 213ff1fea1..5674c75e3c 100644 --- a/test-packages/test-services-openapi/test-service/schema/index.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/index.js b/test-packages/test-services-openapi/test-service/schema/index.js index 1989056b9b..715d53982b 100644 --- a/test-packages/test-services-openapi/test-service/schema/index.js +++ b/test-packages/test-services-openapi/test-service/schema/index.js @@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", { value: true }); /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/index.ts b/test-packages/test-services-openapi/test-service/schema/index.ts index 213ff1fea1..5674c75e3c 100644 --- a/test-packages/test-services-openapi/test-service/schema/index.ts +++ b/test-packages/test-services-openapi/test-service/schema/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/other-simple-test-entity.d.ts b/test-packages/test-services-openapi/test-service/schema/other-simple-test-entity.d.ts index 089f0b6d03..dd25619a55 100644 --- a/test-packages/test-services-openapi/test-service/schema/other-simple-test-entity.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/other-simple-test-entity.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/other-simple-test-entity.ts b/test-packages/test-services-openapi/test-service/schema/other-simple-test-entity.ts index 089f0b6d03..dd25619a55 100644 --- a/test-packages/test-services-openapi/test-service/schema/other-simple-test-entity.ts +++ b/test-packages/test-services-openapi/test-service/schema/other-simple-test-entity.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/schema-123456.d.ts b/test-packages/test-services-openapi/test-service/schema/schema-123456.d.ts index d8957d5cf8..d68670346b 100644 --- a/test-packages/test-services-openapi/test-service/schema/schema-123456.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/schema-123456.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/schema-123456.js b/test-packages/test-services-openapi/test-service/schema/schema-123456.js index e564af5c8f..ece8c93e3b 100644 --- a/test-packages/test-services-openapi/test-service/schema/schema-123456.js +++ b/test-packages/test-services-openapi/test-service/schema/schema-123456.js @@ -1,6 +1,6 @@ "use strict"; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/schema-123456.ts b/test-packages/test-services-openapi/test-service/schema/schema-123456.ts index 84d07b9df1..06f39008f4 100644 --- a/test-packages/test-services-openapi/test-service/schema/schema-123456.ts +++ b/test-packages/test-services-openapi/test-service/schema/schema-123456.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols-1.d.ts b/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols-1.d.ts index be04fb7bc2..a95085079a 100644 --- a/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols-1.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols-1.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols-1.ts b/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols-1.ts index be04fb7bc2..a95085079a 100644 --- a/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols-1.ts +++ b/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols-1.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols.d.ts b/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols.d.ts index 21b64fd4e8..2ab0d271fd 100644 --- a/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols.ts b/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols.ts index 21b64fd4e8..2ab0d271fd 100644 --- a/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols.ts +++ b/test-packages/test-services-openapi/test-service/schema/simple-test-entity-with-symbols.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/simple-test-entity.d.ts b/test-packages/test-services-openapi/test-service/schema/simple-test-entity.d.ts index f26db649b9..b93c2a2647 100644 --- a/test-packages/test-services-openapi/test-service/schema/simple-test-entity.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/simple-test-entity.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/simple-test-entity.js b/test-packages/test-services-openapi/test-service/schema/simple-test-entity.js index 197b2e0943..0c3b117288 100644 --- a/test-packages/test-services-openapi/test-service/schema/simple-test-entity.js +++ b/test-packages/test-services-openapi/test-service/schema/simple-test-entity.js @@ -1,6 +1,6 @@ "use strict"; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/simple-test-entity.ts b/test-packages/test-services-openapi/test-service/schema/simple-test-entity.ts index 4dee8bc01a..d24af3e3d6 100644 --- a/test-packages/test-services-openapi/test-service/schema/simple-test-entity.ts +++ b/test-packages/test-services-openapi/test-service/schema/simple-test-entity.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/test-entity.d.ts b/test-packages/test-services-openapi/test-service/schema/test-entity.d.ts index bc4eeb59b9..907bbfeeb3 100644 --- a/test-packages/test-services-openapi/test-service/schema/test-entity.d.ts +++ b/test-packages/test-services-openapi/test-service/schema/test-entity.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/schema/test-entity.ts b/test-packages/test-services-openapi/test-service/schema/test-entity.ts index bc4eeb59b9..907bbfeeb3 100644 --- a/test-packages/test-services-openapi/test-service/schema/test-entity.ts +++ b/test-packages/test-services-openapi/test-service/schema/test-entity.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/tag-dot-api.d.ts b/test-packages/test-services-openapi/test-service/tag-dot-api.d.ts index 0c65aa0733..eb87653d8f 100644 --- a/test-packages/test-services-openapi/test-service/tag-dot-api.d.ts +++ b/test-packages/test-services-openapi/test-service/tag-dot-api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/tag-dot-api.js b/test-packages/test-services-openapi/test-service/tag-dot-api.js index f991124a5d..bb01faf49a 100644 --- a/test-packages/test-services-openapi/test-service/tag-dot-api.js +++ b/test-packages/test-services-openapi/test-service/tag-dot-api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TagDotApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/tag-dot-api.ts b/test-packages/test-services-openapi/test-service/tag-dot-api.ts index 7e9d0eb2b2..c3012153dc 100644 --- a/test-packages/test-services-openapi/test-service/tag-dot-api.ts +++ b/test-packages/test-services-openapi/test-service/tag-dot-api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/tag-space-api.d.ts b/test-packages/test-services-openapi/test-service/tag-space-api.d.ts index c26d1a2154..1ddcbebded 100644 --- a/test-packages/test-services-openapi/test-service/tag-space-api.d.ts +++ b/test-packages/test-services-openapi/test-service/tag-space-api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/tag-space-api.js b/test-packages/test-services-openapi/test-service/tag-space-api.js index 21f3a0dc09..a3d03765f2 100644 --- a/test-packages/test-services-openapi/test-service/tag-space-api.js +++ b/test-packages/test-services-openapi/test-service/tag-space-api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TagSpaceApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/tag-space-api.ts b/test-packages/test-services-openapi/test-service/tag-space-api.ts index b9818e8c48..b520574bc9 100644 --- a/test-packages/test-services-openapi/test-service/tag-space-api.ts +++ b/test-packages/test-services-openapi/test-service/tag-space-api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/test-case-api.d.ts b/test-packages/test-services-openapi/test-service/test-case-api.d.ts index f471954bba..357e8b2c65 100644 --- a/test-packages/test-services-openapi/test-service/test-case-api.d.ts +++ b/test-packages/test-services-openapi/test-service/test-case-api.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/test-case-api.js b/test-packages/test-services-openapi/test-service/test-case-api.js index 5a4b05ae30..ffa003604d 100644 --- a/test-packages/test-services-openapi/test-service/test-case-api.js +++ b/test-packages/test-services-openapi/test-service/test-case-api.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.TestCaseApi = void 0; /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ diff --git a/test-packages/test-services-openapi/test-service/test-case-api.ts b/test-packages/test-services-openapi/test-service/test-case-api.ts index 06dc9884a9..fa9c5a0df6 100644 --- a/test-packages/test-services-openapi/test-service/test-case-api.ts +++ b/test-packages/test-services-openapi/test-service/test-case-api.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. + * Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. * * This is a generated file powered by the SAP Cloud SDK for JavaScript. */ From 23a7caddc8560aca63007c2ace0a5d9ee677d934 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Thu, 8 Jan 2026 13:10:54 +0100 Subject: [PATCH 44/58] move requestas to technical user type --- .../connectivity/src/scp-cf/destination/ias-types.ts | 10 +++++----- packages/connectivity/src/scp-cf/identity-service.ts | 7 ++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/ias-types.ts b/packages/connectivity/src/scp-cf/destination/ias-types.ts index a610c83109..1d2e4327ed 100644 --- a/packages/connectivity/src/scp-cf/destination/ias-types.ts +++ b/packages/connectivity/src/scp-cf/destination/ias-types.ts @@ -53,11 +53,6 @@ export interface IasOptionsBase { * May be required for multi-tenant communication. */ appTid?: string; - /** - * Specifies whether the token request is made in the context of the current tenant or the provider tenant. - * @defaultValue 'current-tenant' - */ - requestAs?: 'current-tenant' | 'provider-tenant'; /** * Additional parameters to be sent along with the token request. */ @@ -77,6 +72,11 @@ export interface IasOptionsTechnicalUser extends IasOptionsBase { * Assertion not used for technical user authentication. */ assertion?: never; + /** + * Specifies whether the token request is made in the context of the current tenant or the provider tenant. + * @defaultValue 'current-tenant' + */ + requestAs?: 'current-tenant' | 'provider-tenant'; } /** diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index abd85a7d67..47ffbd5b59 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -157,9 +157,6 @@ async function getIasClientCredentialsTokenImpl( arg.assertion ); - const authenticationType = - arg.authenticationType || 'OAuth2ClientCredentials'; - const tokenOptions: IdentityService.TokenFetchOptions & IdentityService.IdentityServiceTokenFetchOptions = { token_format: 'jwt' @@ -181,7 +178,7 @@ async function getIasClientCredentialsTokenImpl( let response: undefined | IdentityService.TokenFetchResponse; - if (authenticationType === 'OAuth2JWTBearer') { + if (arg.authenticationType === 'OAuth2JWTBearer') { // JWT bearer grant for business user propagation if (!arg.assertion) { throw new Error( @@ -201,7 +198,7 @@ async function getIasClientCredentialsTokenImpl( ); } else { if (!arg.appTid) { - const requestAs = arg?.requestAs ?? 'current-tenant'; + const requestAs = arg.requestAs ?? 'current-tenant'; if (requestAs === 'provider-tenant') { tokenOptions.app_tid = arg.serviceCredentials.app_tid; } else if (requestAs === 'current-tenant') { From 893db550093f97f9a1864834cacc933c1c26a487 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 9 Jan 2026 09:04:40 +0100 Subject: [PATCH 45/58] clarify and add types to `extraParams` --- .../connectivity/src/scp-cf/destination/ias-types.ts | 10 ++++++++-- .../connectivity/src/scp-cf/identity-service.spec.ts | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/ias-types.ts b/packages/connectivity/src/scp-cf/destination/ias-types.ts index 1d2e4327ed..f3dbcb13fb 100644 --- a/packages/connectivity/src/scp-cf/destination/ias-types.ts +++ b/packages/connectivity/src/scp-cf/destination/ias-types.ts @@ -1,5 +1,6 @@ import type { Xor } from '@sap-cloud-sdk/util'; import type { AuthenticationType } from './destination-service-types'; +import type { IdentityService } from '@sap/xssec'; /** * The application resource for which the token is requested for App-to-App communication. @@ -54,9 +55,14 @@ export interface IasOptionsBase { */ appTid?: string; /** - * Additional parameters to be sent along with the token request. + * Additional parameters for the token request to be forwarded to the token fetching function + * of `@sap/xssec`. */ - extraParams?: Record; + extraParams?: Omit< + IdentityService.TokenFetchOptions & + IdentityService.IdentityServiceTokenFetchOptions, + 'token_format' | 'resource' | 'app_tid' + >; } /** diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index a8e3bace31..d0735386b6 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -207,7 +207,7 @@ describe('getIasClientCredentialsToken', () => { mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); await getIasClientCredentialsToken(mockIasService, { - extraParams: { custom_param: 'custom_value' } + extraParams: { custom_param: 'custom_value' } as any }); expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ From 78ee44b52a2eb2c6551f8bcbda3291f3b71c7ecf Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 9 Jan 2026 13:52:11 +0100 Subject: [PATCH 46/58] improve refresh token handling --- .../src/scp-cf/identity-service.spec.ts | 44 +++++++++++++++++-- .../src/scp-cf/identity-service.ts | 12 ++++- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index d0735386b6..3dd421e664 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -155,7 +155,7 @@ describe('getIasClientCredentialsToken', () => { app_tid: 'custom-tenant-id', custom_iss: 'https://tenant.accounts.ondemand.com', ias_apis: ['dummy'], - scimId: undefined + scim_id: undefined }); expect(mockFetchClientCredentialsToken).toHaveBeenCalledWith({ token_format: 'jwt' @@ -306,7 +306,7 @@ describe('getIasClientCredentialsToken', () => { expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(userAssertion, { resource: 'urn:sap:identity:application:provider:name:my-app', app_tid: 'tenant-123', - refresh_token: '0', + refresh_expiry: 0, token_format: 'jwt' }); }); @@ -386,7 +386,7 @@ describe('getIasClientCredentialsToken', () => { authenticationType: 'OAuth2JWTBearer', assertion: userAssertion, requestAs: 'provider-tenant' - }); + } as any); expect(mockFetchJwtBearerToken).toHaveBeenCalled(); const [, tokenOptions] = mockFetchJwtBearerToken.mock.calls[0]; @@ -500,6 +500,44 @@ describe('getIasClientCredentialsToken', () => { expect(mockFetchJwtBearerToken).toHaveBeenCalled(); }); + it('disables refresh tokens for JWT bearer exchanges with APP-to-APP flow', async () => { + const assertion = signedJwt({ + iss: subscriberUrl, + user_uuid: 'user-123' + }); + + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion, + resource: { name: 'my-app' } + }); + + expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(assertion, { + resource: 'urn:sap:identity:application:provider:name:my-app', + refresh_expiry: 0, + token_format: 'jwt' + }); + }); + + it('disables refresh tokens for JWT bearer exchanges if app_tid is set', async () => { + const assertion = signedJwt({ + iss: subscriberUrl, + user_uuid: 'user-123' + }); + + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion, + appTid: 'some-tenant-id' + }); + + expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(assertion, { + app_tid: 'some-tenant-id', + refresh_expiry: 0, + token_format: 'jwt' + }); + }); + it('caches subscriber instances per URL', async () => { const subscriber1Url = 'https://subscriber1.accounts.ondemand.com'; const subscriber2Url = 'https://subscriber2.accounts.ondemand.com'; diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 47ffbd5b59..3b0c7d0dca 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -186,10 +186,17 @@ async function getIasClientCredentialsTokenImpl( ); } + // Disable refresh token for App-To-App JWT bearer token exchange (recommended for better performance) + if (arg.resource && tokenOptions.refresh_expiry === undefined) { + tokenOptions.refresh_expiry = 0; + } + // Workaround for IAS bug // https://github.com/SAP/cloud-sdk-java/blob/61903347b607a8397f7930709cd52526f05269b1/cloudplatform/connectivity-oauth/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/OAuth2Service.java#L225-L236 - if (arg.appTid) { - (tokenOptions as any).refresh_token = '0'; + if (tokenOptions.app_tid) { + // TODO: Use `refresh_token` instead to match the Java SDK (currently not forwarded by @sap/xssec) + // Workaround: Use `refresh_expiry` instead for now to disable refresh tokens + tokenOptions.refresh_expiry = 0; } response = await identityService.fetchJwtBearerToken( @@ -197,6 +204,7 @@ async function getIasClientCredentialsTokenImpl( tokenOptions ); } else { + // Technical user client credentials grant if (!arg.appTid) { const requestAs = arg.requestAs ?? 'current-tenant'; if (requestAs === 'provider-tenant') { From 9f832f52da82845f563642b9508651bc5797c9ab Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 12 Jan 2026 10:24:29 +0100 Subject: [PATCH 47/58] remove todo --- packages/connectivity/src/scp-cf/identity-service.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 3b0c7d0dca..3116779b63 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -192,10 +192,9 @@ async function getIasClientCredentialsTokenImpl( } // Workaround for IAS bug - // https://github.com/SAP/cloud-sdk-java/blob/61903347b607a8397f7930709cd52526f05269b1/cloudplatform/connectivity-oauth/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/OAuth2Service.java#L225-L236 + // JAVA SDK: https://github.com/SAP/cloud-sdk-java/blob/61903347b607a8397f7930709cd52526f05269b1/cloudplatform/connectivity-oauth/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/OAuth2Service.java#L225-L236 + // Issue: https://jira.tools.sap/browse/SECREQ-5220 if (tokenOptions.app_tid) { - // TODO: Use `refresh_token` instead to match the Java SDK (currently not forwarded by @sap/xssec) - // Workaround: Use `refresh_expiry` instead for now to disable refresh tokens tokenOptions.refresh_expiry = 0; } From d49c81895ab8969440780594c6009dadf4ab024c Mon Sep 17 00:00:00 2001 From: David Knaack Date: Thu, 15 Jan 2026 10:32:47 +0100 Subject: [PATCH 48/58] re-add credentials cache --- .../client-credentials-token-cache.spec.ts | 172 ++++++++- .../scp-cf/client-credentials-token-cache.ts | 40 +- .../service-binding-to-destination.spec.ts | 351 ++++++++++++++++++ .../service-binding-to-destination.ts | 61 ++- .../src/scp-cf/identity-service.spec.ts | 90 ----- .../src/scp-cf/identity-service.ts | 10 - .../src/scp-cf/token-accessor.spec.ts | 28 +- .../connectivity/src/scp-cf/token-accessor.ts | 4 +- 8 files changed, 635 insertions(+), 121 deletions(-) diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts index a9184bbf3d..d4843452fb 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts @@ -1,5 +1,8 @@ import { createLogger } from '@sap-cloud-sdk/util'; -import { clientCredentialsTokenCache } from './client-credentials-token-cache'; +import { + clientCredentialsTokenCache, + getCacheKey +} from './client-credentials-token-cache'; const oneHourInSeconds = 60 * 60; @@ -18,6 +21,7 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', + undefined, validToken ); @@ -25,7 +29,8 @@ describe('ClientCredentialsTokenCache', () => { const valid = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - 'clientid' + 'clientid', + undefined ); expect(valid).toEqual(validToken); @@ -45,13 +50,15 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', + undefined, expiredToken ); jest.advanceTimersByTime(oneHourInSeconds * 2 * 1000); const expired = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - 'clientid' + 'clientid', + undefined ); expect(expired).toBeUndefined(); @@ -72,6 +79,7 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', + undefined, validToken ); @@ -79,7 +87,8 @@ describe('ClientCredentialsTokenCache', () => { const invalid = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - undefined as any + undefined as any, + undefined ); expect(invalid).toBeUndefined(); @@ -87,4 +96,159 @@ describe('ClientCredentialsTokenCache', () => { 'Cannot create cache key for client credentials token cache. The given client ID is undefined.' ); }); + + describe('IAS resource parameter support', () => { + const validToken = { + access_token: '1234567890', + token_type: 'Bearer', + expires_in: oneHourInSeconds * 3, + jti: '', + scope: '' + }; + + beforeEach(() => { + clientCredentialsTokenCache.clear(); + }); + + it('should cache and retrieve token with resource name', () => { + const resource = { name: 'my-app' }; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resource, + validToken + ); + + const cached = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource + ); + + expect(cached).toEqual(validToken); + }); + + it('should cache and retrieve token with resource clientId', () => { + const resource = { providerClientId: 'resource-client-123' }; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resource, + validToken + ); + + const cached = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource + ); + + expect(cached).toEqual(validToken); + }); + + it('should cache and retrieve token with resource clientId and tenantId', () => { + const resource = { + providerClientId: 'resource-client-123', + providerTenantId: 'tenant-456' + }; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resource, + validToken + ); + + const cached = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource + ); + + expect(cached).toEqual(validToken); + }); + + it('should isolate cache by resource name', () => { + const resource1 = { name: 'app-1' }; + const resource2 = { name: 'app-2' }; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resource1, + validToken + ); + + const cached1 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource1 + ); + const cached2 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource2 + ); + + expect(cached1).toEqual(validToken); + expect(cached2).toBeUndefined(); + }); + + it('should isolate cache by resource clientId', () => { + const resource1 = { providerClientId: 'client-1' }; + const resource2 = { providerClientId: 'client-2' }; + + clientCredentialsTokenCache.cacheToken( + 'subscriber-tenant', + 'clientid', + resource1, + validToken + ); + + const cached1 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource1 + ); + const cached2 = clientCredentialsTokenCache.getToken( + 'subscriber-tenant', + 'clientid', + resource2 + ); + + expect(cached1).toEqual(validToken); + expect(cached2).toBeUndefined(); + }); + + it('should generate correct cache key with resource name', () => { + const key = getCacheKey('tenant-123', 'client-id', { name: 'my-app' }); + expect(key).toBe('tenant-123:client-id:name=my-app'); + }); + + it('should generate correct cache key with resource clientId only', () => { + const key = getCacheKey('tenant-123', 'client-id', { + providerClientId: 'resource-client-123' + }); + expect(key).toBe( + 'tenant-123:client-id:provider-clientId=resource-client-123' + ); + }); + + it('should generate correct cache key with resource clientId and tenantId', () => { + const key = getCacheKey('tenant-123', 'client-id', { + providerClientId: 'resource-client-123', + providerTenantId: 'tenant-456' + }); + expect(key).toBe( + 'tenant-123:client-id:provider-clientId=resource-client-123:provider-tenantId=tenant-456' + ); + }); + + it('should generate cache key without resource when not provided', () => { + const key = getCacheKey('tenant-123', 'client-id'); + expect(key).toBe('tenant-123:client-id'); + }); + }); }); diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts index 575b6a1b94..d69cac2d30 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sap-cloud-sdk/util'; import { Cache } from './cache'; import type { ClientCredentialsResponse } from './xsuaa-service-types'; +import type { IasResource } from './destination'; const logger = createLogger({ package: 'connectivity', @@ -12,16 +13,18 @@ const ClientCredentialsTokenCache = ( ) => ({ getToken: ( tenantId: string | undefined, - clientId: string + clientId: string, + resource?: IasResource ): ClientCredentialsResponse | undefined => - cache.get(getCacheKey(tenantId, clientId)), + cache.get(getCacheKey(tenantId, clientId, resource)), cacheToken: ( tenantId: string | undefined, clientId: string, + resource: IasResource | undefined, token: ClientCredentialsResponse ): void => { - cache.set(getCacheKey(tenantId, clientId), { + cache.set(getCacheKey(tenantId, clientId, resource), { entry: token, expires: token.expires_in ? Date.now() + token.expires_in * 1000 @@ -34,15 +37,38 @@ const ClientCredentialsTokenCache = ( getCacheInstance: () => cache }); +/** + * Normalizes the IAS resource parameter to a consistent string format for cache key. + * @param resource - The resource parameter from iasOptions. + * @returns Normalized resource string or empty string if not provided. + * @internal + */ +function normalizeResource(resource?: IasResource): string | undefined { + if (!resource) { + return undefined; + } + if ('name' in resource) { + return `name=${resource.name}`; + } + + let normalized = `provider-clientId=${resource.providerClientId}`; + if (resource.providerTenantId) { + normalized += `:provider-tenantId=${resource.providerTenantId}`; + } + return normalized; +} + /** * * @internal * @param tenantId - The ID of the tenant to cache the token for. - * @param clientId - Client ID to fetch the token. + * @param clientId - ClientId to fetch the token. + * @param resource - Optional resource parameter (for IAS app2app scenarios). * @returns The cache key. */ export function getCacheKey( tenantId: string | undefined, - clientId: string + clientId: string, + resource?: IasResource ): string | undefined { if (!tenantId) { logger.warn( @@ -57,6 +83,10 @@ export function getCacheKey( return; } const parts = [tenantId, clientId]; + const resourceStr = normalizeResource(resource); + if (resourceStr) { + parts.push(resourceStr); + } return parts.join(':'); } diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index 1a68fe2bf3..5a4e379885 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -1,5 +1,6 @@ import { resolveServiceBinding } from '../environment-accessor/service-bindings'; import { getIasClientCredentialsToken } from '../identity-service'; +import { clientCredentialsTokenCache } from '../client-credentials-token-cache'; import { decodeJwt } from '../jwt'; import { serviceToken } from '../token-accessor'; import { @@ -138,6 +139,11 @@ describe('service binding to destination', () => { process.env.VCAP_SERVICES = JSON.stringify(services); }); + beforeEach(() => { + clientCredentialsTokenCache.clear(); + jest.clearAllMocks(); + }); + afterAll(() => { jest.clearAllMocks(); delete process.env.VCAP_SERVICES; @@ -366,4 +372,349 @@ describe('service binding to destination', () => { }) ); }); + + describe('iasBindingToDestination requestAs handling', () => { + beforeEach(() => { + jest.clearAllMocks(); + clientCredentialsTokenCache.clear(); + // Re-apply mock after clearAllMocks + (getIasClientCredentialsToken as jest.Mock).mockResolvedValue({ + access_token: 'ias-access-token', + token_type: 'Bearer', + expires_in: 3600, + scope: 'openid', + jti: 'mock-jti' + }); + }); + + it('uses provider tenant when requestAs is provider-tenant', async () => { + const identityServiceWithAppTid = { + ...services.identity[0], + app_tid: 'provider-tenant-id', + credentials: { + ...services.identity[0].credentials, + app_tid: 'provider-tenant-id' + } + }; + + process.env.VCAP_SERVICES = JSON.stringify({ + ...services, + identity: [identityServiceWithAppTid] + }); + + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + iasOptions: { requestAs: 'provider-tenant' } + } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledWith( + expect.objectContaining({ + label: 'identity' + }), + expect.objectContaining({}) + ); + + // Verify cache was populated with provider tenant + const cached = clientCredentialsTokenCache.getToken( + 'provider-tenant-id', + 'identity-clientid', + undefined + ); + expect(cached?.access_token).toBe('ias-access-token'); + + // Restore original VCAP_SERVICES + process.env.VCAP_SERVICES = JSON.stringify(services); + }); + + it('uses current tenant when requestAs is current-tenant', async () => { + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'current-tenant-id' }, + iasOptions: { requestAs: 'current-tenant' } + } + ); + + // Verify cache was populated with current tenant + const cached = clientCredentialsTokenCache.getToken( + 'current-tenant-id', + 'identity-clientid', + undefined + ); + expect(cached?.access_token).toBe('ias-access-token'); + }); + + it('defaults to current tenant when requestAs is not specified', async () => { + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'current-tenant-id' } + } + ); + + // Verify cache was populated with current tenant (default behavior) + const cached = clientCredentialsTokenCache.getToken( + 'current-tenant-id', + 'identity-clientid', + undefined + ); + expect(cached?.access_token).toBe('ias-access-token'); + }); + + it('prioritizes explicit appTid over requestAs', async () => { + const identityServiceWithAppTid = { + ...services.identity[0], + app_tid: 'provider-tenant-id', + credentials: { + ...services.identity[0].credentials, + app_tid: 'provider-tenant-id' + } + }; + + process.env.VCAP_SERVICES = JSON.stringify({ + ...services, + identity: [identityServiceWithAppTid] + }); + + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + iasOptions: { + requestAs: 'provider-tenant', + appTid: 'explicit-tenant-789' + } + } + ); + + // Verify cache was populated with explicit appTid, not provider tenant + const cached = clientCredentialsTokenCache.getToken( + 'explicit-tenant-789', + 'identity-clientid', + undefined + ); + expect(cached?.access_token).toBe('ias-access-token'); + + // Restore original VCAP_SERVICES + process.env.VCAP_SERVICES = JSON.stringify(services); + }); + }); + + describe('iasBindingToDestination cache functionality', () => { + beforeEach(() => { + jest.clearAllMocks(); + clientCredentialsTokenCache.clear(); + // Re-apply mock after clearAllMocks + (getIasClientCredentialsToken as jest.Mock).mockResolvedValue({ + access_token: 'ias-access-token', + token_type: 'Bearer', + expires_in: 3600, + scope: 'openid', + jti: 'mock-jti' + }); + }); + + it('caches IAS token after first request', async () => { + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { jwt: { app_tid: 'tenant-123' } } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Second call should use cached token + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { jwt: { app_tid: 'tenant-123' } } + ); + + // Should still only be called once due to cache hit + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + }); + + it('does not cache IAS token if useCache is false', async () => { + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { jwt: { app_tid: 'tenant-123' }, useCache: false } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Second call should use cached token + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { jwt: { app_tid: 'tenant-123' }, useCache: false } + ); + + // Should be called twice - no caching + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + }); + + it('does cache IAS token if useCache is true', async () => { + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { jwt: { app_tid: 'tenant-123' }, useCache: true } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Second call should use cached token + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { jwt: { app_tid: 'tenant-123' }, useCache: true } + ); + + // Should still only be called once due to cache hit + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + }); + + it('uses cache key with resource parameter', async () => { + const resource = { name: 'my-app' }; + + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'tenant-123' }, + iasOptions: { resource } + } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Second call with same resource should use cache + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'tenant-123' }, + iasOptions: { resource } + } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + }); + + it('does not use cache for different resource parameters', async () => { + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'tenant-123' }, + iasOptions: { resource: { name: 'app1' } } + } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Second call with different resource should NOT use cache + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'tenant-123' }, + iasOptions: { resource: { name: 'app2' } } + } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + }); + + it('isolates cache by tenant (appTid)', async () => { + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { jwt: { app_tid: 'tenant-123' } } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Different tenant should not use cache + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { jwt: { app_tid: 'tenant-456' } } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + }); + + it('supports resource with providerClientId', async () => { + const resource = { providerClientId: 'resource-client-123' }; + + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'tenant-123' }, + iasOptions: { resource } + } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Second call with same resource should use cache + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'tenant-123' }, + iasOptions: { resource } + } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + }); + + it('supports resource with providerClientId and providerTenantId', async () => { + const resource = { + providerClientId: 'resource-client-123', + providerTenantId: 'resource-tenant-456' + }; + + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'tenant-123' }, + iasOptions: { resource } + } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Second call with same resource should use cache + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'tenant-123' }, + iasOptions: { resource } + } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + }); + + it('does not cache JWT bearer tokens (OAuth2JWTBearer)', async () => { + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'tenant-123' }, + iasOptions: { + authenticationType: 'OAuth2JWTBearer', + assertion: 'user-jwt-token' + } + } + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Second call should NOT use cache for JWT bearer tokens + await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + jwt: { app_tid: 'tenant-123' }, + iasOptions: { + authenticationType: 'OAuth2JWTBearer', + assertion: 'user-jwt-token' + } + } + ); + + // Should be called twice - no caching for JWT bearer + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + }); + }); }); diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index 8bc0f96d76..de3cfbf63c 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -1,12 +1,16 @@ import { serviceToken } from '../token-accessor'; import { decodeJwt } from '../jwt'; import { getIasClientCredentialsToken } from '../identity-service'; +import { clientCredentialsTokenCache } from '../client-credentials-token-cache'; import type { Service } from '../environment-accessor'; import type { ServiceBindingTransformFunction, ServiceBindingTransformOptions } from './destination-from-vcap'; import type { Destination } from './destination-service-types'; +import type { IasOptions } from './ias-types'; +import type { IasClientCredentialsResponse } from '../identity-service'; +import type { CachingOptions } from '../cache'; /** * @internal @@ -186,12 +190,61 @@ async function iasBindingToDestination( service: Service, options?: ServiceBindingTransformOptions ): Promise { - const { access_token } = await getIasClientCredentialsToken(service, { - jwt: options?.jwt, + const iasOptions = { + authenticationType: 'OAuth2ClientCredentials' as const, + useCache: options?.useCache !== false, ...(options?.iasOptions || {}) - }); + } as IasOptions & CachingOptions; + + let accessToken: string | undefined; + + if (iasOptions.authenticationType === 'OAuth2ClientCredentials') { + // Technical user client credentials grant + if (!iasOptions.appTid) { + const requestAs = iasOptions.requestAs ?? 'current-tenant'; + if (requestAs === 'provider-tenant') { + iasOptions.appTid = service.app_tid; + } else if (requestAs === 'current-tenant') { + iasOptions.appTid = options?.jwt?.app_tid; + } + + if (iasOptions.useCache) { + const cached = clientCredentialsTokenCache.getToken( + iasOptions.appTid, + service.credentials.clientid, + iasOptions.resource + ); + if (cached) { + accessToken = cached.access_token; + } + } + } + } + + let response: IasClientCredentialsResponse | undefined; + if (!accessToken) { + response = await getIasClientCredentialsToken(service, { + jwt: options?.jwt, + ...(options?.iasOptions || {}) + }); + accessToken = response.access_token; + + if ( + iasOptions.authenticationType === 'OAuth2ClientCredentials' && + iasOptions.useCache && + response + ) { + clientCredentialsTokenCache.cacheToken( + iasOptions.appTid, + service.credentials.clientid, + iasOptions.resource, + response + ); + } + } + const destination = buildClientCredentialsDestination( - access_token, + accessToken, options?.iasOptions?.targetUrl ?? service.credentials.url, service.name ); diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 3dd421e664..29b5abc917 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -312,96 +312,6 @@ describe('getIasClientCredentialsToken', () => { }); }); - describe('requestAs behavior', () => { - const providerTenantId = 'provider-tenant-id-123'; - - const serviceWithProviderTenant: Service = { - ...mockIasService, - credentials: { - ...mockIasService.credentials, - app_tid: providerTenantId - } - }; - - beforeEach(() => { - jest.clearAllMocks(); - identityServicesCache.clear(); - mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); - }); - - it('defaults to current tenant and forwards jwt.app_tid when provided', async () => { - await getIasClientCredentialsToken(serviceWithProviderTenant, { - jwt: { app_tid: 'current-tenant-app-tid' } - }); - - expect(mockFetchClientCredentialsToken).toHaveBeenCalled(); - const callArg = mockFetchClientCredentialsToken.mock.calls[0][0]; - expect(callArg).toEqual( - expect.objectContaining({ - token_format: 'jwt', - app_tid: 'current-tenant-app-tid' - }) - ); - }); - - it("uses provider tenant when requestAs is 'provider-tenant'", async () => { - await getIasClientCredentialsToken(serviceWithProviderTenant, { - requestAs: 'provider-tenant' - }); - - expect(mockFetchClientCredentialsToken).toHaveBeenCalled(); - const callArg = mockFetchClientCredentialsToken.mock.calls[0][0]; - expect(callArg).toEqual( - expect.objectContaining({ - token_format: 'jwt', - app_tid: providerTenantId - }) - ); - }); - - it('prioritizes explicit appTid over requestAs', async () => { - await getIasClientCredentialsToken(serviceWithProviderTenant, { - requestAs: 'provider-tenant', - appTid: 'explicit-tenant-789' - }); - - const callArg = mockFetchClientCredentialsToken.mock.calls[0][0]; - expect(callArg).toEqual( - expect.objectContaining({ - token_format: 'jwt', - app_tid: 'explicit-tenant-789' - }) - ); - }); - - it('ignores requestAs for JWT bearer flow', async () => { - mockFetchJwtBearerToken.mockResolvedValue(mockTokenResponse); - - const userAssertion = signedJwt({ - iss: 'https://tenant.accounts.ondemand.com', - user_uuid: 'user-123' - }); - - await getIasClientCredentialsToken(mockIasService, { - authenticationType: 'OAuth2JWTBearer', - assertion: userAssertion, - requestAs: 'provider-tenant' - } as any); - - expect(mockFetchJwtBearerToken).toHaveBeenCalled(); - const [, tokenOptions] = mockFetchJwtBearerToken.mock.calls[0]; - expect(tokenOptions).toEqual({ token_format: 'jwt' }); - }); - - it("does not set app_tid when requestAs is 'current-tenant' and jwt is missing", async () => { - await getIasClientCredentialsToken(serviceWithProviderTenant, { - requestAs: 'current-tenant' - }); - - const callArg = mockFetchClientCredentialsToken.mock.calls[0][0]; - expect(callArg).toEqual({ token_format: 'jwt' }); - }); - }); describe('multi-tenant subscriber routing', () => { const providerUrl = 'https://provider.accounts.ondemand.com'; const subscriberUrl = 'https://subscriber.accounts.ondemand.com'; diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 3116779b63..266f14d1e9 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -204,16 +204,6 @@ async function getIasClientCredentialsTokenImpl( ); } else { // Technical user client credentials grant - if (!arg.appTid) { - const requestAs = arg.requestAs ?? 'current-tenant'; - if (requestAs === 'provider-tenant') { - tokenOptions.app_tid = arg.serviceCredentials.app_tid; - } else if (requestAs === 'current-tenant') { - tokenOptions.app_tid = arg.jwt?.app_tid; - } - } - - // Client credentials for technical users response = await identityService.fetchClientCredentialsToken(tokenOptions); } diff --git a/packages/connectivity/src/scp-cf/token-accessor.spec.ts b/packages/connectivity/src/scp-cf/token-accessor.spec.ts index deb0fd0dfe..f1739c04bf 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.spec.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.spec.ts @@ -205,11 +205,13 @@ describe('token accessor', () => { const providerTokenFromCache = clientCredentialsTokenCache.getToken( providerUserPayload.zid, - destinationBindingClientSecretMock.credentials.clientid + destinationBindingClientSecretMock.credentials.clientid, + undefined ); const subscriberTokenFromCache = clientCredentialsTokenCache.getToken( subscriberUserPayload.zid, - destinationBindingClientSecretMock.credentials.clientid + destinationBindingClientSecretMock.credentials.clientid, + undefined ); expect(providerTokenFromCache?.access_token).toEqual(providerToken); @@ -218,23 +220,33 @@ describe('token accessor', () => { expect( clientCredentialsTokenCache.getToken( 'https://doesnotexist.example.com', - destinationBindingClientSecretMock.credentials.clientid + destinationBindingClientSecretMock.credentials.clientid, + undefined ) ).toBeUndefined(); expect( clientCredentialsTokenCache.getToken( 'https://doesnotexist.example.com', - 'schmusername' + 'schmusername', + undefined ) ).toBeUndefined(); expect( - clientCredentialsTokenCache.getToken(providerXsuaaUrl, 'schmusername') + clientCredentialsTokenCache.getToken( + providerXsuaaUrl, + 'schmusername', + undefined + ) ).toBeUndefined(); expect( - clientCredentialsTokenCache.getToken(subscriberXsuaaUrl, 'schmusername') + clientCredentialsTokenCache.getToken( + subscriberXsuaaUrl, + 'schmusername', + undefined + ) ).toBeUndefined(); }); @@ -320,6 +332,7 @@ describe('token accessor', () => { clientCredentialsTokenCache.cacheToken( destinationBindingClientSecretMock.credentials.tenantid, destinationBindingClientSecretMock.credentials.clientid, + undefined, { access_token: token } as ClientCredentialsResponse ); @@ -348,7 +361,8 @@ describe('token accessor', () => { expect( clientCredentialsTokenCache.getToken( destinationBindingClientSecretMock.credentials.tenantid, - destinationBindingClientSecretMock.credentials.clientid + destinationBindingClientSecretMock.credentials.clientid, + undefined ) ).toEqual(token); }); diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index 80d9d369c9..dc0a2ee2a6 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -45,7 +45,8 @@ export async function serviceToken( if (opts.useCache) { const cachedToken = clientCredentialsTokenCache.getToken( tenantForCaching, - serviceCredentials.clientid + serviceCredentials.clientid, + undefined ); if (cachedToken) { return cachedToken.access_token; @@ -59,6 +60,7 @@ export async function serviceToken( clientCredentialsTokenCache.cacheToken( tenantForCaching, serviceCredentials.clientid, + undefined, token ); } From 9d5f8eaa90752acb11d72d44901d1e579775a103 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 19 Jan 2026 08:40:01 +0100 Subject: [PATCH 49/58] add ias tenant to tenant cache key --- .../service-binding-to-destination.spec.ts | 75 ++++++++++++++++++- .../service-binding-to-destination.ts | 31 +++++--- .../src/scp-cf/subdomain-replacer.ts | 19 ++++- 3 files changed, 107 insertions(+), 18 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index 5a4e379885..ee27767631 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -418,7 +418,7 @@ describe('service binding to destination', () => { // Verify cache was populated with provider tenant const cached = clientCredentialsTokenCache.getToken( - 'provider-tenant-id', + 'iasTenant=tenant.accounts.ondemand.com&appTid=provider-tenant-id', 'identity-clientid', undefined ); @@ -439,7 +439,7 @@ describe('service binding to destination', () => { // Verify cache was populated with current tenant const cached = clientCredentialsTokenCache.getToken( - 'current-tenant-id', + 'iasTenant=tenant.accounts.ondemand.com&appTid=current-tenant-id', 'identity-clientid', undefined ); @@ -456,7 +456,7 @@ describe('service binding to destination', () => { // Verify cache was populated with current tenant (default behavior) const cached = clientCredentialsTokenCache.getToken( - 'current-tenant-id', + 'iasTenant=tenant.accounts.ondemand.com&appTid=current-tenant-id', 'identity-clientid', undefined ); @@ -490,7 +490,7 @@ describe('service binding to destination', () => { // Verify cache was populated with explicit appTid, not provider tenant const cached = clientCredentialsTokenCache.getToken( - 'explicit-tenant-789', + 'iasTenant=tenant.accounts.ondemand.com&appTid=explicit-tenant-789', 'identity-clientid', undefined ); @@ -716,5 +716,72 @@ describe('service binding to destination', () => { // Should be called twice - no caching for JWT bearer expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); }); + + it('handles missing app_tid (no JWT, no provider tenant)', async () => { + await transformServiceBindingToDestination( + resolveServiceBinding('identity') + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Verify cache was populated with just IAS tenant (no app_tid) + const cached = clientCredentialsTokenCache.getToken( + 'iasTenant=tenant.accounts.ondemand.com', + 'identity-clientid', + undefined + ); + expect(cached?.access_token).toBe('ias-access-token'); + + // Second call without app_tid should use cache + await transformServiceBindingToDestination( + resolveServiceBinding('identity') + ); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + }); + + it('isolates cache by IAS tenant (different service URLs)', async () => { + const iasService1 = { + ...services.identity[0], + name: 'ias-service-1', + credentials: { + ...services.identity[0].credentials, + url: 'https://tenant1.accounts.ondemand.com', + clientid: 'client-1' + } + }; + + const iasService2 = { + ...services.identity[0], + name: 'ias-service-2', + credentials: { + ...services.identity[0].credentials, + url: 'https://tenant2.accounts.ondemand.com', + clientid: 'client-2' + } + }; + + // First call to IAS service 1 + await transformServiceBindingToDestination(iasService1, { + jwt: { app_tid: 'tenant-123' } + }); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + + // Second call to IAS service 2 with same app_tid should NOT use cache + // because IAS tenant is different + await transformServiceBindingToDestination(iasService2, { + jwt: { app_tid: 'tenant-123' } + }); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + + // Third call to IAS service 1 again with same app_tid should use cache + await transformServiceBindingToDestination(iasService1, { + jwt: { app_tid: 'tenant-123' } + }); + + expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + }); }); }); diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index de3cfbf63c..d966ee366b 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -2,6 +2,7 @@ import { serviceToken } from '../token-accessor'; import { decodeJwt } from '../jwt'; import { getIasClientCredentialsToken } from '../identity-service'; import { clientCredentialsTokenCache } from '../client-credentials-token-cache'; +import { parseUrlAndGetHost } from '../subdomain-replacer'; import type { Service } from '../environment-accessor'; import type { ServiceBindingTransformFunction, @@ -197,9 +198,11 @@ async function iasBindingToDestination( } as IasOptions & CachingOptions; let accessToken: string | undefined; + let iasTenant: string | undefined; + let tenantCacheKey: string | undefined; + // Technical user client credentials grant preperation if (iasOptions.authenticationType === 'OAuth2ClientCredentials') { - // Technical user client credentials grant if (!iasOptions.appTid) { const requestAs = iasOptions.requestAs ?? 'current-tenant'; if (requestAs === 'provider-tenant') { @@ -207,16 +210,22 @@ async function iasBindingToDestination( } else if (requestAs === 'current-tenant') { iasOptions.appTid = options?.jwt?.app_tid; } + } + + if (iasOptions.useCache) { + iasTenant = parseUrlAndGetHost(service.credentials.url); + tenantCacheKey = new URLSearchParams({ + iasTenant, + ...(iasOptions.appTid && { appTid: iasOptions.appTid }) + }).toString(); - if (iasOptions.useCache) { - const cached = clientCredentialsTokenCache.getToken( - iasOptions.appTid, - service.credentials.clientid, - iasOptions.resource - ); - if (cached) { - accessToken = cached.access_token; - } + const cached = clientCredentialsTokenCache.getToken( + tenantCacheKey, + service.credentials.clientid, + iasOptions.resource + ); + if (cached) { + accessToken = cached.access_token; } } } @@ -235,7 +244,7 @@ async function iasBindingToDestination( response ) { clientCredentialsTokenCache.cacheToken( - iasOptions.appTid, + tenantCacheKey, service.credentials.clientid, iasOptions.resource, response diff --git a/packages/connectivity/src/scp-cf/subdomain-replacer.ts b/packages/connectivity/src/scp-cf/subdomain-replacer.ts index 4b42223766..1f72ebff53 100644 --- a/packages/connectivity/src/scp-cf/subdomain-replacer.ts +++ b/packages/connectivity/src/scp-cf/subdomain-replacer.ts @@ -23,13 +23,26 @@ export function getIssuerSubdomain( function getHost(url: URL): string { const { host } = url; if (!host || host.indexOf('.') === -1) { - throw new Error( - `Failed to determine sub-domain: invalid host in "${url}".` - ); + throw new Error(`Failed to determine hostname: invalid host in "${url}".`); } return host; } +/** + * @internal + * This functions returns the host part of an URL, with URL validation. + * @param url + * @returns host + */ +export function parseUrlAndGetHost(url: string): string { + if (!isValidUrl(url)) { + throw new Error(`URL is not a valid URL: "${url}".`); + } + + const parsed = new URL(url); + return getHost(parsed); +} + function isValidUrl(url: string): boolean { try { new URL(url); From 96779dc62f008e5ba7626cfa1de3183d11b6b65b Mon Sep 17 00:00:00 2001 From: David Knaack Date: Mon, 19 Jan 2026 16:02:14 +0100 Subject: [PATCH 50/58] address review comments --- .../client-credentials-token-cache.spec.ts | 210 +++++++++++------- .../scp-cf/client-credentials-token-cache.ts | 102 +++++++-- .../service-binding-to-destination.spec.ts | 49 ++-- .../service-binding-to-destination.ts | 136 +++++++----- .../src/scp-cf/identity-service.ts | 4 +- .../src/scp-cf/subdomain-replacer.ts | 5 +- .../src/scp-cf/token-accessor.spec.ts | 28 +-- .../connectivity/src/scp-cf/token-accessor.ts | 4 +- 8 files changed, 331 insertions(+), 207 deletions(-) diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts index d4843452fb..2779fe07c3 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts @@ -1,7 +1,7 @@ import { createLogger } from '@sap-cloud-sdk/util'; import { clientCredentialsTokenCache, - getCacheKey + getCacheKeyIas } from './client-credentials-token-cache'; const oneHourInSeconds = 60 * 60; @@ -21,7 +21,6 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', - undefined, validToken ); @@ -29,8 +28,7 @@ describe('ClientCredentialsTokenCache', () => { const valid = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - 'clientid', - undefined + 'clientid' ); expect(valid).toEqual(validToken); @@ -50,15 +48,13 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', - undefined, expiredToken ); jest.advanceTimersByTime(oneHourInSeconds * 2 * 1000); const expired = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - 'clientid', - undefined + 'clientid' ); expect(expired).toBeUndefined(); @@ -79,7 +75,6 @@ describe('ClientCredentialsTokenCache', () => { clientCredentialsTokenCache.cacheToken( 'subscriber-tenant', 'clientid', - undefined, validToken ); @@ -87,8 +82,7 @@ describe('ClientCredentialsTokenCache', () => { const invalid = clientCredentialsTokenCache.getToken( 'subscriber-tenant', - undefined as any, - undefined + undefined as any ); expect(invalid).toBeUndefined(); @@ -105,26 +99,20 @@ describe('ClientCredentialsTokenCache', () => { jti: '', scope: '' }; + const iasTokenCacheData = { + iasInstance: 'subscriber-tenant', + clientId: 'clientid', + resource: { name: 'my-app' } + }; beforeEach(() => { clientCredentialsTokenCache.clear(); }); it('should cache and retrieve token with resource name', () => { - const resource = { name: 'my-app' }; + clientCredentialsTokenCache.cacheTokenIas(iasTokenCacheData, validToken); - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resource, - validToken - ); - - const cached = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource - ); + const cached = clientCredentialsTokenCache.getTokenIas(iasTokenCacheData); expect(cached).toEqual(validToken); }); @@ -132,18 +120,18 @@ describe('ClientCredentialsTokenCache', () => { it('should cache and retrieve token with resource clientId', () => { const resource = { providerClientId: 'resource-client-123' }; - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resource, + clientCredentialsTokenCache.cacheTokenIas( + { + ...iasTokenCacheData, + resource + }, validToken ); - const cached = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', + const cached = clientCredentialsTokenCache.getTokenIas({ + ...iasTokenCacheData, resource - ); + }); expect(cached).toEqual(validToken); }); @@ -154,18 +142,18 @@ describe('ClientCredentialsTokenCache', () => { providerTenantId: 'tenant-456' }; - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resource, + clientCredentialsTokenCache.cacheTokenIas( + { + ...iasTokenCacheData, + resource + }, validToken ); - const cached = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', + const cached = clientCredentialsTokenCache.getTokenIas({ + ...iasTokenCacheData, resource - ); + }); expect(cached).toEqual(validToken); }); @@ -174,81 +162,145 @@ describe('ClientCredentialsTokenCache', () => { const resource1 = { name: 'app-1' }; const resource2 = { name: 'app-2' }; - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resource1, + clientCredentialsTokenCache.cacheTokenIas( + { + ...iasTokenCacheData, + resource: resource1 + }, validToken ); - const cached1 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource1 - ); - const cached2 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource2 - ); + const cached1 = clientCredentialsTokenCache.getTokenIas({ + ...iasTokenCacheData, + resource: resource1 + }); + const cached2 = clientCredentialsTokenCache.getTokenIas({ + ...iasTokenCacheData, + resource: resource2 + }); expect(cached1).toEqual(validToken); expect(cached2).toBeUndefined(); }); - it('should isolate cache by resource clientId', () => { + it('should isolate cache by resource providerClientId', () => { const resource1 = { providerClientId: 'client-1' }; const resource2 = { providerClientId: 'client-2' }; - clientCredentialsTokenCache.cacheToken( - 'subscriber-tenant', - 'clientid', - resource1, + clientCredentialsTokenCache.cacheTokenIas( + { + ...iasTokenCacheData, + resource: resource1 + }, validToken ); - const cached1 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource1 - ); - const cached2 = clientCredentialsTokenCache.getToken( - 'subscriber-tenant', - 'clientid', - resource2 - ); + const cached1 = clientCredentialsTokenCache.getTokenIas({ + ...iasTokenCacheData, + resource: resource1 + }); + const cached2 = clientCredentialsTokenCache.getTokenIas({ + ...iasTokenCacheData, + resource: resource2 + }); expect(cached1).toEqual(validToken); expect(cached2).toBeUndefined(); }); it('should generate correct cache key with resource name', () => { - const key = getCacheKey('tenant-123', 'client-id', { name: 'my-app' }); - expect(key).toBe('tenant-123:client-id:name=my-app'); + const key = getCacheKeyIas({ + iasInstance: 'tenant-123', + clientId: 'client-id', + resource: { name: 'my-app' } + }); + expect(key).toBe('tenant-123::client-id:name=my-app'); }); it('should generate correct cache key with resource clientId only', () => { - const key = getCacheKey('tenant-123', 'client-id', { - providerClientId: 'resource-client-123' + const key = getCacheKeyIas({ + iasInstance: 'tenant-123', + clientId: 'client-id', + resource: { + providerClientId: 'resource-client-123' + } }); expect(key).toBe( - 'tenant-123:client-id:provider-clientId=resource-client-123' + 'tenant-123::client-id:provider-clientId=resource-client-123' ); }); it('should generate correct cache key with resource clientId and tenantId', () => { - const key = getCacheKey('tenant-123', 'client-id', { - providerClientId: 'resource-client-123', - providerTenantId: 'tenant-456' + const key = getCacheKeyIas({ + iasInstance: 'tenant-123', + clientId: 'client-id', + resource: { + providerClientId: 'resource-client-123', + providerTenantId: 'tenant-456' + } }); expect(key).toBe( - 'tenant-123:client-id:provider-clientId=resource-client-123:provider-tenantId=tenant-456' + 'tenant-123::client-id:provider-clientId=resource-client-123:provider-tenantId=tenant-456' ); }); it('should generate cache key without resource when not provided', () => { - const key = getCacheKey('tenant-123', 'client-id'); - expect(key).toBe('tenant-123:client-id'); + const key = getCacheKeyIas({ + iasInstance: 'tenant-123', + clientId: 'client-id' + }); + expect(key).toBe('tenant-123::client-id'); + }); + + it('should isolate cache by appTid', () => { + clientCredentialsTokenCache.cacheTokenIas( + { + ...iasTokenCacheData, + appTid: 'tenant-123' + }, + validToken + ); + + const cached1 = clientCredentialsTokenCache.getTokenIas({ + ...iasTokenCacheData, + appTid: 'tenant-123' + }); + const cached2 = clientCredentialsTokenCache.getTokenIas({ + ...iasTokenCacheData, + appTid: 'tenant-456' + }); + const cached3 = clientCredentialsTokenCache.getTokenIas({ + ...iasTokenCacheData + // No appTid + }); + + expect(cached1).toEqual(validToken); + expect(cached2).toBeUndefined(); + expect(cached3).toBeUndefined(); + }); + + it('should generate correct cache key with appTid', () => { + const key = getCacheKeyIas({ + iasInstance: 'tenant-123', + clientId: 'client-id', + appTid: 'app-tenant-456' + }); + expect(key).toBe('tenant-123:app-tenant-456:client-id'); + }); + + it('should generate cache key with double colon when appTid is undefined', () => { + const key1 = getCacheKeyIas({ + iasInstance: 'tenant-123', + clientId: 'client-id', + appTid: undefined + }); + const key2 = getCacheKeyIas({ + iasInstance: 'tenant-123', + clientId: 'client-id' + }); + // Both should produce the same key with double colon + expect(key1).toBe('tenant-123::client-id'); + expect(key2).toBe('tenant-123::client-id'); }); }); }); diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts index d69cac2d30..f8111ce759 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts @@ -13,24 +13,39 @@ const ClientCredentialsTokenCache = ( ) => ({ getToken: ( tenantId: string | undefined, - clientId: string, - resource?: IasResource + clientId: string ): ClientCredentialsResponse | undefined => - cache.get(getCacheKey(tenantId, clientId, resource)), + cache.get(getCacheKey(tenantId, clientId)), cacheToken: ( tenantId: string | undefined, clientId: string, - resource: IasResource | undefined, token: ClientCredentialsResponse ): void => { - cache.set(getCacheKey(tenantId, clientId, resource), { + cache.set(getCacheKey(tenantId, clientId), { entry: token, expires: token.expires_in ? Date.now() + token.expires_in * 1000 : undefined }); }, + + getTokenIas: ( + data: IasClientCredentialsCacheKeyData + ): ClientCredentialsResponse | undefined => cache.get(getCacheKeyIas(data)), + + cacheTokenIas: ( + data: IasClientCredentialsCacheKeyData, + tokenResponse: ClientCredentialsResponse + ): void => { + cache.set(getCacheKeyIas(data), { + entry: tokenResponse, + expires: tokenResponse.expires_in + ? Date.now() + tokenResponse.expires_in * 1000 + : undefined + }); + }, + clear: (): void => { cache.clear(); }, @@ -43,17 +58,21 @@ const ClientCredentialsTokenCache = ( * @returns Normalized resource string or empty string if not provided. * @internal */ -function normalizeResource(resource?: IasResource): string | undefined { +function normalizeResource( + resource?: IasResource +): [] | [string] | [string, string] { if (!resource) { - return undefined; + return []; } if ('name' in resource) { - return `name=${resource.name}`; + return [`name=${resource.name}`]; } - let normalized = `provider-clientId=${resource.providerClientId}`; + const normalized: [string] | [string, string] = [ + `provider-clientId=${resource.providerClientId}` + ]; if (resource.providerTenantId) { - normalized += `:provider-tenantId=${resource.providerTenantId}`; + normalized.push(`provider-tenantId=${resource.providerTenantId}`); } return normalized; } @@ -62,13 +81,11 @@ function normalizeResource(resource?: IasResource): string | undefined { * @internal * @param tenantId - The ID of the tenant to cache the token for. * @param clientId - ClientId to fetch the token. - * @param resource - Optional resource parameter (for IAS app2app scenarios). * @returns The cache key. */ export function getCacheKey( tenantId: string | undefined, - clientId: string, - resource?: IasResource + clientId: string ): string | undefined { if (!tenantId) { logger.warn( @@ -82,12 +99,61 @@ export function getCacheKey( ); return; } - const parts = [tenantId, clientId]; - const resourceStr = normalizeResource(resource); - if (resourceStr) { - parts.push(resourceStr); + return [tenantId, clientId].join(':'); +} + +/** + * An interface for data the is used for IAS client credentials cache keys + * @internal + */ +interface IasClientCredentialsCacheKeyData { + /** + * The hostname of the IAS instance. + * @example tenant.accounts400.ondemand.com + */ + iasInstance: string; + /** + * The client credentials client ID. + */ + clientId: string; + /** + * The BTP instanced ID supplied with the token request. + */ + appTid?: string | undefined; + resource?: IasResource | undefined; +} + +/** * + * @internal + * @param data.iasInstance - The IAS instance (tenant) hostname the token is fetched from. + * @param data.appTid - The BTP instance (tenant) id (App-Tid) + * @param data.clientId - ClientId to fetch the token. + * @param data.resource - The App-To-App resource the token is scoped for. + * @returns The cache key. + */ +export function getCacheKeyIas( + data: IasClientCredentialsCacheKeyData +): string | undefined { + const { iasInstance, appTid, clientId, resource } = data; + if (!iasInstance) { + logger.warn( + 'Cannot create cache key for client credentials token cache. The given IAS instance hostname is undefined.' + ); + return; + } + + if (!clientId) { + logger.warn( + 'Cannot create cache key for client credentials token cache. The given client ID is undefined.' + ); + return; } - return parts.join(':'); + + const output = [iasInstance, appTid || '', clientId]; + const normalizedResource = normalizeResource(resource); + output.push(...normalizedResource); + + return output.join(':'); } /** diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index ee27767631..8f024d1df2 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -417,11 +417,11 @@ describe('service binding to destination', () => { ); // Verify cache was populated with provider tenant - const cached = clientCredentialsTokenCache.getToken( - 'iasTenant=tenant.accounts.ondemand.com&appTid=provider-tenant-id', - 'identity-clientid', - undefined - ); + const cached = clientCredentialsTokenCache.getTokenIas({ + iasInstance: 'tenant.accounts.ondemand.com', + appTid: 'provider-tenant-id', + clientId: 'identity-clientid' + }); expect(cached?.access_token).toBe('ias-access-token'); // Restore original VCAP_SERVICES @@ -438,11 +438,11 @@ describe('service binding to destination', () => { ); // Verify cache was populated with current tenant - const cached = clientCredentialsTokenCache.getToken( - 'iasTenant=tenant.accounts.ondemand.com&appTid=current-tenant-id', - 'identity-clientid', - undefined - ); + const cached = clientCredentialsTokenCache.getTokenIas({ + iasInstance: 'tenant.accounts.ondemand.com', + appTid: 'current-tenant-id', + clientId: 'identity-clientid' + }); expect(cached?.access_token).toBe('ias-access-token'); }); @@ -455,11 +455,11 @@ describe('service binding to destination', () => { ); // Verify cache was populated with current tenant (default behavior) - const cached = clientCredentialsTokenCache.getToken( - 'iasTenant=tenant.accounts.ondemand.com&appTid=current-tenant-id', - 'identity-clientid', - undefined - ); + const cached = clientCredentialsTokenCache.getTokenIas({ + iasInstance: 'tenant.accounts.ondemand.com', + appTid: 'current-tenant-id', + clientId: 'identity-clientid' + }); expect(cached?.access_token).toBe('ias-access-token'); }); @@ -489,11 +489,11 @@ describe('service binding to destination', () => { ); // Verify cache was populated with explicit appTid, not provider tenant - const cached = clientCredentialsTokenCache.getToken( - 'iasTenant=tenant.accounts.ondemand.com&appTid=explicit-tenant-789', - 'identity-clientid', - undefined - ); + const cached = clientCredentialsTokenCache.getTokenIas({ + iasInstance: 'tenant.accounts.ondemand.com', + appTid: 'explicit-tenant-789', + clientId: 'identity-clientid' + }); expect(cached?.access_token).toBe('ias-access-token'); // Restore original VCAP_SERVICES @@ -725,11 +725,10 @@ describe('service binding to destination', () => { expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); // Verify cache was populated with just IAS tenant (no app_tid) - const cached = clientCredentialsTokenCache.getToken( - 'iasTenant=tenant.accounts.ondemand.com', - 'identity-clientid', - undefined - ); + const cached = clientCredentialsTokenCache.getTokenIas({ + iasInstance: 'tenant.accounts.ondemand.com', + clientId: 'identity-clientid' + }); expect(cached?.access_token).toBe('ias-access-token'); // Second call without app_tid should use cache diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index d966ee366b..78c516a907 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -9,8 +9,7 @@ import type { ServiceBindingTransformOptions } from './destination-from-vcap'; import type { Destination } from './destination-service-types'; -import type { IasOptions } from './ias-types'; -import type { IasClientCredentialsResponse } from '../identity-service'; +import type { IasOptions, IasOptionsTechnicalUser } from './ias-types'; import type { CachingOptions } from '../cache'; /** @@ -186,6 +185,58 @@ async function xfS4hanaCloudBindingToDestination( password: service.credentials.Password }; } +/** + * Tries to resolve `app_tid` based on supplied IAS options. + * @param iasOptions - IAS technical user options. + * @param service - Service binding for identity service. + * @param options - Service bidning transform options. + * @returns The BTP app_tid based on `requestAs` configuration. + */ +function iasGetAppTid( + iasOptions: IasOptionsTechnicalUser, + service: Service, + options?: ServiceBindingTransformOptions +): string { + const { requestAs } = iasOptions; + if (requestAs === 'provider-tenant') { + return service.app_tid; + } + if (requestAs === 'current-tenant' || !requestAs) { + return options?.jwt?.app_tid; + } + + requestAs satisfies never; + throw new Error(`Invalid requestAs value: ${requestAs}`); +} + +/** + * Builds destination based on supplied IAS options. + * Uses `targetUrl` as the destination URL if supplied and adds `mtlsKeyPair` if available. + * @param accessToken - The JWT token to access the service. + * @param service - Service binding for identity service. + * @param options - Service bidning transform options. + * @returns A destination object. + */ +function iasBuildDestination( + accessToken: string, + service: Service, + options?: ServiceBindingTransformOptions +): Destination { + const destination = buildClientCredentialsDestination( + accessToken, + options?.iasOptions?.targetUrl ?? service.credentials.url, + service.name + ); + + // Add mTLS key pair if available + if (service.credentials.certificate && service.credentials.key) { + destination.mtlsKeyPair = { + cert: service.credentials.certificate, + key: service.credentials.key + }; + } + return destination; +} async function iasBindingToDestination( service: Service, @@ -197,76 +248,49 @@ async function iasBindingToDestination( ...(options?.iasOptions || {}) } as IasOptions & CachingOptions; - let accessToken: string | undefined; - let iasTenant: string | undefined; - let tenantCacheKey: string | undefined; + const iasInstance = parseUrlAndGetHost(service.credentials.url); // Technical user client credentials grant preperation if (iasOptions.authenticationType === 'OAuth2ClientCredentials') { if (!iasOptions.appTid) { - const requestAs = iasOptions.requestAs ?? 'current-tenant'; - if (requestAs === 'provider-tenant') { - iasOptions.appTid = service.app_tid; - } else if (requestAs === 'current-tenant') { - iasOptions.appTid = options?.jwt?.app_tid; - } + iasOptions.appTid = iasGetAppTid(iasOptions, service, options); } if (iasOptions.useCache) { - iasTenant = parseUrlAndGetHost(service.credentials.url); - tenantCacheKey = new URLSearchParams({ - iasTenant, - ...(iasOptions.appTid && { appTid: iasOptions.appTid }) - }).toString(); - - const cached = clientCredentialsTokenCache.getToken( - tenantCacheKey, - service.credentials.clientid, - iasOptions.resource - ); + const cached = clientCredentialsTokenCache.getTokenIas({ + iasInstance, + appTid: iasOptions.appTid, + clientId: service.credentials.clientid, + resource: options?.iasOptions?.resource + }); if (cached) { - accessToken = cached.access_token; + return iasBuildDestination(cached.access_token, service, options); } } } - let response: IasClientCredentialsResponse | undefined; - if (!accessToken) { - response = await getIasClientCredentialsToken(service, { - jwt: options?.jwt, - ...(options?.iasOptions || {}) - }); - accessToken = response.access_token; + const response = await getIasClientCredentialsToken(service, { + jwt: options?.jwt, + ...(options?.iasOptions || {}) + }); - if ( - iasOptions.authenticationType === 'OAuth2ClientCredentials' && - iasOptions.useCache && + if ( + iasOptions.authenticationType === 'OAuth2ClientCredentials' && + iasOptions.useCache && + response + ) { + clientCredentialsTokenCache.cacheTokenIas( + { + iasInstance, + appTid: iasOptions.appTid, + clientId: service.credentials.clientid, + resource: options?.iasOptions?.resource + }, response - ) { - clientCredentialsTokenCache.cacheToken( - tenantCacheKey, - service.credentials.clientid, - iasOptions.resource, - response - ); - } - } - - const destination = buildClientCredentialsDestination( - accessToken, - options?.iasOptions?.targetUrl ?? service.credentials.url, - service.name - ); - - // Add mTLS key pair if available - if (service.credentials.certificate && service.credentials.key) { - destination.mtlsKeyPair = { - cert: service.credentials.certificate, - key: service.credentials.key - }; + ); } - return destination; + return iasBuildDestination(response.access_token, service, options); } function buildClientCredentialsDestination( diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 266f14d1e9..60e74fa727 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -157,7 +157,7 @@ async function getIasClientCredentialsTokenImpl( arg.assertion ); - const tokenOptions: IdentityService.TokenFetchOptions & + let tokenOptions: IdentityService.TokenFetchOptions & IdentityService.IdentityServiceTokenFetchOptions = { token_format: 'jwt' }; @@ -173,7 +173,7 @@ async function getIasClientCredentialsTokenImpl( // Add any extra parameters if (arg.extraParams) { - Object.assign(tokenOptions, arg.extraParams); + tokenOptions = { ...tokenOptions, ...arg.extraParams }; } let response: undefined | IdentityService.TokenFetchResponse; diff --git a/packages/connectivity/src/scp-cf/subdomain-replacer.ts b/packages/connectivity/src/scp-cf/subdomain-replacer.ts index 1f72ebff53..029082e548 100644 --- a/packages/connectivity/src/scp-cf/subdomain-replacer.ts +++ b/packages/connectivity/src/scp-cf/subdomain-replacer.ts @@ -1,4 +1,5 @@ import { URL } from 'url'; +import { removeTrailingSlashes } from '@sap-cloud-sdk/util'; import type { JwtPayload } from './jsonwebtoken-type'; /** @@ -73,9 +74,7 @@ export function replaceSubdomain( let result = url.toString(); // Remove trailing slash for consistency - if (result.endsWith('/')) { - result = result.slice(0, -1); - } + result = removeTrailingSlashes(result); return result; } diff --git a/packages/connectivity/src/scp-cf/token-accessor.spec.ts b/packages/connectivity/src/scp-cf/token-accessor.spec.ts index f1739c04bf..deb0fd0dfe 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.spec.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.spec.ts @@ -205,13 +205,11 @@ describe('token accessor', () => { const providerTokenFromCache = clientCredentialsTokenCache.getToken( providerUserPayload.zid, - destinationBindingClientSecretMock.credentials.clientid, - undefined + destinationBindingClientSecretMock.credentials.clientid ); const subscriberTokenFromCache = clientCredentialsTokenCache.getToken( subscriberUserPayload.zid, - destinationBindingClientSecretMock.credentials.clientid, - undefined + destinationBindingClientSecretMock.credentials.clientid ); expect(providerTokenFromCache?.access_token).toEqual(providerToken); @@ -220,33 +218,23 @@ describe('token accessor', () => { expect( clientCredentialsTokenCache.getToken( 'https://doesnotexist.example.com', - destinationBindingClientSecretMock.credentials.clientid, - undefined + destinationBindingClientSecretMock.credentials.clientid ) ).toBeUndefined(); expect( clientCredentialsTokenCache.getToken( 'https://doesnotexist.example.com', - 'schmusername', - undefined + 'schmusername' ) ).toBeUndefined(); expect( - clientCredentialsTokenCache.getToken( - providerXsuaaUrl, - 'schmusername', - undefined - ) + clientCredentialsTokenCache.getToken(providerXsuaaUrl, 'schmusername') ).toBeUndefined(); expect( - clientCredentialsTokenCache.getToken( - subscriberXsuaaUrl, - 'schmusername', - undefined - ) + clientCredentialsTokenCache.getToken(subscriberXsuaaUrl, 'schmusername') ).toBeUndefined(); }); @@ -332,7 +320,6 @@ describe('token accessor', () => { clientCredentialsTokenCache.cacheToken( destinationBindingClientSecretMock.credentials.tenantid, destinationBindingClientSecretMock.credentials.clientid, - undefined, { access_token: token } as ClientCredentialsResponse ); @@ -361,8 +348,7 @@ describe('token accessor', () => { expect( clientCredentialsTokenCache.getToken( destinationBindingClientSecretMock.credentials.tenantid, - destinationBindingClientSecretMock.credentials.clientid, - undefined + destinationBindingClientSecretMock.credentials.clientid ) ).toEqual(token); }); diff --git a/packages/connectivity/src/scp-cf/token-accessor.ts b/packages/connectivity/src/scp-cf/token-accessor.ts index dc0a2ee2a6..80d9d369c9 100644 --- a/packages/connectivity/src/scp-cf/token-accessor.ts +++ b/packages/connectivity/src/scp-cf/token-accessor.ts @@ -45,8 +45,7 @@ export async function serviceToken( if (opts.useCache) { const cachedToken = clientCredentialsTokenCache.getToken( tenantForCaching, - serviceCredentials.clientid, - undefined + serviceCredentials.clientid ); if (cachedToken) { return cachedToken.access_token; @@ -60,7 +59,6 @@ export async function serviceToken( clientCredentialsTokenCache.cacheToken( tenantForCaching, serviceCredentials.clientid, - undefined, token ); } From bbef802c6b78b1246926eac98292a149702f072c Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 20 Jan 2026 09:15:41 +0100 Subject: [PATCH 51/58] Minor improvements to IAS refresh token handling - Forward refresh_token from IAS token response if present - Extract appTid from assertion JWT if not provided in token options, matching Java SDK behavior --- .../src/scp-cf/identity-service.spec.ts | 90 ++++++++++++++++++- .../src/scp-cf/identity-service.ts | 17 +++- 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 29b5abc917..6987feb3bb 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -272,7 +272,9 @@ describe('getIasClientCredentialsToken', () => { }); expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(userAssertion, { - token_format: 'jwt' + token_format: 'jwt', + app_tid: 'tenant-456', + refresh_expiry: 0 }); expect(mockFetchClientCredentialsToken).not.toHaveBeenCalled(); }); @@ -345,7 +347,8 @@ describe('getIasClientCredentialsToken', () => { }); expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(assertion, { - token_format: 'jwt' + token_format: 'jwt', + app_tid: null }); }); @@ -371,7 +374,8 @@ describe('getIasClientCredentialsToken', () => { ); expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(assertion, { - token_format: 'jwt' + token_format: 'jwt', + app_tid: null }); }); @@ -425,10 +429,31 @@ describe('getIasClientCredentialsToken', () => { expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(assertion, { resource: 'urn:sap:identity:application:provider:name:my-app', refresh_expiry: 0, - token_format: 'jwt' + token_format: 'jwt', + app_tid: null }); }); + it('includes refresh_token when returned by fetchJwtBearerToken', async () => { + const assertion = signedJwt({ + iss: subscriberUrl, + user_uuid: 'user-123' + }); + + const responseWithRefreshToken = { + ...mockTokenResponse, + refresh_token: 'test-refresh-token' + }; + mockFetchJwtBearerToken.mockResolvedValue(responseWithRefreshToken); + + const result = await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion + }); + + expect(result.refresh_token).toBe('test-refresh-token'); + }); + it('disables refresh tokens for JWT bearer exchanges if app_tid is set', async () => { const assertion = signedJwt({ iss: subscriberUrl, @@ -448,6 +473,63 @@ describe('getIasClientCredentialsToken', () => { }); }); + it('extracts app_tid from assertion when not explicitly provided', async () => { + const assertion = signedJwt({ + iss: subscriberUrl, + user_uuid: 'user-123', + app_tid: 'extracted-tenant-id' + }); + + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion + }); + + expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(assertion, { + app_tid: 'extracted-tenant-id', + refresh_expiry: 0, + token_format: 'jwt' + }); + }); + + it('sets app_tid to null when neither app_tid nor zone_uuid exist in assertion', async () => { + const assertion = signedJwt({ + iss: subscriberUrl, + user_uuid: 'user-123' + // No app_tid or zone_uuid + }); + + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion + }); + + expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(assertion, { + app_tid: null, + token_format: 'jwt' + }); + }); + + it('does not override explicit app_tid with value from assertion', async () => { + const assertion = signedJwt({ + iss: subscriberUrl, + user_uuid: 'user-123', + app_tid: 'assertion-tenant-id' + }); + + await getIasClientCredentialsToken(providerService, { + authenticationType: 'OAuth2JWTBearer', + assertion, + appTid: 'explicit-tenant-id' + }); + + expect(mockFetchJwtBearerToken).toHaveBeenCalledWith(assertion, { + app_tid: 'explicit-tenant-id', + refresh_expiry: 0, + token_format: 'jwt' + }); + }); + it('caches subscriber instances per URL', async () => { const subscriber1Url = 'https://subscriber1.accounts.ondemand.com'; const subscriber2Url = 'https://subscriber2.accounts.ondemand.com'; diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 60e74fa727..9654b523d3 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -49,6 +49,10 @@ export interface IasClientCredentialsResponse extends ClientCredentialsResponse * IAS tokens don't have scope property. */ scope: ''; + /** + * @internal + */ + refresh_token?: string; } /** @@ -191,6 +195,13 @@ async function getIasClientCredentialsTokenImpl( tokenOptions.refresh_expiry = 0; } + // Extract appTid from assertion if not provided + const token = new IdentityServiceToken(arg.assertion); + if (!tokenOptions.app_tid) { + // Set to `null` if not prevent xssec from also trying to extract it internally + tokenOptions.app_tid = token.appTid ?? null; + } + // Workaround for IAS bug // JAVA SDK: https://github.com/SAP/cloud-sdk-java/blob/61903347b607a8397f7930709cd52526f05269b1/cloudplatform/connectivity-oauth/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/OAuth2Service.java#L225-L236 // Issue: https://jira.tools.sap/browse/SECREQ-5220 @@ -222,6 +233,10 @@ async function getIasClientCredentialsTokenImpl( scim_id: decodedJwt.scimId, // Added if resource parameter was specified ias_apis: decodedJwt?.consumedApis, - custom_iss: decodedJwt.customIssuer ?? undefined + custom_iss: decodedJwt.customIssuer ?? undefined, + // fetchJwtBearerToken may return a refresh token + refresh_token: ( + response as unknown as IdentityService.RefreshableTokenFetchResponse + )?.refresh_token }; } From f786e9873098276e8e5d4e5c52c8f5f70775b340 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Wed, 21 Jan 2026 13:30:42 +0100 Subject: [PATCH 52/58] address review comments --- .../client-credentials-token-cache.spec.ts | 28 +++--- .../scp-cf/client-credentials-token-cache.ts | 17 ++-- .../service-binding-to-destination.spec.ts | 4 +- .../service-binding-to-destination.ts | 22 ++--- .../src/scp-cf/identity-service.ts | 89 ++++++++++--------- 5 files changed, 83 insertions(+), 77 deletions(-) diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts index 2779fe07c3..083171b81a 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.spec.ts @@ -1,7 +1,7 @@ import { createLogger } from '@sap-cloud-sdk/util'; import { clientCredentialsTokenCache, - getCacheKeyIas + getIasCacheKey } from './client-credentials-token-cache'; const oneHourInSeconds = 60 * 60; @@ -110,7 +110,7 @@ describe('ClientCredentialsTokenCache', () => { }); it('should cache and retrieve token with resource name', () => { - clientCredentialsTokenCache.cacheTokenIas(iasTokenCacheData, validToken); + clientCredentialsTokenCache.cacheIasToken(iasTokenCacheData, validToken); const cached = clientCredentialsTokenCache.getTokenIas(iasTokenCacheData); @@ -120,7 +120,7 @@ describe('ClientCredentialsTokenCache', () => { it('should cache and retrieve token with resource clientId', () => { const resource = { providerClientId: 'resource-client-123' }; - clientCredentialsTokenCache.cacheTokenIas( + clientCredentialsTokenCache.cacheIasToken( { ...iasTokenCacheData, resource @@ -142,7 +142,7 @@ describe('ClientCredentialsTokenCache', () => { providerTenantId: 'tenant-456' }; - clientCredentialsTokenCache.cacheTokenIas( + clientCredentialsTokenCache.cacheIasToken( { ...iasTokenCacheData, resource @@ -162,7 +162,7 @@ describe('ClientCredentialsTokenCache', () => { const resource1 = { name: 'app-1' }; const resource2 = { name: 'app-2' }; - clientCredentialsTokenCache.cacheTokenIas( + clientCredentialsTokenCache.cacheIasToken( { ...iasTokenCacheData, resource: resource1 @@ -187,7 +187,7 @@ describe('ClientCredentialsTokenCache', () => { const resource1 = { providerClientId: 'client-1' }; const resource2 = { providerClientId: 'client-2' }; - clientCredentialsTokenCache.cacheTokenIas( + clientCredentialsTokenCache.cacheIasToken( { ...iasTokenCacheData, resource: resource1 @@ -209,7 +209,7 @@ describe('ClientCredentialsTokenCache', () => { }); it('should generate correct cache key with resource name', () => { - const key = getCacheKeyIas({ + const key = getIasCacheKey({ iasInstance: 'tenant-123', clientId: 'client-id', resource: { name: 'my-app' } @@ -218,7 +218,7 @@ describe('ClientCredentialsTokenCache', () => { }); it('should generate correct cache key with resource clientId only', () => { - const key = getCacheKeyIas({ + const key = getIasCacheKey({ iasInstance: 'tenant-123', clientId: 'client-id', resource: { @@ -231,7 +231,7 @@ describe('ClientCredentialsTokenCache', () => { }); it('should generate correct cache key with resource clientId and tenantId', () => { - const key = getCacheKeyIas({ + const key = getIasCacheKey({ iasInstance: 'tenant-123', clientId: 'client-id', resource: { @@ -245,7 +245,7 @@ describe('ClientCredentialsTokenCache', () => { }); it('should generate cache key without resource when not provided', () => { - const key = getCacheKeyIas({ + const key = getIasCacheKey({ iasInstance: 'tenant-123', clientId: 'client-id' }); @@ -253,7 +253,7 @@ describe('ClientCredentialsTokenCache', () => { }); it('should isolate cache by appTid', () => { - clientCredentialsTokenCache.cacheTokenIas( + clientCredentialsTokenCache.cacheIasToken( { ...iasTokenCacheData, appTid: 'tenant-123' @@ -280,7 +280,7 @@ describe('ClientCredentialsTokenCache', () => { }); it('should generate correct cache key with appTid', () => { - const key = getCacheKeyIas({ + const key = getIasCacheKey({ iasInstance: 'tenant-123', clientId: 'client-id', appTid: 'app-tenant-456' @@ -289,12 +289,12 @@ describe('ClientCredentialsTokenCache', () => { }); it('should generate cache key with double colon when appTid is undefined', () => { - const key1 = getCacheKeyIas({ + const key1 = getIasCacheKey({ iasInstance: 'tenant-123', clientId: 'client-id', appTid: undefined }); - const key2 = getCacheKeyIas({ + const key2 = getIasCacheKey({ iasInstance: 'tenant-123', clientId: 'client-id' }); diff --git a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts index f8111ce759..b724c1302d 100644 --- a/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts +++ b/packages/connectivity/src/scp-cf/client-credentials-token-cache.ts @@ -32,13 +32,13 @@ const ClientCredentialsTokenCache = ( getTokenIas: ( data: IasClientCredentialsCacheKeyData - ): ClientCredentialsResponse | undefined => cache.get(getCacheKeyIas(data)), + ): ClientCredentialsResponse | undefined => cache.get(getIasCacheKey(data)), - cacheTokenIas: ( + cacheIasToken: ( data: IasClientCredentialsCacheKeyData, tokenResponse: ClientCredentialsResponse ): void => { - cache.set(getCacheKeyIas(data), { + cache.set(getIasCacheKey(data), { entry: tokenResponse, expires: tokenResponse.expires_in ? Date.now() + tokenResponse.expires_in * 1000 @@ -131,7 +131,7 @@ interface IasClientCredentialsCacheKeyData { * @param data.resource - The App-To-App resource the token is scoped for. * @returns The cache key. */ -export function getCacheKeyIas( +export function getIasCacheKey( data: IasClientCredentialsCacheKeyData ): string | undefined { const { iasInstance, appTid, clientId, resource } = data; @@ -149,9 +149,12 @@ export function getCacheKeyIas( return; } - const output = [iasInstance, appTid || '', clientId]; - const normalizedResource = normalizeResource(resource); - output.push(...normalizedResource); + const output = [ + iasInstance, + appTid || '', + clientId, + ...normalizeResource(resource) + ]; return output.join(':'); } diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index 8f024d1df2..aec85dac3f 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -373,7 +373,7 @@ describe('service binding to destination', () => { ); }); - describe('iasBindingToDestination requestAs handling', () => { + describe('transformIasBindingToDestination requestAs handling', () => { beforeEach(() => { jest.clearAllMocks(); clientCredentialsTokenCache.clear(); @@ -501,7 +501,7 @@ describe('service binding to destination', () => { }); }); - describe('iasBindingToDestination cache functionality', () => { + describe('transformIasBindingToDestination cache functionality', () => { beforeEach(() => { jest.clearAllMocks(); clientCredentialsTokenCache.clear(); diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index 78c516a907..ffc43dd818 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -27,7 +27,7 @@ export const serviceToDestinationTransformers: Record< 'service-manager': serviceManagerBindingToDestination, xsuaa: xsuaaToDestination, aicore: aicoreToDestination, - identity: iasBindingToDestination + identity: transformIasBindingToDestination }; /** @@ -192,7 +192,7 @@ async function xfS4hanaCloudBindingToDestination( * @param options - Service bidning transform options. * @returns The BTP app_tid based on `requestAs` configuration. */ -function iasGetAppTid( +function getIasAppTid( iasOptions: IasOptionsTechnicalUser, service: Service, options?: ServiceBindingTransformOptions @@ -217,7 +217,7 @@ function iasGetAppTid( * @param options - Service bidning transform options. * @returns A destination object. */ -function iasBuildDestination( +function buildIasDestination( accessToken: string, service: Service, options?: ServiceBindingTransformOptions @@ -238,7 +238,7 @@ function iasBuildDestination( return destination; } -async function iasBindingToDestination( +async function transformIasBindingToDestination( service: Service, options?: ServiceBindingTransformOptions ): Promise { @@ -246,14 +246,14 @@ async function iasBindingToDestination( authenticationType: 'OAuth2ClientCredentials' as const, useCache: options?.useCache !== false, ...(options?.iasOptions || {}) - } as IasOptions & CachingOptions; + } satisfies IasOptions & CachingOptions; const iasInstance = parseUrlAndGetHost(service.credentials.url); // Technical user client credentials grant preperation if (iasOptions.authenticationType === 'OAuth2ClientCredentials') { if (!iasOptions.appTid) { - iasOptions.appTid = iasGetAppTid(iasOptions, service, options); + iasOptions.appTid = getIasAppTid(iasOptions, service, options); } if (iasOptions.useCache) { @@ -264,14 +264,14 @@ async function iasBindingToDestination( resource: options?.iasOptions?.resource }); if (cached) { - return iasBuildDestination(cached.access_token, service, options); + return buildIasDestination(cached.access_token, service, options); } } } const response = await getIasClientCredentialsToken(service, { jwt: options?.jwt, - ...(options?.iasOptions || {}) + ...iasOptions }); if ( @@ -279,18 +279,18 @@ async function iasBindingToDestination( iasOptions.useCache && response ) { - clientCredentialsTokenCache.cacheTokenIas( + clientCredentialsTokenCache.cacheIasToken( { iasInstance, appTid: iasOptions.appTid, clientId: service.credentials.clientid, - resource: options?.iasOptions?.resource + resource: iasOptions?.resource }, response ); } - return iasBuildDestination(response.access_token, service, options); + return buildIasDestination(response.access_token, service, options); } function buildClientCredentialsDestination( diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 9654b523d3..6be36f1639 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -140,47 +140,32 @@ function convertResourceToUrn(resource: IasResource): string { return `urn:sap:identity:application:provider:name:${resource.name}`; } - let urn = `urn:sap:identity:application:provider:clientid:${resource.providerClientId}`; + const segments = [ + `urn:sap:identity:application:provider:clientid:${resource.providerClientId}` + ]; if (resource.providerTenantId) { - urn += `:apptid:${resource.providerTenantId}`; + segments.push(`apptid:${resource.providerTenantId}`); } - return urn; + return segments.join(':'); } /** - * Implementation of the IAS client credentials token retrieval using @sap/xssec. - * @param arg - The parameters for IAS token retrieval. - * @returns A promise resolving to the client credentials response. + * Transforms IAS options to the format expected by @sap/xssec. + * @param arg - The IAS parameters including options. + * @returns The transformed token fetch options. * @internal */ -async function getIasClientCredentialsTokenImpl( +function transformIasOptionsToXssecArgs( arg: IasParameters -): Promise { - const identityService = getIdentityServiceInstanceFromCredentials( - arg.serviceCredentials, - arg.assertion - ); - - let tokenOptions: IdentityService.TokenFetchOptions & - IdentityService.IdentityServiceTokenFetchOptions = { - token_format: 'jwt' - }; - - // Stringify resource - if (arg.resource) { - tokenOptions.resource = convertResourceToUrn(arg.resource); - } - - if (arg.appTid) { - tokenOptions.app_tid = arg.appTid; - } - - // Add any extra parameters - if (arg.extraParams) { - tokenOptions = { ...tokenOptions, ...arg.extraParams }; - } - - let response: undefined | IdentityService.TokenFetchResponse; +): IdentityService.TokenFetchOptions & + IdentityService.IdentityServiceTokenFetchOptions { + const tokenOptions = { + token_format: 'jwt', + ...(arg.resource && { resource: convertResourceToUrn(arg.resource) }), + ...(arg.appTid && { app_tid: arg.appTid }), + ...(arg.extraParams || {}) + } satisfies IdentityService.TokenFetchOptions & + IdentityService.IdentityServiceTokenFetchOptions; if (arg.authenticationType === 'OAuth2JWTBearer') { // JWT bearer grant for business user propagation @@ -198,8 +183,8 @@ async function getIasClientCredentialsTokenImpl( // Extract appTid from assertion if not provided const token = new IdentityServiceToken(arg.assertion); if (!tokenOptions.app_tid) { - // Set to `null` if not prevent xssec from also trying to extract it internally - tokenOptions.app_tid = token.appTid ?? null; + // Set to `null` if not set to prevent xssec from also trying to extract it internally + tokenOptions.app_tid = token?.appTid || (null as unknown as undefined); } // Workaround for IAS bug @@ -208,16 +193,34 @@ async function getIasClientCredentialsTokenImpl( if (tokenOptions.app_tid) { tokenOptions.refresh_expiry = 0; } - - response = await identityService.fetchJwtBearerToken( - arg.assertion, - tokenOptions - ); - } else { - // Technical user client credentials grant - response = await identityService.fetchClientCredentialsToken(tokenOptions); } + return tokenOptions; +} + +/** + * Implementation of the IAS client credentials token retrieval using @sap/xssec. + * @param arg - The parameters for IAS token retrieval. + * @returns A promise resolving to the client credentials response. + * @internal + */ +async function getIasClientCredentialsTokenImpl( + arg: IasParameters +): Promise { + const identityService = getIdentityServiceInstanceFromCredentials( + arg.serviceCredentials, + arg.assertion + ); + + const tokenOptions = transformIasOptionsToXssecArgs(arg); + + const response: IdentityService.TokenFetchResponse = + arg.authenticationType === 'OAuth2JWTBearer' + ? // JWT bearer grant for business user access + await identityService.fetchJwtBearerToken(arg.assertion, tokenOptions) + : // Technical user client credentials grant + await identityService.fetchClientCredentialsToken(tokenOptions); + const decodedJwt = new IdentityServiceToken(response.access_token); return { From 39f9c1e3e03c9db89abd23f481174968abf5dfc8 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Wed, 21 Jan 2026 14:06:56 +0100 Subject: [PATCH 53/58] minor fixes --- .../scp-cf/destination/service-binding-to-destination.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index ffc43dd818..1a48d7e843 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -189,14 +189,14 @@ async function xfS4hanaCloudBindingToDestination( * Tries to resolve `app_tid` based on supplied IAS options. * @param iasOptions - IAS technical user options. * @param service - Service binding for identity service. - * @param options - Service bidning transform options. + * @param options - Service binding transform options. * @returns The BTP app_tid based on `requestAs` configuration. */ function getIasAppTid( iasOptions: IasOptionsTechnicalUser, service: Service, options?: ServiceBindingTransformOptions -): string { +): string | undefined { const { requestAs } = iasOptions; if (requestAs === 'provider-tenant') { return service.app_tid; @@ -296,7 +296,7 @@ async function transformIasBindingToDestination( function buildClientCredentialsDestination( token: string, url: string, - name + name: string ): Destination { const expirationTime = decodeJwt(token).exp; const expiresIn = expirationTime From 4e2175430b77f79cd22b02ed2cc424f497dadb46 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 23 Jan 2026 09:15:34 +0100 Subject: [PATCH 54/58] address review comments --- .../destination/destination-from-vcap.spec.ts | 20 ++-- .../service-binding-to-destination.spec.ts | 96 +++++++++++++------ .../service-binding-to-destination.ts | 77 +++++++-------- .../src/scp-cf/identity-service.spec.ts | 58 +++++------ .../src/scp-cf/identity-service.ts | 6 +- 5 files changed, 142 insertions(+), 115 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts index 8f5218888e..5a98883ce8 100644 --- a/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts @@ -292,17 +292,15 @@ describe('vcap-service-destination', () => { describe('IAS service binding', () => { function mockIasClientCredentialsToken(aud: string) { const token = signedJwt({ jti: 'some-jti', ias_apis: [], aud }); - const spy = jest - .spyOn(identityService, 'getIasClientCredentialsToken') - .mockResolvedValue({ - access_token: token, - expires_in: 3600, - token_type: 'bearer', - aud, - scope: '' as const, - ias_apis: [], - jti: 'some-jti' - }); + const spy = jest.spyOn(identityService, 'getIasToken').mockResolvedValue({ + access_token: token, + expires_in: 3600, + token_type: 'bearer', + aud, + scope: '' as const, + ias_apis: [], + jti: 'some-jti' + }); return { token, spy }; } diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index aec85dac3f..66fd56da09 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -1,5 +1,5 @@ import { resolveServiceBinding } from '../environment-accessor/service-bindings'; -import { getIasClientCredentialsToken } from '../identity-service'; +import { getIasToken } from '../identity-service'; import { clientCredentialsTokenCache } from '../client-credentials-token-cache'; import { decodeJwt } from '../jwt'; import { serviceToken } from '../token-accessor'; @@ -9,7 +9,7 @@ import { } from './service-binding-to-destination'; jest.mock('../identity-service', () => ({ - getIasClientCredentialsToken: jest.fn() + getIasToken: jest.fn() })); jest.mock('../token-accessor', () => ({ @@ -128,7 +128,7 @@ const services = { describe('service binding to destination', () => { beforeAll(() => { (serviceToken as jest.Mock).mockResolvedValue('access-token'); - (getIasClientCredentialsToken as jest.Mock).mockResolvedValue({ + (getIasToken as jest.Mock).mockResolvedValue({ access_token: 'ias-access-token', token_type: 'Bearer', expires_in: 3600, @@ -305,7 +305,7 @@ describe('service binding to destination', () => { ]) }) ); - expect(getIasClientCredentialsToken).toHaveBeenCalledWith( + expect(getIasToken).toHaveBeenCalledWith( expect.objectContaining({ label: 'identity', name: 'my-identity-service' @@ -314,6 +314,40 @@ describe('service binding to destination', () => { ); }); + it('transforms identity (IAS) service binding for JWT bearer authentication', async () => { + const destination = await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + iasOptions: { + authenticationType: 'OAuth2JWTBearer', + assertion: 'user-jwt-token' + } + } + ); + expect(destination).toEqual( + expect.objectContaining({ + url: 'https://tenant.accounts.ondemand.com', + name: 'my-identity-service', + authentication: 'OAuth2JWTBearer', + authTokens: expect.arrayContaining([ + expect.objectContaining({ + value: 'ias-access-token', + type: 'bearer' + }) + ]) + }) + ); + expect(getIasToken).toHaveBeenCalledWith( + expect.objectContaining({ + label: 'identity' + }), + expect.objectContaining({ + authenticationType: 'OAuth2JWTBearer', + assertion: 'user-jwt-token' + }) + ); + }); + it('transforms identity (IAS) service binding with appName parameter', async () => { const destination = await transformServiceBindingToDestination( resolveServiceBinding('identity'), @@ -326,7 +360,7 @@ describe('service binding to destination', () => { authentication: 'OAuth2ClientCredentials' }) ); - expect(getIasClientCredentialsToken).toHaveBeenCalledWith( + expect(getIasToken).toHaveBeenCalledWith( expect.objectContaining({ label: 'identity' }), @@ -378,7 +412,7 @@ describe('service binding to destination', () => { jest.clearAllMocks(); clientCredentialsTokenCache.clear(); // Re-apply mock after clearAllMocks - (getIasClientCredentialsToken as jest.Mock).mockResolvedValue({ + (getIasToken as jest.Mock).mockResolvedValue({ access_token: 'ias-access-token', token_type: 'Bearer', expires_in: 3600, @@ -409,7 +443,7 @@ describe('service binding to destination', () => { } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledWith( + expect(getIasToken).toHaveBeenCalledWith( expect.objectContaining({ label: 'identity' }), @@ -506,7 +540,7 @@ describe('service binding to destination', () => { jest.clearAllMocks(); clientCredentialsTokenCache.clear(); // Re-apply mock after clearAllMocks - (getIasClientCredentialsToken as jest.Mock).mockResolvedValue({ + (getIasToken as jest.Mock).mockResolvedValue({ access_token: 'ias-access-token', token_type: 'Bearer', expires_in: 3600, @@ -521,7 +555,7 @@ describe('service binding to destination', () => { { jwt: { app_tid: 'tenant-123' } } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Second call should use cached token await transformServiceBindingToDestination( @@ -530,7 +564,7 @@ describe('service binding to destination', () => { ); // Should still only be called once due to cache hit - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); }); it('does not cache IAS token if useCache is false', async () => { @@ -539,7 +573,7 @@ describe('service binding to destination', () => { { jwt: { app_tid: 'tenant-123' }, useCache: false } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Second call should use cached token await transformServiceBindingToDestination( @@ -548,7 +582,7 @@ describe('service binding to destination', () => { ); // Should be called twice - no caching - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + expect(getIasToken).toHaveBeenCalledTimes(2); }); it('does cache IAS token if useCache is true', async () => { @@ -557,7 +591,7 @@ describe('service binding to destination', () => { { jwt: { app_tid: 'tenant-123' }, useCache: true } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Second call should use cached token await transformServiceBindingToDestination( @@ -566,7 +600,7 @@ describe('service binding to destination', () => { ); // Should still only be called once due to cache hit - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); }); it('uses cache key with resource parameter', async () => { @@ -580,7 +614,7 @@ describe('service binding to destination', () => { } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Second call with same resource should use cache await transformServiceBindingToDestination( @@ -591,7 +625,7 @@ describe('service binding to destination', () => { } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); }); it('does not use cache for different resource parameters', async () => { @@ -603,7 +637,7 @@ describe('service binding to destination', () => { } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Second call with different resource should NOT use cache await transformServiceBindingToDestination( @@ -614,7 +648,7 @@ describe('service binding to destination', () => { } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + expect(getIasToken).toHaveBeenCalledTimes(2); }); it('isolates cache by tenant (appTid)', async () => { @@ -623,7 +657,7 @@ describe('service binding to destination', () => { { jwt: { app_tid: 'tenant-123' } } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Different tenant should not use cache await transformServiceBindingToDestination( @@ -631,7 +665,7 @@ describe('service binding to destination', () => { { jwt: { app_tid: 'tenant-456' } } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + expect(getIasToken).toHaveBeenCalledTimes(2); }); it('supports resource with providerClientId', async () => { @@ -645,7 +679,7 @@ describe('service binding to destination', () => { } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Second call with same resource should use cache await transformServiceBindingToDestination( @@ -656,7 +690,7 @@ describe('service binding to destination', () => { } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); }); it('supports resource with providerClientId and providerTenantId', async () => { @@ -673,7 +707,7 @@ describe('service binding to destination', () => { } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Second call with same resource should use cache await transformServiceBindingToDestination( @@ -684,7 +718,7 @@ describe('service binding to destination', () => { } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); }); it('does not cache JWT bearer tokens (OAuth2JWTBearer)', async () => { @@ -699,7 +733,7 @@ describe('service binding to destination', () => { } ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Second call should NOT use cache for JWT bearer tokens await transformServiceBindingToDestination( @@ -714,7 +748,7 @@ describe('service binding to destination', () => { ); // Should be called twice - no caching for JWT bearer - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + expect(getIasToken).toHaveBeenCalledTimes(2); }); it('handles missing app_tid (no JWT, no provider tenant)', async () => { @@ -722,7 +756,7 @@ describe('service binding to destination', () => { resolveServiceBinding('identity') ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Verify cache was populated with just IAS tenant (no app_tid) const cached = clientCredentialsTokenCache.getTokenIas({ @@ -736,7 +770,7 @@ describe('service binding to destination', () => { resolveServiceBinding('identity') ); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); }); it('isolates cache by IAS tenant (different service URLs)', async () => { @@ -765,7 +799,7 @@ describe('service binding to destination', () => { jwt: { app_tid: 'tenant-123' } }); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(1); + expect(getIasToken).toHaveBeenCalledTimes(1); // Second call to IAS service 2 with same app_tid should NOT use cache // because IAS tenant is different @@ -773,14 +807,14 @@ describe('service binding to destination', () => { jwt: { app_tid: 'tenant-123' } }); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + expect(getIasToken).toHaveBeenCalledTimes(2); // Third call to IAS service 1 again with same app_tid should use cache await transformServiceBindingToDestination(iasService1, { jwt: { app_tid: 'tenant-123' } }); - expect(getIasClientCredentialsToken).toHaveBeenCalledTimes(2); + expect(getIasToken).toHaveBeenCalledTimes(2); }); }); }); diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index 1a48d7e843..7bb6895953 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -1,6 +1,6 @@ import { serviceToken } from '../token-accessor'; import { decodeJwt } from '../jwt'; -import { getIasClientCredentialsToken } from '../identity-service'; +import { getIasToken } from '../identity-service'; import { clientCredentialsTokenCache } from '../client-credentials-token-cache'; import { parseUrlAndGetHost } from '../subdomain-replacer'; import type { Service } from '../environment-accessor'; @@ -8,7 +8,10 @@ import type { ServiceBindingTransformFunction, ServiceBindingTransformOptions } from './destination-from-vcap'; -import type { Destination } from './destination-service-types'; +import type { + AuthenticationType, + Destination +} from './destination-service-types'; import type { IasOptions, IasOptionsTechnicalUser } from './ias-types'; import type { CachingOptions } from '../cache'; @@ -76,11 +79,7 @@ export async function transformServiceBindingToClientCredentialsDestination( options?: ServiceBindingTransformOptions & { url?: string } ): Promise { const token = await serviceToken(service, options); - return buildClientCredentialsDestination( - token, - options?.url ?? service.url, - service.name - ); + return buildDestination(token, options?.url ?? service.url, service.name); } async function aicoreToDestination( @@ -88,7 +87,7 @@ async function aicoreToDestination( options?: ServiceBindingTransformOptions ): Promise { const token = await serviceToken(service, options); - return buildClientCredentialsDestination( + return buildDestination( token, service.credentials.serviceurls.AI_API_URL, service.name @@ -100,11 +99,7 @@ async function xsuaaToDestination( options?: ServiceBindingTransformOptions ): Promise { const token = await serviceToken(service, options); - return buildClientCredentialsDestination( - token, - service.credentials.apiurl, - service.name - ); + return buildDestination(token, service.credentials.apiurl, service.name); } async function serviceManagerBindingToDestination( @@ -112,11 +107,7 @@ async function serviceManagerBindingToDestination( options?: ServiceBindingTransformOptions ): Promise { const token = await serviceToken(service, options); - return buildClientCredentialsDestination( - token, - service.credentials.sm_url, - service.name - ); + return buildDestination(token, service.credentials.sm_url, service.name); } async function destinationBindingToDestination( @@ -124,11 +115,7 @@ async function destinationBindingToDestination( options?: ServiceBindingTransformOptions ): Promise { const token = await serviceToken(service, options); - return buildClientCredentialsDestination( - token, - service.credentials.uri, - service.name - ); + return buildDestination(token, service.credentials.uri, service.name); } async function saasRegistryBindingToDestination( @@ -136,7 +123,7 @@ async function saasRegistryBindingToDestination( options?: ServiceBindingTransformOptions ): Promise { const token = await serviceToken(service, options); - return buildClientCredentialsDestination( + return buildDestination( token, service.credentials['saas_registry_url'], service.name @@ -152,11 +139,7 @@ async function businessLoggingBindingToDestination( credentials: { ...service.credentials.uaa } }; const token = await serviceToken(transformedService, options); - return buildClientCredentialsDestination( - token, - service.credentials.writeUrl, - service.name - ); + return buildDestination(token, service.credentials.writeUrl, service.name); } async function workflowBindingToDestination( @@ -168,7 +151,7 @@ async function workflowBindingToDestination( credentials: { ...service.credentials.uaa } }; const token = await serviceToken(transformedService, options); - return buildClientCredentialsDestination( + return buildDestination( token, service.credentials.endpoints.workflow_odata_url, service.name @@ -214,18 +197,19 @@ function getIasAppTid( * Uses `targetUrl` as the destination URL if supplied and adds `mtlsKeyPair` if available. * @param accessToken - The JWT token to access the service. * @param service - Service binding for identity service. - * @param options - Service bidning transform options. + * @param iasOptions - IAS options to build the destination. * @returns A destination object. */ function buildIasDestination( accessToken: string, service: Service, - options?: ServiceBindingTransformOptions + iasOptions: IasOptions ): Destination { - const destination = buildClientCredentialsDestination( + const destination = buildDestination( accessToken, - options?.iasOptions?.targetUrl ?? service.credentials.url, - service.name + iasOptions?.targetUrl ?? service.credentials.url, + service.name, + iasOptions.authenticationType || 'OAuth2ClientCredentials' ); // Add mTLS key pair if available @@ -264,12 +248,12 @@ async function transformIasBindingToDestination( resource: options?.iasOptions?.resource }); if (cached) { - return buildIasDestination(cached.access_token, service, options); + return buildIasDestination(cached.access_token, service, iasOptions); } } } - const response = await getIasClientCredentialsToken(service, { + const response = await getIasToken(service, { jwt: options?.jwt, ...iasOptions }); @@ -290,13 +274,24 @@ async function transformIasBindingToDestination( ); } - return buildIasDestination(response.access_token, service, options); + return buildIasDestination(response.access_token, service, iasOptions); } -function buildClientCredentialsDestination( +/** + * Builds a destination object with a token, name, and url. + * If no authentication type is provided, 'OAuth2ClientCredentials' is used by default. + * @internal + * @param token - The access token for the destination. + * @param url - The URL of the destination. + * @param name - The name of the destination. + * @param authentication - The authentication type for the destination. Defaults to 'OAuth2ClientCredentials'. + * @returns A destination object. + */ +function buildDestination( token: string, url: string, - name: string + name: string, + authentication: AuthenticationType = 'OAuth2ClientCredentials' ): Destination { const expirationTime = decodeJwt(token).exp; const expiresIn = expirationTime @@ -305,7 +300,7 @@ function buildClientCredentialsDestination( return { url, name, - authentication: 'OAuth2ClientCredentials', + authentication, authTokens: [ { value: token, diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 6987feb3bb..716a09a0be 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -1,6 +1,6 @@ import { signedJwt } from '../../../../test-resources/test/test-util'; import { - getIasClientCredentialsToken, + getIasToken, shouldExchangeToken, identityServicesCache } from './identity-service'; @@ -76,7 +76,7 @@ describe('shouldExchangeToken', () => { }); }); -describe('getIasClientCredentialsToken', () => { +describe('getIasToken', () => { const mockIasService: Service = { name: 'my-identity-service', label: 'identity', @@ -112,7 +112,7 @@ describe('getIasClientCredentialsToken', () => { it('fetches IAS token with mTLS authentication', async () => { mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); - const result = await getIasClientCredentialsToken(mockIasService); + const result = await getIasToken(mockIasService); expect(result).toEqual({ access_token: mockTokenResponse.access_token, @@ -143,7 +143,7 @@ describe('getIasClientCredentialsToken', () => { mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); - const result = await getIasClientCredentialsToken(serviceWithSecret); + const result = await getIasToken(serviceWithSecret); expect(result).toEqual({ access_token: mockTokenResponse.access_token, @@ -165,7 +165,7 @@ describe('getIasClientCredentialsToken', () => { it('includes resource parameter for app2app flow', async () => { mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); - await getIasClientCredentialsToken(mockIasService, { + await getIasToken(mockIasService, { resource: { name: 'my-app' } }); @@ -178,7 +178,7 @@ describe('getIasClientCredentialsToken', () => { it('includes appTid parameter for multi-tenant scenarios', async () => { mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); - await getIasClientCredentialsToken(mockIasService, { + await getIasToken(mockIasService, { appTid: 'tenant-123' }); @@ -191,7 +191,7 @@ describe('getIasClientCredentialsToken', () => { it('includes both appName and appTid parameters', async () => { mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); - await getIasClientCredentialsToken(mockIasService, { + await getIasToken(mockIasService, { resource: { name: 'my-app' }, appTid: 'tenant-123' }); @@ -206,7 +206,7 @@ describe('getIasClientCredentialsToken', () => { it('includes extraParams for additional OAuth2 parameters', async () => { mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); - await getIasClientCredentialsToken(mockIasService, { + await getIasToken(mockIasService, { extraParams: { custom_param: 'custom_value' } as any }); @@ -221,7 +221,7 @@ describe('getIasClientCredentialsToken', () => { new Error('Network error') ); - await expect(getIasClientCredentialsToken(mockIasService)).rejects.toThrow( + await expect(getIasToken(mockIasService)).rejects.toThrow( 'Could not fetch IAS client credentials token for service "my-identity-service" of type identity: Network error' ); }); @@ -231,7 +231,7 @@ describe('getIasClientCredentialsToken', () => { error.response = { status: 401 }; mockFetchClientCredentialsToken.mockRejectedValue(error); - await expect(getIasClientCredentialsToken(mockIasService)).rejects.toThrow( + await expect(getIasToken(mockIasService)).rejects.toThrow( /ensure that the service instance is declared as dependency to SaaS Provisioning Service or Subscription Manager/ ); }); @@ -240,7 +240,7 @@ describe('getIasClientCredentialsToken', () => { it('uses OAuth2ClientCredentials (technical-user) by default', async () => { mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); - await getIasClientCredentialsToken(mockIasService, {}); + await getIasToken(mockIasService, {}); expect(mockFetchClientCredentialsToken).toHaveBeenCalled(); expect(mockFetchJwtBearerToken).not.toHaveBeenCalled(); @@ -249,7 +249,7 @@ describe('getIasClientCredentialsToken', () => { it('uses client credentials for OAuth2ClientCredentials', async () => { mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); - await getIasClientCredentialsToken(mockIasService, { + await getIasToken(mockIasService, { authenticationType: 'OAuth2ClientCredentials' }); @@ -266,7 +266,7 @@ describe('getIasClientCredentialsToken', () => { app_tid: 'tenant-456' }); - await getIasClientCredentialsToken(mockIasService, { + await getIasToken(mockIasService, { authenticationType: 'OAuth2JWTBearer', assertion: userAssertion }); @@ -281,7 +281,7 @@ describe('getIasClientCredentialsToken', () => { it('throws error for OAuth2JWTBearer (business-user) without assertion', async () => { await expect( - getIasClientCredentialsToken(mockIasService, { + getIasToken(mockIasService, { authenticationType: 'OAuth2JWTBearer' } as any) ).rejects.toThrow( @@ -298,7 +298,7 @@ describe('getIasClientCredentialsToken', () => { app_tid: 'tenant-456' }); - await getIasClientCredentialsToken(mockIasService, { + await getIasToken(mockIasService, { authenticationType: 'OAuth2JWTBearer', assertion: userAssertion, resource: { name: 'my-app' }, @@ -341,7 +341,7 @@ describe('getIasClientCredentialsToken', () => { user_uuid: 'user-123' }); - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion }); @@ -360,7 +360,7 @@ describe('getIasClientCredentialsToken', () => { const { IdentityService } = jest.requireMock('@sap/xssec'); - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion }); @@ -384,7 +384,7 @@ describe('getIasClientCredentialsToken', () => { mockFetchClientCredentialsToken.mockResolvedValue(mockTokenResponse); - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2ClientCredentials' }); @@ -404,7 +404,7 @@ describe('getIasClientCredentialsToken', () => { user_uuid: 'user-123' }); - const result = await getIasClientCredentialsToken(providerService, { + const result = await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion }); @@ -420,7 +420,7 @@ describe('getIasClientCredentialsToken', () => { user_uuid: 'user-123' }); - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion, resource: { name: 'my-app' } @@ -446,7 +446,7 @@ describe('getIasClientCredentialsToken', () => { }; mockFetchJwtBearerToken.mockResolvedValue(responseWithRefreshToken); - const result = await getIasClientCredentialsToken(providerService, { + const result = await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion }); @@ -460,7 +460,7 @@ describe('getIasClientCredentialsToken', () => { user_uuid: 'user-123' }); - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion, appTid: 'some-tenant-id' @@ -480,7 +480,7 @@ describe('getIasClientCredentialsToken', () => { app_tid: 'extracted-tenant-id' }); - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion }); @@ -499,7 +499,7 @@ describe('getIasClientCredentialsToken', () => { // No app_tid or zone_uuid }); - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion }); @@ -517,7 +517,7 @@ describe('getIasClientCredentialsToken', () => { app_tid: 'assertion-tenant-id' }); - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion, appTid: 'explicit-tenant-id' @@ -547,7 +547,7 @@ describe('getIasClientCredentialsToken', () => { const { IdentityService } = jest.requireMock('@sap/xssec'); // First call with subscriber1 - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion: assertion1 }); @@ -555,7 +555,7 @@ describe('getIasClientCredentialsToken', () => { const callsAfterFirst = IdentityService.mock.calls.length; // Second call with same subscriber1 - should use cached instance - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion: assertion1 }); @@ -564,7 +564,7 @@ describe('getIasClientCredentialsToken', () => { expect(IdentityService.mock.calls.length).toBe(callsAfterFirst); // Third call with different subscriber2 - should create new instance - await getIasClientCredentialsToken(providerService, { + await getIasToken(providerService, { authenticationType: 'OAuth2JWTBearer', assertion: assertion2 }); @@ -593,7 +593,7 @@ describe('getIasClientCredentialsToken', () => { const { IdentityService } = jest.requireMock('@sap/xssec'); - await getIasClientCredentialsToken(serviceWithSlash, { + await getIasToken(serviceWithSlash, { authenticationType: 'OAuth2JWTBearer', assertion }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 6be36f1639..753309d46d 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -84,7 +84,7 @@ type IasParameters = { * @internal * @experimental */ -export async function getIasClientCredentialsToken( +export async function getIasToken( service: string | Service, options: IasOptions & { jwt?: JwtPayload } = {} ): Promise { @@ -100,7 +100,7 @@ export async function getIasClientCredentialsToken( IasClientCredentialsResponse, MiddlewareContext >(resilience(), { - fn: getIasClientCredentialsTokenImpl, + fn: getIasTokenImpl, fnArgument, context: { uri: fnArgument.serviceCredentials.url, @@ -204,7 +204,7 @@ function transformIasOptionsToXssecArgs( * @returns A promise resolving to the client credentials response. * @internal */ -async function getIasClientCredentialsTokenImpl( +async function getIasTokenImpl( arg: IasParameters ): Promise { const identityService = getIdentityServiceInstanceFromCredentials( From d349c6efd4fd1453456152662d89b70dd87504b2 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 23 Jan 2026 09:34:09 +0100 Subject: [PATCH 55/58] add additional unit tests for OAuth2JWTBearer --- .../service-binding-to-destination.spec.ts | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts index 66fd56da09..1d4b7578fc 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.spec.ts @@ -407,6 +407,121 @@ describe('service binding to destination', () => { ); }); + it('transforms identity (IAS) service binding for JWT bearer with mTLS cert/key', async () => { + const destination = await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + iasOptions: { + authenticationType: 'OAuth2JWTBearer', + assertion: 'user-jwt-token' + } + } + ); + expect(destination).toEqual( + expect.objectContaining({ + url: 'https://tenant.accounts.ondemand.com', + name: 'my-identity-service', + authentication: 'OAuth2JWTBearer', + mtlsKeyPair: { + cert: '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----', + key: '-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----' + }, + authTokens: expect.arrayContaining([ + expect.objectContaining({ + value: 'ias-access-token', + type: 'bearer' + }) + ]) + }) + ); + }); + + it('transforms identity (IAS) service binding for JWT bearer with custom targetUrl', async () => { + const destination = await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + iasOptions: { + authenticationType: 'OAuth2JWTBearer', + assertion: 'user-jwt-token', + targetUrl: 'https://custom-target.example.com' + } + } + ); + expect(destination).toEqual( + expect.objectContaining({ + url: 'https://custom-target.example.com', + name: 'my-identity-service', + authentication: 'OAuth2JWTBearer' + }) + ); + }); + + it('transforms identity (IAS) service binding for JWT bearer with resource parameter', async () => { + const destination = await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + iasOptions: { + authenticationType: 'OAuth2JWTBearer', + assertion: 'user-jwt-token', + resource: { name: 'my-target-app' } + } + } + ); + expect(destination).toEqual( + expect.objectContaining({ + url: 'https://tenant.accounts.ondemand.com', + name: 'my-identity-service', + authentication: 'OAuth2JWTBearer' + }) + ); + expect(getIasToken).toHaveBeenCalledWith( + expect.objectContaining({ + label: 'identity' + }), + expect.objectContaining({ + authenticationType: 'OAuth2JWTBearer', + assertion: 'user-jwt-token', + resource: { name: 'my-target-app' } + }) + ); + }); + + it('transforms identity (IAS) service binding for JWT bearer with resource providerClientId', async () => { + const destination = await transformServiceBindingToDestination( + resolveServiceBinding('identity'), + { + iasOptions: { + authenticationType: 'OAuth2JWTBearer', + assertion: 'user-jwt-token', + resource: { + providerClientId: 'target-client-id', + providerTenantId: 'target-tenant-id' + } + } + } + ); + expect(destination).toEqual( + expect.objectContaining({ + url: 'https://tenant.accounts.ondemand.com', + name: 'my-identity-service', + authentication: 'OAuth2JWTBearer' + }) + ); + expect(getIasToken).toHaveBeenCalledWith( + expect.objectContaining({ + label: 'identity' + }), + expect.objectContaining({ + authenticationType: 'OAuth2JWTBearer', + assertion: 'user-jwt-token', + resource: { + providerClientId: 'target-client-id', + providerTenantId: 'target-tenant-id' + } + }) + ); + }); + describe('transformIasBindingToDestination requestAs handling', () => { beforeEach(() => { jest.clearAllMocks(); From 2ee6a9ef137a7b8de6c812e72cc39b76ab96eebf Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 23 Jan 2026 14:35:41 +0100 Subject: [PATCH 56/58] address review comments --- .../service-binding-to-destination.ts | 5 ++++- .../src/scp-cf/identity-service.spec.ts | 2 +- .../connectivity/src/scp-cf/identity-service.ts | 16 +++++++--------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts index 7bb6895953..5e53438bfb 100644 --- a/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts +++ b/packages/connectivity/src/scp-cf/destination/service-binding-to-destination.ts @@ -291,7 +291,10 @@ function buildDestination( token: string, url: string, name: string, - authentication: AuthenticationType = 'OAuth2ClientCredentials' + authentication: Extract< + AuthenticationType, + 'OAuth2ClientCredentials' | 'OAuth2JWTBearer' + > = 'OAuth2ClientCredentials' ): Destination { const expirationTime = decodeJwt(token).exp; const expiresIn = expirationTime diff --git a/packages/connectivity/src/scp-cf/identity-service.spec.ts b/packages/connectivity/src/scp-cf/identity-service.spec.ts index 716a09a0be..aabdd38471 100644 --- a/packages/connectivity/src/scp-cf/identity-service.spec.ts +++ b/packages/connectivity/src/scp-cf/identity-service.spec.ts @@ -222,7 +222,7 @@ describe('getIasToken', () => { ); await expect(getIasToken(mockIasService)).rejects.toThrow( - 'Could not fetch IAS client credentials token for service "my-identity-service" of type identity: Network error' + 'Could not fetch IAS client for service "my-identity-service" of type identity: Network error' ); }); diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 753309d46d..8c18ebf174 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -21,10 +21,10 @@ export { identityServicesCache } from './environment-accessor'; /** * @internal - * Represents the response to an IAS client credentials request. - * Extends the XSUAA response with IAS-specific fields. + * Represents the response to an IAS token request using client credentials or JWT bearer grant. + * This interface extends the XSUAA `ClientCredentialsResponse` response with IAS-specific fields. */ -export interface IasClientCredentialsResponse extends ClientCredentialsResponse { +export interface IasTokenResponse extends ClientCredentialsResponse { /** * Audience claim from the JWT token. */ @@ -87,7 +87,7 @@ type IasParameters = { export async function getIasToken( service: string | Service, options: IasOptions & { jwt?: JwtPayload } = {} -): Promise { +): Promise { const resolvedService = resolveServiceBinding(service); const fnArgument: IasParameters = { @@ -97,7 +97,7 @@ export async function getIasToken( const token = await executeWithMiddleware< IasParameters, - IasClientCredentialsResponse, + IasTokenResponse, MiddlewareContext >(resilience(), { fn: getIasTokenImpl, @@ -109,7 +109,7 @@ export async function getIasToken( }).catch(err => { const serviceName = typeof service === 'string' ? service : service.name || 'unknown'; - let message = `Could not fetch IAS client credentials token for service "${serviceName}" of type ${resolvedService.label}`; + let message = `Could not fetch IAS client for service "${serviceName}" of type ${resolvedService.label}`; // Add contextual hints based on error status code (similar to Java SDK) if (err.response?.status === 401) { @@ -204,9 +204,7 @@ function transformIasOptionsToXssecArgs( * @returns A promise resolving to the client credentials response. * @internal */ -async function getIasTokenImpl( - arg: IasParameters -): Promise { +async function getIasTokenImpl(arg: IasParameters): Promise { const identityService = getIdentityServiceInstanceFromCredentials( arg.serviceCredentials, arg.assertion From d3391225d82c1c03bc657069156109dee3efeeda Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 23 Jan 2026 15:19:24 +0100 Subject: [PATCH 57/58] remove @experimental from `getIasToken` --- packages/connectivity/src/scp-cf/identity-service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/connectivity/src/scp-cf/identity-service.ts b/packages/connectivity/src/scp-cf/identity-service.ts index 8c18ebf174..c2907295d6 100644 --- a/packages/connectivity/src/scp-cf/identity-service.ts +++ b/packages/connectivity/src/scp-cf/identity-service.ts @@ -82,7 +82,6 @@ type IasParameters = { * @param options - Options for token fetching, including authenticationType to specify authentication mode, optional resource parameter for app2app, appTid for multi-tenant scenarios, and extraParams for additional OAuth2 parameters. * @returns Client credentials token response. * @internal - * @experimental */ export async function getIasToken( service: string | Service, From 5712f9811e01710007bb188c1e86a5e37f211a53 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Fri, 23 Jan 2026 15:22:10 +0100 Subject: [PATCH 58/58] changeset: remove (experimental) --- .changeset/slow-cars-lie.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/slow-cars-lie.md b/.changeset/slow-cars-lie.md index ac0acd820d..a4608714d7 100644 --- a/.changeset/slow-cars-lie.md +++ b/.changeset/slow-cars-lie.md @@ -2,4 +2,4 @@ '@sap-cloud-sdk/connectivity': minor --- -[New Functionality] Support IAS (App-to-App) authentication (experimental). Use `transformServiceBindingToDestination()` function or `getDestinationFromServiceBinding()` function to create a destination targeting an IAS application. +[New Functionality] Support IAS (App-to-App) authentication. Use `transformServiceBindingToDestination()` function or `getDestinationFromServiceBinding()` function to create a destination targeting an IAS application.