@commercetools/checkout-payments-processor-sdk 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +42 -0
- package/biome.json +38 -0
- package/package.json +48 -0
- package/src/api/context/request-context.helper.test.ts +98 -0
- package/src/api/context/request-context.helper.ts +89 -0
- package/src/api/context/request-context.provider.ts +27 -0
- package/src/api/context/types/request-context.type.ts +18 -0
- package/src/api/handlers/payment-components.handler.test.ts +378 -0
- package/src/api/handlers/payment-components.handler.ts +80 -0
- package/src/api/handlers/payment-intents.handler.test.ts +475 -0
- package/src/api/handlers/payment-intents.handler.ts +169 -0
- package/src/api/handlers/status.handler.test.ts +551 -0
- package/src/api/handlers/status.handler.ts +216 -0
- package/src/api/handlers/transaction.handler.ts +86 -0
- package/src/api/handlers/types/handler.type.ts +49 -0
- package/src/api/hooks/authorize.hook.test.ts +62 -0
- package/src/api/hooks/authorize.hook.ts +51 -0
- package/src/api/hooks/jwt-auth.hook.test.ts +54 -0
- package/src/api/hooks/jwt-auth.hook.ts +56 -0
- package/src/api/hooks/oauth2-auth.hook.test.ts +173 -0
- package/src/api/hooks/oauth2-auth.hook.ts +46 -0
- package/src/api/hooks/session-header-auth.hook.ts +47 -0
- package/src/api/hooks/session-query-param-auth.hook.ts +46 -0
- package/src/api/hooks/types/hook.type.ts +12 -0
- package/src/api/index.ts +11 -0
- package/src/api/types.ts +3 -0
- package/src/commercetools/api/base-api.ts +28 -0
- package/src/commercetools/api/cart-api.test.ts +95 -0
- package/src/commercetools/api/cart-api.ts +55 -0
- package/src/commercetools/api/custom-type-api.ts +39 -0
- package/src/commercetools/api/order-api.ts +22 -0
- package/src/commercetools/api/payment-api.ts +64 -0
- package/src/commercetools/api/payment-method-api.ts +118 -0
- package/src/commercetools/api/root-api.ts +109 -0
- package/src/commercetools/errors/ct-api.error.ts +27 -0
- package/src/commercetools/helpers/currency.converter.test.ts +121 -0
- package/src/commercetools/helpers/currency.converter.test_data.json +2379 -0
- package/src/commercetools/helpers/currency.converter.ts +102 -0
- package/src/commercetools/helpers/taxrate.converter.test.ts +48 -0
- package/src/commercetools/helpers/taxrate.converter.ts +39 -0
- package/src/commercetools/index.ts +22 -0
- package/src/commercetools/services/ct-cart.service.test.ts +1616 -0
- package/src/commercetools/services/ct-cart.service.ts +316 -0
- package/src/commercetools/services/ct-custom-type.service.ts +62 -0
- package/src/commercetools/services/ct-order.service.test.ts +136 -0
- package/src/commercetools/services/ct-order.service.ts +47 -0
- package/src/commercetools/services/ct-payment-method.service.test.ts +878 -0
- package/src/commercetools/services/ct-payment-method.service.ts +223 -0
- package/src/commercetools/services/ct-payment.service.test.ts +1123 -0
- package/src/commercetools/services/ct-payment.service.ts +574 -0
- package/src/commercetools/types/api.type.ts +162 -0
- package/src/commercetools/types/cart.type.ts +83 -0
- package/src/commercetools/types/custom-type.type.ts +21 -0
- package/src/commercetools/types/order.type.ts +19 -0
- package/src/commercetools/types/payment-method.type.ts +130 -0
- package/src/commercetools/types/payment.type.ts +74 -0
- package/src/commercetools/types/predefined-custom-types.type.ts +240 -0
- package/src/commercetools/types.ts +7 -0
- package/src/errorx/errorx.ts +625 -0
- package/src/errorx/index.ts +1 -0
- package/src/errorx/types.ts +5 -0
- package/src/fetch/decorators/base.decorator.ts +13 -0
- package/src/fetch/decorators/monitoring.decorator.ts +41 -0
- package/src/fetch/fetch.test.ts +57 -0
- package/src/fetch/index.ts +3 -0
- package/src/fetch/types/fetch.type.ts +11 -0
- package/src/fetch/types.ts +1 -0
- package/src/index.ts +15 -0
- package/src/logger/commercetools-logger.ts +69 -0
- package/src/logger/index.ts +1 -0
- package/src/logger/types/logger.type.ts +6 -0
- package/src/logger/types.ts +1 -0
- package/src/mocks/auth.mock.ts +6 -0
- package/src/mocks/cart.mock.ts +239 -0
- package/src/mocks/ct-api-error.mock.ts +23 -0
- package/src/mocks/index.ts +4 -0
- package/src/mocks/payment.mock.ts +35 -0
- package/src/payment-processor.ts +317 -0
- package/src/security/authn/authns.ts +219 -0
- package/src/security/authn/bearer-utils.test.ts +35 -0
- package/src/security/authn/bearer-utils.ts +28 -0
- package/src/security/authn/jwt-authn-manager.ts +69 -0
- package/src/security/authn/oauth2-authn-manager.ts +105 -0
- package/src/security/authn/session-header-authn-manager.ts +58 -0
- package/src/security/authn/session-query-param-authn-manager.ts +53 -0
- package/src/security/authn/types/authn.type.ts +50 -0
- package/src/security/authz/authorization-manager.ts +39 -0
- package/src/security/authz/types/authz.type.ts +13 -0
- package/src/security/index.ts +13 -0
- package/src/security/services/authorization.service.ts +54 -0
- package/src/security/services/jwt.service.test.ts +27 -0
- package/src/security/services/jwt.service.ts +45 -0
- package/src/security/services/oauth2.service.ts +60 -0
- package/src/security/services/session.service.ts +141 -0
- package/src/security/services/types/authorization.type.ts +10 -0
- package/src/security/services/types/jwt.type.ts +3 -0
- package/src/security/services/types/oauth2.type.ts +15 -0
- package/src/security/services/types/session.type.ts +38 -0
- package/src/security/types.ts +6 -0
- package/tsconfig.json +28 -0
- package/tsconfig.prod.json +9 -0
- package/typedoc.json +6 -0
- package/vitest.config.ts +15 -0
- package/wiki/getting-started.md +13 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { Logger } from '@logger-types';
|
|
2
|
+
import {
|
|
3
|
+
HeaderBasedAuthentication,
|
|
4
|
+
Oauth2Authentication,
|
|
5
|
+
type Oauth2AuthenticationManager,
|
|
6
|
+
} from '@security';
|
|
7
|
+
import type {
|
|
8
|
+
ContextProvider,
|
|
9
|
+
RequestContextData,
|
|
10
|
+
} from '@/api/context/types/request-context.type.js';
|
|
11
|
+
import { Oauth2AuthenticationHook } from '@/api/hooks/oauth2-auth.hook.js';
|
|
12
|
+
|
|
13
|
+
describe('Oauth2AuthenticationHook', () => {
|
|
14
|
+
const authenticationManager = {
|
|
15
|
+
authenticate: vi.fn(),
|
|
16
|
+
};
|
|
17
|
+
const contextProvider = {
|
|
18
|
+
updateContextData: vi.fn(),
|
|
19
|
+
};
|
|
20
|
+
const logger = vi.fn() as unknown as Logger;
|
|
21
|
+
const authenticationHook = new Oauth2AuthenticationHook({
|
|
22
|
+
authenticationManager:
|
|
23
|
+
authenticationManager as unknown as Oauth2AuthenticationManager,
|
|
24
|
+
contextProvider:
|
|
25
|
+
contextProvider as unknown as ContextProvider<RequestContextData>,
|
|
26
|
+
logger,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
vi.clearAllMocks();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('authenticate should update the context data with authentication', async () => {
|
|
34
|
+
// Arrange
|
|
35
|
+
const authorizationHeader = 'Bearer <oauth2-token>';
|
|
36
|
+
const request = {
|
|
37
|
+
headers: {
|
|
38
|
+
authorization: authorizationHeader,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const oauth2Authentication = new Oauth2Authentication('<oauth2-token>', {
|
|
42
|
+
scope: 'manage_project:test-project customer_id:123',
|
|
43
|
+
clientId: 'test-client',
|
|
44
|
+
customerId: '123',
|
|
45
|
+
anonymousId: undefined,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Act
|
|
49
|
+
authenticationManager.authenticate.mockResolvedValue(oauth2Authentication);
|
|
50
|
+
const authenticateFn = authenticationHook.authenticate();
|
|
51
|
+
await authenticateFn(request);
|
|
52
|
+
|
|
53
|
+
// Assert
|
|
54
|
+
expect(contextProvider.updateContextData).toHaveBeenCalledWith({
|
|
55
|
+
authentication: oauth2Authentication,
|
|
56
|
+
});
|
|
57
|
+
expect(authenticationManager.authenticate).toHaveBeenCalledWith(
|
|
58
|
+
expect.any(HeaderBasedAuthentication),
|
|
59
|
+
);
|
|
60
|
+
const capturedArg = authenticationManager.authenticate.mock
|
|
61
|
+
.calls[0][0] as HeaderBasedAuthentication;
|
|
62
|
+
expect(capturedArg.getCredentials()).toBe(authorizationHeader);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('authenticate should handle requests without authorization header', async () => {
|
|
66
|
+
// Arrange
|
|
67
|
+
const request = {
|
|
68
|
+
headers: {},
|
|
69
|
+
};
|
|
70
|
+
const oauth2Authentication = new Oauth2Authentication('<oauth2-token>', {
|
|
71
|
+
scope: 'manage_project:test-project',
|
|
72
|
+
clientId: 'test-client',
|
|
73
|
+
customerId: undefined,
|
|
74
|
+
anonymousId: undefined,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Act
|
|
78
|
+
authenticationManager.authenticate.mockResolvedValue(oauth2Authentication);
|
|
79
|
+
const authenticateFn = authenticationHook.authenticate();
|
|
80
|
+
await authenticateFn(request);
|
|
81
|
+
|
|
82
|
+
// Assert
|
|
83
|
+
expect(contextProvider.updateContextData).toHaveBeenCalledWith({
|
|
84
|
+
authentication: oauth2Authentication,
|
|
85
|
+
});
|
|
86
|
+
expect(authenticationManager.authenticate).toHaveBeenCalledWith(
|
|
87
|
+
expect.any(HeaderBasedAuthentication),
|
|
88
|
+
);
|
|
89
|
+
const capturedArg = authenticationManager.authenticate.mock
|
|
90
|
+
.calls[0][0] as HeaderBasedAuthentication;
|
|
91
|
+
expect(capturedArg.getCredentials()).toBeUndefined();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('authenticate should handle authentication with anonymous user', async () => {
|
|
95
|
+
// Arrange
|
|
96
|
+
const authorizationHeader = 'Bearer <oauth2-token>';
|
|
97
|
+
const request = {
|
|
98
|
+
headers: {
|
|
99
|
+
authorization: authorizationHeader,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
const oauth2Authentication = new Oauth2Authentication('<oauth2-token>', {
|
|
103
|
+
scope: 'manage_project:test-project anonymous_id:456',
|
|
104
|
+
clientId: 'test-client',
|
|
105
|
+
customerId: undefined,
|
|
106
|
+
anonymousId: '456',
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Act
|
|
110
|
+
authenticationManager.authenticate.mockResolvedValue(oauth2Authentication);
|
|
111
|
+
const authenticateFn = authenticationHook.authenticate();
|
|
112
|
+
await authenticateFn(request);
|
|
113
|
+
|
|
114
|
+
// Assert
|
|
115
|
+
expect(contextProvider.updateContextData).toHaveBeenCalledWith({
|
|
116
|
+
authentication: oauth2Authentication,
|
|
117
|
+
});
|
|
118
|
+
expect(authenticationManager.authenticate).toHaveBeenCalledWith(
|
|
119
|
+
expect.any(HeaderBasedAuthentication),
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('authenticate should propagate authentication manager errors', async () => {
|
|
124
|
+
// Arrange
|
|
125
|
+
const authorizationHeader = 'Bearer invalid-token';
|
|
126
|
+
const request = {
|
|
127
|
+
headers: {
|
|
128
|
+
authorization: authorizationHeader,
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
const error = new Error('Invalid token');
|
|
132
|
+
|
|
133
|
+
// Act
|
|
134
|
+
authenticationManager.authenticate.mockRejectedValue(error);
|
|
135
|
+
const authenticateFn = authenticationHook.authenticate();
|
|
136
|
+
|
|
137
|
+
// Assert
|
|
138
|
+
await expect(authenticateFn(request)).rejects.toThrow('Invalid token');
|
|
139
|
+
expect(contextProvider.updateContextData).not.toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('authenticate should handle requests with query parameters', async () => {
|
|
143
|
+
// Arrange
|
|
144
|
+
const authorizationHeader = 'Bearer <oauth2-token>';
|
|
145
|
+
const request = {
|
|
146
|
+
headers: {
|
|
147
|
+
authorization: authorizationHeader,
|
|
148
|
+
},
|
|
149
|
+
query: {
|
|
150
|
+
someParam: 'someValue',
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
const oauth2Authentication = new Oauth2Authentication('<oauth2-token>', {
|
|
154
|
+
scope: 'manage_project:test-project',
|
|
155
|
+
clientId: 'test-client',
|
|
156
|
+
customerId: undefined,
|
|
157
|
+
anonymousId: undefined,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Act
|
|
161
|
+
authenticationManager.authenticate.mockResolvedValue(oauth2Authentication);
|
|
162
|
+
const authenticateFn = authenticationHook.authenticate();
|
|
163
|
+
await authenticateFn(request);
|
|
164
|
+
|
|
165
|
+
// Assert
|
|
166
|
+
expect(contextProvider.updateContextData).toHaveBeenCalledWith({
|
|
167
|
+
authentication: oauth2Authentication,
|
|
168
|
+
});
|
|
169
|
+
expect(authenticationManager.authenticate).toHaveBeenCalledWith(
|
|
170
|
+
expect.any(HeaderBasedAuthentication),
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
2
|
+
import type { Logger } from '@logger-types';
|
|
3
|
+
import {
|
|
4
|
+
HeaderBasedAuthentication,
|
|
5
|
+
type Oauth2AuthenticationManager,
|
|
6
|
+
} from '@security';
|
|
7
|
+
import type {
|
|
8
|
+
ContextProvider,
|
|
9
|
+
RequestContextData,
|
|
10
|
+
} from '@/api/context/types/request-context.type.js';
|
|
11
|
+
import type { AuthenticationHook } from '@/api/hooks/types/hook.type.js';
|
|
12
|
+
|
|
13
|
+
export class Oauth2AuthenticationHook implements AuthenticationHook {
|
|
14
|
+
private authenticationManager: Oauth2AuthenticationManager;
|
|
15
|
+
private contextProvider: ContextProvider<RequestContextData>;
|
|
16
|
+
private logger: Logger;
|
|
17
|
+
|
|
18
|
+
constructor(opts: {
|
|
19
|
+
authenticationManager: Oauth2AuthenticationManager;
|
|
20
|
+
contextProvider: ContextProvider<RequestContextData>;
|
|
21
|
+
logger: Logger;
|
|
22
|
+
}) {
|
|
23
|
+
this.authenticationManager = opts.authenticationManager;
|
|
24
|
+
this.contextProvider = opts.contextProvider;
|
|
25
|
+
this.logger = opts.logger;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
authenticate() {
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
return async (request: {
|
|
31
|
+
headers: IncomingHttpHeaders;
|
|
32
|
+
query?: unknown;
|
|
33
|
+
}) => {
|
|
34
|
+
const authorizationHeader = new HeaderBasedAuthentication(
|
|
35
|
+
request.headers.authorization as string,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const authn =
|
|
39
|
+
await this.authenticationManager.authenticate(authorizationHeader);
|
|
40
|
+
|
|
41
|
+
this.contextProvider.updateContextData({
|
|
42
|
+
authentication: authn,
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
2
|
+
import type { Logger } from '@logger-types';
|
|
3
|
+
import {
|
|
4
|
+
HeaderBasedAuthentication,
|
|
5
|
+
type SessionHeaderAuthenticationManager,
|
|
6
|
+
} from '@security';
|
|
7
|
+
import type {
|
|
8
|
+
ContextProvider,
|
|
9
|
+
RequestContextData,
|
|
10
|
+
} from '@/api/context/types/request-context.type.js';
|
|
11
|
+
import type { AuthenticationHook } from '@/api/hooks/types/hook.type.js';
|
|
12
|
+
|
|
13
|
+
export class SessionHeaderAuthenticationHook implements AuthenticationHook {
|
|
14
|
+
private authenticationManager: SessionHeaderAuthenticationManager;
|
|
15
|
+
private contextProvider: ContextProvider<RequestContextData>;
|
|
16
|
+
private logger: Logger;
|
|
17
|
+
|
|
18
|
+
constructor(opts: {
|
|
19
|
+
authenticationManager: SessionHeaderAuthenticationManager;
|
|
20
|
+
contextProvider: ContextProvider<RequestContextData>;
|
|
21
|
+
logger: Logger;
|
|
22
|
+
}) {
|
|
23
|
+
this.authenticationManager = opts.authenticationManager;
|
|
24
|
+
this.contextProvider = opts.contextProvider;
|
|
25
|
+
this.logger = opts.logger;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
authenticate() {
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
return async (request: {
|
|
31
|
+
headers: IncomingHttpHeaders;
|
|
32
|
+
query?: unknown;
|
|
33
|
+
}) => {
|
|
34
|
+
const sessionIdAuthn = new HeaderBasedAuthentication(
|
|
35
|
+
request.headers['x-session-id'] as string,
|
|
36
|
+
);
|
|
37
|
+
const authn =
|
|
38
|
+
await this.authenticationManager.authenticate(sessionIdAuthn);
|
|
39
|
+
const correlationId = authn.getPrincipal().correlationId;
|
|
40
|
+
|
|
41
|
+
this.contextProvider.updateContextData({
|
|
42
|
+
authentication: authn,
|
|
43
|
+
...(correlationId && { correlationId }),
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
2
|
+
import type { Logger } from '@logger-types';
|
|
3
|
+
import type {
|
|
4
|
+
ContextProvider,
|
|
5
|
+
RequestContextData,
|
|
6
|
+
} from '@/api/context/types/request-context.type.js';
|
|
7
|
+
import type { AuthenticationHook } from '@/api/hooks/types/hook.type.js';
|
|
8
|
+
import { QueryParamBasedAuthentication } from '@/security/authn/authns.js';
|
|
9
|
+
import type { SessionQueryParamAuthenticationManager } from '@/security/authn/session-query-param-authn-manager.js';
|
|
10
|
+
|
|
11
|
+
// TODO: This should not be here or at least being more generic
|
|
12
|
+
export class SessionQueryParamAuthenticationHook implements AuthenticationHook {
|
|
13
|
+
private authenticationManager: SessionQueryParamAuthenticationManager;
|
|
14
|
+
private contextProvider: ContextProvider<RequestContextData>;
|
|
15
|
+
private logger: Logger;
|
|
16
|
+
|
|
17
|
+
constructor(opts: {
|
|
18
|
+
authenticationManager: SessionQueryParamAuthenticationManager;
|
|
19
|
+
contextProvider: ContextProvider<RequestContextData>;
|
|
20
|
+
logger: Logger;
|
|
21
|
+
}) {
|
|
22
|
+
this.authenticationManager = opts.authenticationManager;
|
|
23
|
+
this.contextProvider = opts.contextProvider;
|
|
24
|
+
this.logger = opts.logger;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
authenticate() {
|
|
28
|
+
return async (request: {
|
|
29
|
+
headers: IncomingHttpHeaders;
|
|
30
|
+
query?: unknown;
|
|
31
|
+
}) => {
|
|
32
|
+
const query = request.query as { ctsid: string };
|
|
33
|
+
const sessionIdAuthn = new QueryParamBasedAuthentication(
|
|
34
|
+
query.ctsid as string,
|
|
35
|
+
);
|
|
36
|
+
const authn =
|
|
37
|
+
await this.authenticationManager.authenticate(sessionIdAuthn);
|
|
38
|
+
const correlationId = authn.getPrincipal().correlationId;
|
|
39
|
+
|
|
40
|
+
this.contextProvider.updateContextData({
|
|
41
|
+
authentication: authn,
|
|
42
|
+
...(correlationId && { correlationId }),
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
2
|
+
|
|
3
|
+
export interface AuthenticationHook {
|
|
4
|
+
authenticate(): (request: {
|
|
5
|
+
headers: IncomingHttpHeaders;
|
|
6
|
+
query?: unknown;
|
|
7
|
+
}) => Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AuthorizationHook {
|
|
11
|
+
authorize(...authorities: string[]): () => Promise<void>;
|
|
12
|
+
}
|
package/src/api/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from '@/api/context/request-context.helper.js';
|
|
2
|
+
export * from '@/api/context/request-context.provider.js';
|
|
3
|
+
export * from '@/api/handlers/payment-components.handler.js';
|
|
4
|
+
export * from '@/api/handlers/payment-intents.handler.js';
|
|
5
|
+
export * from '@/api/handlers/status.handler.js';
|
|
6
|
+
export * from '@/api/handlers/transaction.handler.js';
|
|
7
|
+
export * from '@/api/hooks/authorize.hook.js';
|
|
8
|
+
export * from '@/api/hooks/jwt-auth.hook.js';
|
|
9
|
+
export * from '@/api/hooks/oauth2-auth.hook.js';
|
|
10
|
+
export * from '@/api/hooks/session-header-auth.hook.js';
|
|
11
|
+
export * from '@/api/hooks/session-query-param-auth.hook.js';
|
package/src/api/types.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ClientResponse } from '@commercetools/platform-sdk';
|
|
2
|
+
import {
|
|
3
|
+
type CocoErrorType,
|
|
4
|
+
CommercetoolsAPIError,
|
|
5
|
+
} from '@/commercetools/errors/ct-api.error.js';
|
|
6
|
+
import type {
|
|
7
|
+
APIOpts,
|
|
8
|
+
CommercetoolsClient,
|
|
9
|
+
} from '@/commercetools/types/api.type.js';
|
|
10
|
+
|
|
11
|
+
export abstract class CommercetoolsBaseAPI {
|
|
12
|
+
protected client: CommercetoolsClient;
|
|
13
|
+
|
|
14
|
+
constructor(opts: APIOpts) {
|
|
15
|
+
this.client = opts.client;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
protected async executeCall<T>(cr: Promise<ClientResponse<T>>): Promise<T> {
|
|
19
|
+
try {
|
|
20
|
+
return (await cr).body;
|
|
21
|
+
} catch (e: unknown) {
|
|
22
|
+
if (e instanceof Object && 'statusCode' in e) {
|
|
23
|
+
throw new CommercetoolsAPIError(e as CocoErrorType);
|
|
24
|
+
}
|
|
25
|
+
throw e;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { RequestContextProvider } from '@api';
|
|
2
|
+
import { CommercetoolsLogger } from '@logger';
|
|
3
|
+
import { HttpResponse, http } from 'msw';
|
|
4
|
+
import { setupServer } from 'msw/node';
|
|
5
|
+
import { DefaultCommercetoolsAPI } from '@/commercetools/api/root-api.js';
|
|
6
|
+
import { authToken } from '@/mocks/auth.mock.js';
|
|
7
|
+
import { cartMock } from '@/mocks/cart.mock.js';
|
|
8
|
+
import { ctAPIErrorMock } from '@/mocks/ct-api-error.mock.js';
|
|
9
|
+
|
|
10
|
+
const mockServer = setupServer();
|
|
11
|
+
|
|
12
|
+
describe('commercetools cart API', () => {
|
|
13
|
+
beforeAll(() =>
|
|
14
|
+
mockServer.listen({
|
|
15
|
+
onUnhandledRequest: 'error',
|
|
16
|
+
}),
|
|
17
|
+
);
|
|
18
|
+
afterAll(() => mockServer.close());
|
|
19
|
+
const projectKey = 'test';
|
|
20
|
+
const contextProvider = new RequestContextProvider({
|
|
21
|
+
getContextFn: () => ({
|
|
22
|
+
correlationId: 'correlation-id',
|
|
23
|
+
requestId: 'request-id',
|
|
24
|
+
}),
|
|
25
|
+
updateContextFn: () => {},
|
|
26
|
+
});
|
|
27
|
+
const logger = new CommercetoolsLogger({ contextProvider, projectKey });
|
|
28
|
+
|
|
29
|
+
const { cart: cartAPI } = new DefaultCommercetoolsAPI({
|
|
30
|
+
apiUrl: 'http://api.test.com',
|
|
31
|
+
authUrl: 'http://auth.test.com',
|
|
32
|
+
clientId: 'clientId',
|
|
33
|
+
clientSecret: 'clientSecret',
|
|
34
|
+
projectKey,
|
|
35
|
+
contextProvider,
|
|
36
|
+
logger,
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
httpClient: (input: string | URL | Request, init?: RequestInit) => {
|
|
39
|
+
return fetch(input, init);
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('getCartById', () => {
|
|
44
|
+
test('should return a cart', async () => {
|
|
45
|
+
//given
|
|
46
|
+
const cartId = '12345';
|
|
47
|
+
mockServer.use(
|
|
48
|
+
mockGetAuthToken(200, authToken),
|
|
49
|
+
mockGetCartById(cartId, 200, cartMock),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
//when
|
|
53
|
+
const cart = await cartAPI.getCartById(cartId);
|
|
54
|
+
|
|
55
|
+
//then
|
|
56
|
+
expect(cart).toEqual(cartMock);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('should throw an error when cart is not found', async () => {
|
|
60
|
+
//given
|
|
61
|
+
const cartId = '12345';
|
|
62
|
+
const statusCode = 404;
|
|
63
|
+
const errorMessage = 'Cart not found';
|
|
64
|
+
const error = ctAPIErrorMock(statusCode, 'Not Found', errorMessage);
|
|
65
|
+
mockServer.use(
|
|
66
|
+
mockGetAuthToken(200, authToken),
|
|
67
|
+
mockGetCartById(cartId, statusCode, error),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
//when
|
|
71
|
+
const cartPromise = cartAPI.getCartById(cartId);
|
|
72
|
+
|
|
73
|
+
//then
|
|
74
|
+
await expect(cartPromise).rejects.toThrow(errorMessage);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const mockGetAuthToken = (respCode: number, data?: object) =>
|
|
80
|
+
http.post(
|
|
81
|
+
'http://auth.test.com/oauth/token',
|
|
82
|
+
() => HttpResponse.json(data, { status: respCode }),
|
|
83
|
+
{ once: true },
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const mockGetCartById = (id: string, respCode: number, data?: object) => {
|
|
87
|
+
return http.get(
|
|
88
|
+
`http://api.test.com/test/carts/${id}`,
|
|
89
|
+
() =>
|
|
90
|
+
HttpResponse.json(data, {
|
|
91
|
+
status: respCode,
|
|
92
|
+
}),
|
|
93
|
+
{ once: true },
|
|
94
|
+
);
|
|
95
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Cart, CartPagedQueryResponse } from '@commercetools/platform-sdk';
|
|
2
|
+
import { CommercetoolsBaseAPI } from '@/commercetools/api/base-api.js';
|
|
3
|
+
import type {
|
|
4
|
+
AddPayment,
|
|
5
|
+
APIOpts,
|
|
6
|
+
CartAPI,
|
|
7
|
+
} from '@/commercetools/types/api.type.js';
|
|
8
|
+
|
|
9
|
+
export class CommercetoolsCartAPI
|
|
10
|
+
extends CommercetoolsBaseAPI
|
|
11
|
+
implements CartAPI
|
|
12
|
+
{
|
|
13
|
+
constructor(opts: APIOpts) {
|
|
14
|
+
super(opts);
|
|
15
|
+
Object.setPrototypeOf(this, CommercetoolsCartAPI.prototype);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public async find(queryPredicate: string): Promise<CartPagedQueryResponse> {
|
|
19
|
+
return this.executeCall(
|
|
20
|
+
this.client
|
|
21
|
+
.carts()
|
|
22
|
+
.get({ queryArgs: { where: queryPredicate } })
|
|
23
|
+
.execute(),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async getCartById(id: string): Promise<Cart> {
|
|
28
|
+
return this.executeCall(
|
|
29
|
+
this.client.carts().withId({ ID: id }).get().execute(),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async addPayment(opts: AddPayment): Promise<Cart> {
|
|
34
|
+
return this.executeCall(
|
|
35
|
+
this.client
|
|
36
|
+
.carts()
|
|
37
|
+
.withId({ ID: opts.resource.id })
|
|
38
|
+
.post({
|
|
39
|
+
body: {
|
|
40
|
+
version: opts.resource.version,
|
|
41
|
+
actions: [
|
|
42
|
+
{
|
|
43
|
+
action: 'addPayment',
|
|
44
|
+
payment: {
|
|
45
|
+
id: opts.paymentId,
|
|
46
|
+
typeId: 'payment',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
.execute(),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Type, TypePagedQueryResponse } from '@commercetools/platform-sdk';
|
|
2
|
+
import type { APIOpts, CustomTypeAPI } from '../types/api.type.js';
|
|
3
|
+
import { CommercetoolsBaseAPI } from './base-api.js';
|
|
4
|
+
|
|
5
|
+
export class CommercetoolsCustomTypeAPI
|
|
6
|
+
extends CommercetoolsBaseAPI
|
|
7
|
+
implements CustomTypeAPI
|
|
8
|
+
{
|
|
9
|
+
constructor(opts: APIOpts) {
|
|
10
|
+
super(opts);
|
|
11
|
+
Object.setPrototypeOf(this, CommercetoolsCustomTypeAPI.prototype);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async getByKey(key: string): Promise<Type> {
|
|
15
|
+
return this.executeCall(
|
|
16
|
+
this.client.types().withKey({ key }).get().execute(),
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public async find(queryPredicate: string): Promise<TypePagedQueryResponse> {
|
|
21
|
+
return this.executeCall(
|
|
22
|
+
this.client
|
|
23
|
+
.types()
|
|
24
|
+
.get({ queryArgs: { where: queryPredicate } })
|
|
25
|
+
.execute(),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public async create(draft: Type): Promise<Type> {
|
|
30
|
+
return this.executeCall(
|
|
31
|
+
this.client
|
|
32
|
+
.types()
|
|
33
|
+
.post({
|
|
34
|
+
body: draft,
|
|
35
|
+
})
|
|
36
|
+
.execute(),
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OrderPagedQueryResponse } from '@commercetools/platform-sdk';
|
|
2
|
+
import { CommercetoolsBaseAPI } from '@/commercetools/api/base-api.js';
|
|
3
|
+
import type { APIOpts, OrderAPI } from '@/commercetools/types/api.type.js';
|
|
4
|
+
|
|
5
|
+
export class CommercetoolsOrderAPI
|
|
6
|
+
extends CommercetoolsBaseAPI
|
|
7
|
+
implements OrderAPI
|
|
8
|
+
{
|
|
9
|
+
constructor(opts: APIOpts) {
|
|
10
|
+
super(opts);
|
|
11
|
+
Object.setPrototypeOf(this, CommercetoolsOrderAPI.prototype);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async find(queryPredicate: string): Promise<OrderPagedQueryResponse> {
|
|
15
|
+
return this.executeCall(
|
|
16
|
+
this.client
|
|
17
|
+
.orders()
|
|
18
|
+
.get({ queryArgs: { where: queryPredicate } })
|
|
19
|
+
.execute(),
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Payment,
|
|
3
|
+
PaymentDraft,
|
|
4
|
+
PaymentPagedQueryResponse,
|
|
5
|
+
} from '@commercetools/platform-sdk';
|
|
6
|
+
import { CommercetoolsBaseAPI } from '@/commercetools/api/base-api.js';
|
|
7
|
+
import type {
|
|
8
|
+
APIOpts,
|
|
9
|
+
PaymentAPI,
|
|
10
|
+
UpdatePayment,
|
|
11
|
+
} from '@/commercetools/types/api.type.js';
|
|
12
|
+
|
|
13
|
+
export class CommercetoolsPaymentAPI
|
|
14
|
+
extends CommercetoolsBaseAPI
|
|
15
|
+
implements PaymentAPI
|
|
16
|
+
{
|
|
17
|
+
constructor(opts: APIOpts) {
|
|
18
|
+
super(opts);
|
|
19
|
+
Object.setPrototypeOf(this, CommercetoolsPaymentAPI.prototype);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async getPaymentById(id: string): Promise<Payment> {
|
|
23
|
+
return this.executeCall(
|
|
24
|
+
this.client.payments().withId({ ID: id }).get().execute(),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public async find(
|
|
29
|
+
queryPredicate: string,
|
|
30
|
+
): Promise<PaymentPagedQueryResponse> {
|
|
31
|
+
return this.executeCall(
|
|
32
|
+
this.client
|
|
33
|
+
.payments()
|
|
34
|
+
.get({ queryArgs: { where: queryPredicate } })
|
|
35
|
+
.execute(),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async createPayment(paymentDraft: PaymentDraft): Promise<Payment> {
|
|
40
|
+
return this.executeCall(
|
|
41
|
+
this.client
|
|
42
|
+
.payments()
|
|
43
|
+
.post({
|
|
44
|
+
body: paymentDraft,
|
|
45
|
+
})
|
|
46
|
+
.execute(),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public async updatePayment(opts: UpdatePayment): Promise<Payment> {
|
|
51
|
+
return this.executeCall(
|
|
52
|
+
this.client
|
|
53
|
+
.payments()
|
|
54
|
+
.withId({ ID: opts.resource.id })
|
|
55
|
+
.post({
|
|
56
|
+
body: {
|
|
57
|
+
version: opts.resource.version,
|
|
58
|
+
actions: opts.actions,
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
.execute(),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|