Skip to content

Commit 51cab2a

Browse files
authored
fix: Remove destination cache when getting destinations from service binding (#5635)
* fix: remove destination cache for getting from vcap * lint and lock * changeset * try to trigger workflows * fix: using jest.restoreAllMocks
1 parent 3dc1b5a commit 51cab2a

File tree

4 files changed

+39
-142
lines changed

4 files changed

+39
-142
lines changed

.changeset/twenty-ideas-sleep.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sap-cloud-sdk/connectivity': patch
3+
---
4+
5+
[Fixed Issue] Remove destination cache in `getDestinationFromServiceBinding()` function to let cached destinations retrieved in `getDestinationFromDestinationService()` function be added with the `proxyConfiguration` property.

packages/connectivity/src/scp-cf/destination/destination-from-vcap.spec.ts

Lines changed: 25 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,32 @@
11
import {
22
mockServiceToken,
3-
providerUserPayload,
43
providerUserToken,
5-
signedJwt,
6-
signedJwtForVerification,
7-
testTenants
4+
signedJwt
85
} from '../../../../../test-resources/test/test-util';
9-
import * as tokenAccessor from '../token-accessor';
10-
import { decodeJwt } from '../jwt';
6+
import * as xsuaaService from '../xsuaa-service';
7+
import { clientCredentialsTokenCache } from '../client-credentials-token-cache';
118
import { getDestination } from './destination-accessor';
129
import { getDestinationFromServiceBinding } from './destination-from-vcap';
13-
import { destinationCache } from './destination-cache';
1410
import SpyInstance = jest.SpyInstance;
1511
import type { Service } from '../environment-accessor';
1612

1713
describe('vcap-service-destination', () => {
18-
const serviceTokenSpy = jest.spyOn(tokenAccessor, 'serviceToken');
19-
20-
beforeAll(() => {
21-
mockServiceToken();
22-
});
23-
2414
beforeEach(() => {
2515
mockServiceBindings();
2616
});
2717

28-
afterEach(async () => {
18+
afterEach(() => {
2919
delete process.env.VCAP_SERVICES;
30-
jest.clearAllMocks();
31-
await destinationCache.clear();
20+
clientCredentialsTokenCache.clear();
21+
jest.restoreAllMocks();
3222
});
3323

3424
function getActualClientId(spyInstance: SpyInstance): string {
3525
return spyInstance.mock.calls[0][0]['credentials']['clientid'];
3626
}
3727

3828
it('creates a destination for the aicore service', async () => {
29+
const serviceTokenSpy = mockServiceToken();
3930
await expect(
4031
getDestinationFromServiceBinding({
4132
destinationName: 'my-aicore',
@@ -52,6 +43,7 @@ describe('vcap-service-destination', () => {
5243
});
5344

5445
it('creates a destination for the business logging service', async () => {
46+
const serviceTokenSpy = mockServiceToken();
5547
await expect(
5648
getDestinationFromServiceBinding({
5749
destinationName: 'my-business-logging',
@@ -68,6 +60,7 @@ describe('vcap-service-destination', () => {
6860
});
6961

7062
it('creates ad destination for the xsuaa service', async () => {
63+
const serviceTokenSpy = mockServiceToken();
7164
await expect(
7265
getDestinationFromServiceBinding({
7366
destinationName: 'my-xsuaa',
@@ -84,6 +77,7 @@ describe('vcap-service-destination', () => {
8477
});
8578

8679
it('creates a destination for the service manager service', async () => {
80+
const serviceTokenSpy = mockServiceToken();
8781
await expect(
8882
getDestinationFromServiceBinding({
8983
destinationName: 'my-service-manager',
@@ -100,6 +94,7 @@ describe('vcap-service-destination', () => {
10094
});
10195

10296
it('creates a destination for the destination service', async () => {
97+
const serviceTokenSpy = mockServiceToken();
10398
await expect(
10499
getDestinationFromServiceBinding({
105100
destinationName: 'my-destination-service',
@@ -115,6 +110,7 @@ describe('vcap-service-destination', () => {
115110
});
116111

117112
it('creates a destination for the saas registry', async () => {
113+
const serviceTokenSpy = mockServiceToken();
118114
await expect(
119115
getDestinationFromServiceBinding({
120116
destinationName: 'my-saas-registry',
@@ -130,6 +126,7 @@ describe('vcap-service-destination', () => {
130126
});
131127

132128
it('creates a destination for the workflow', async () => {
129+
const serviceTokenSpy = mockServiceToken();
133130
await expect(
134131
getDestinationFromServiceBinding({
135132
destinationName: 'my-workflow',
@@ -157,6 +154,16 @@ describe('vcap-service-destination', () => {
157154
});
158155

159156
it('uses the cache if enabled', async () => {
157+
const getClientCredentialsTokenSpy = jest
158+
.spyOn(xsuaaService, 'getClientCredentialsToken')
159+
.mockResolvedValue({
160+
access_token: providerUserToken,
161+
token_type: 'bearer',
162+
expires_in: 1000,
163+
scope: 'some scope',
164+
jti: 'some jti'
165+
});
166+
160167
await getDestinationFromServiceBinding({
161168
destinationName: 'my-destination-service',
162169
useCache: true,
@@ -167,35 +174,7 @@ describe('vcap-service-destination', () => {
167174
useCache: true,
168175
jwt: providerUserToken
169176
});
170-
expect(serviceTokenSpy).toBeCalledTimes(1);
171-
expect(
172-
destinationCache
173-
.getCacheInstance()
174-
.get(`${providerUserPayload.zid}::my-destination`)
175-
).toBeDefined();
176-
});
177-
178-
it('returns undefined if cached destination JWT has expired', async () => {
179-
const jwtPayload = {
180-
iat: 1692273899,
181-
exp: 1692317098,
182-
zid: testTenants.provider,
183-
user_id: 'user-prov',
184-
ext_attr: { enhancer: 'XSUAA' }
185-
};
186-
const jwt = signedJwtForVerification(jwtPayload);
187-
jest.useFakeTimers();
188-
await getDestinationFromServiceBinding({
189-
destinationName: 'my-destination-service',
190-
useCache: true,
191-
jwt
192-
});
193-
jest.advanceTimersByTime(720 * 60 * 1000 + 1);
194-
await expect(
195-
destinationCache
196-
.getCacheInstance()
197-
.get(`${jwtPayload.zid}::my-destination-service`)
198-
).resolves.toBeUndefined();
177+
expect(getClientCredentialsTokenSpy).toHaveBeenCalledTimes(1);
199178
});
200179

201180
it('creates a destination using a custom transformation function', async () => {
@@ -274,7 +253,7 @@ describe('vcap-service-destination', () => {
274253
expect(serviceBindingTransformFn).toBeCalledTimes(1);
275254
});
276255

277-
it('sets forwarded auth token if needed (uncached)', async () => {
256+
it('sets forwarded auth token if needed', async () => {
278257
const jwt = signedJwt({});
279258

280259
const destination = await getDestinationFromServiceBinding({
@@ -288,51 +267,6 @@ describe('vcap-service-destination', () => {
288267

289268
expect(destination?.authTokens?.[0]).toMatchObject({ value: jwt });
290269
});
291-
292-
it('sets forwarded auth token if needed (cached)', async () => {
293-
const jwt = signedJwt({ zid: 'tenant' });
294-
295-
destinationCache.cacheRetrievedDestination(
296-
decodeJwt(jwt),
297-
{
298-
name: 'my-custom-service',
299-
forwardAuthToken: true
300-
},
301-
'tenant'
302-
);
303-
304-
const destination = await getDestinationFromServiceBinding({
305-
destinationName: 'my-custom-service',
306-
useCache: true,
307-
jwt
308-
});
309-
310-
expect(destination?.authTokens?.[0]).toMatchObject({ value: jwt });
311-
});
312-
313-
// TODO: fix in #1123
314-
it.skip('does not cache the forwarded token', async () => {
315-
const jwt = signedJwt({ zid: 'tenant' });
316-
317-
await getDestinationFromServiceBinding({
318-
destinationName: 'my-custom-service',
319-
useCache: true,
320-
jwt,
321-
serviceBindingTransformFn: async ({ name }) => ({
322-
forwardAuthToken: true,
323-
name
324-
})
325-
});
326-
327-
const destination = await destinationCache.retrieveDestinationFromCache(
328-
decodeJwt(jwt),
329-
'my-custom-service',
330-
'tenant'
331-
);
332-
333-
expect(destination).toBeDefined();
334-
expect(destination?.authTokens).toBeUndefined();
335-
});
336270
});
337271

338272
function mockServiceBindings() {

packages/connectivity/src/scp-cf/destination/destination-from-vcap.ts

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { createLogger } from '@sap-cloud-sdk/util';
2-
import { decodeJwt, decodeOrMakeJwt } from '../jwt';
2+
import { decodeJwt } from '../jwt';
33
import { getServiceBindingByInstanceName } from '../environment-accessor';
44
import {
55
addProxyConfigurationInternet,
66
proxyStrategy
77
} from './http-proxy-util';
88
import { isHttpDestination } from './destination-service-types';
9-
import { destinationCache } from './destination-cache';
109
import { serviceToDestinationTransformers } from './service-binding-to-destination';
1110
import { setForwardedAuthTokenIfNeeded } from './forward-auth-token';
1211
import type { DestinationFetchOptions } from './destination-accessor-types';
@@ -54,27 +53,8 @@ export async function getDestinationFromServiceBinding(
5453
: undefined;
5554

5655
const retrievalOptions = { ...options, jwt: decodedJwt };
57-
let destination;
58-
if (options.useCache) {
59-
destination = await destinationCache.retrieveDestinationFromCache(
60-
decodeOrMakeJwt(retrievalOptions.jwt),
61-
retrievalOptions.destinationName,
62-
'tenant'
63-
);
64-
}
56+
const destination = await retrieveDestination(retrievalOptions);
6557

66-
if (!destination) {
67-
destination = await retrieveDestinationWithoutCache(retrievalOptions);
68-
69-
if (options.useCache) {
70-
// As the grant type is clientCredential, isolation strategy is 'tenant'.
71-
await destinationCache.cacheRetrievedDestination(
72-
decodeOrMakeJwt(options.jwt),
73-
destination,
74-
'tenant'
75-
);
76-
}
77-
}
7858
const destWithProxy =
7959
destination &&
8060
isHttpDestination(destination) &&
@@ -89,7 +69,7 @@ export async function getDestinationFromServiceBinding(
8969
return destWithProxy;
9070
}
9171

92-
async function retrieveDestinationWithoutCache({
72+
async function retrieveDestination({
9373
useCache,
9474
jwt,
9575
destinationName,
@@ -120,19 +100,6 @@ export async function destinationForServiceBinding(
120100
options: DestinationForServiceBindingOptions &
121101
PartialDestinationFetchOptions = {}
122102
): Promise<Destination> {
123-
if (options.useCache) {
124-
const destinationFromCache =
125-
await destinationCache.retrieveDestinationFromCache(
126-
decodeOrMakeJwt(options.jwt),
127-
serviceInstanceName,
128-
'tenant'
129-
);
130-
131-
if (destinationFromCache) {
132-
return destinationFromCache;
133-
}
134-
}
135-
136103
const optionsForTransformation = {
137104
useCache: options.useCache,
138105
jwt: options.jwt
@@ -148,15 +115,6 @@ export async function destinationForServiceBinding(
148115
? addProxyConfigurationInternet(destination)
149116
: destination;
150117

151-
if (options.useCache) {
152-
// As the grant type is clientCredential, isolation strategy is 'tenant'.
153-
await destinationCache.cacheRetrievedDestination(
154-
decodeOrMakeJwt(options.jwt),
155-
destWithProxy,
156-
'tenant'
157-
);
158-
}
159-
160118
return destWithProxy;
161119
}
162120

yarn.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,12 +1677,12 @@
16771677
dependencies:
16781678
"@sinonjs/commons" "^3.0.0"
16791679

1680-
"@stylistic/eslint-plugin@^4.1.0":
1681-
version "4.1.0"
1682-
resolved "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.1.0.tgz#8882a6fe307cc94022e4495720373e8a5652d60c"
1683-
integrity sha512-bytbL7qiici7yPyEiId0fGPK9kjQbzcPMj2aftPfzTCyJ/CRSKdtI+iVjM0LSGzGxfunflI+MDDU9vyIIeIpoQ==
1680+
"@stylistic/eslint-plugin@^3.1.0":
1681+
version "3.1.0"
1682+
resolved "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz#a9f655c518f76bfc5feb46b467d0f06e511b289d"
1683+
integrity sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==
16841684
dependencies:
1685-
"@typescript-eslint/utils" "^8.23.0"
1685+
"@typescript-eslint/utils" "^8.13.0"
16861686
eslint-visitor-keys "^4.2.0"
16871687
espree "^10.3.0"
16881688
estraverse "^5.3.0"
@@ -2128,7 +2128,7 @@
21282128
semver "^7.6.0"
21292129
ts-api-utils "^2.0.1"
21302130

2131-
"@typescript-eslint/[email protected]", "@typescript-eslint/utils@^8.13.0", "@typescript-eslint/utils@^8.23.0":
2131+
"@typescript-eslint/[email protected]", "@typescript-eslint/utils@^8.13.0":
21322132
version "8.26.0"
21332133
resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.0.tgz#845d20ed8378a5594e6445f54e53b972aee7b3e6"
21342134
integrity sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==

0 commit comments

Comments
 (0)