@reactionary/source 0.0.40 → 0.0.42
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/.env-template +8 -5
- package/README.md +41 -0
- package/core/src/client/client.ts +2 -0
- package/core/src/decorators/reactionary.decorator.ts +16 -0
- package/core/src/index.ts +5 -28
- package/core/src/providers/cart-payment.provider.ts +56 -0
- package/core/src/providers/cart.provider.ts +125 -1
- package/core/src/providers/index.ts +10 -0
- package/core/src/providers/price.provider.ts +1 -1
- package/core/src/providers/product.provider.ts +13 -1
- package/core/src/schemas/capabilities.schema.ts +1 -0
- package/core/src/schemas/models/cart.model.ts +16 -3
- package/core/src/schemas/models/identifiers.model.ts +43 -3
- package/core/src/schemas/models/identity.model.ts +22 -2
- package/core/src/schemas/models/index.ts +14 -0
- package/core/src/schemas/models/payment.model.ts +41 -0
- package/core/src/schemas/models/profile.model.ts +34 -0
- package/core/src/schemas/models/shipping-method.model.ts +14 -0
- package/core/src/schemas/mutations/cart-payment.mutation.ts +21 -0
- package/core/src/schemas/mutations/cart.mutation.ts +67 -8
- package/core/src/schemas/mutations/identity.mutation.ts +2 -1
- package/core/src/schemas/mutations/index.ts +9 -0
- package/core/src/schemas/queries/cart-payment.query.ts +12 -0
- package/core/src/schemas/queries/index.ts +1 -0
- package/examples/next/src/app/page.tsx +20 -15
- package/examples/node/package.json +3 -1
- package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +13 -5
- package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +11 -1
- package/examples/node/src/basic/basic-node-setup.spec.ts +7 -3
- package/examples/node/src/test-utils.ts +33 -0
- package/otel/src/trace-decorator.ts +6 -5
- package/package.json +4 -3
- package/providers/algolia/src/test/search.provider.spec.ts +28 -19
- package/providers/algolia/src/test/test-utils.ts +31 -0
- package/providers/commercetools/README.md +16 -0
- package/providers/commercetools/package.json +1 -1
- package/providers/commercetools/src/core/client.ts +31 -16
- package/providers/commercetools/src/core/initialize.ts +39 -6
- package/providers/commercetools/src/providers/cart-payment.provider.ts +192 -0
- package/providers/commercetools/src/providers/cart.provider.ts +414 -107
- package/providers/commercetools/src/providers/category.provider.ts +7 -4
- package/providers/commercetools/src/providers/identity.provider.ts +12 -4
- package/providers/commercetools/src/providers/inventory.provider.ts +10 -0
- package/providers/commercetools/src/providers/price.provider.ts +12 -5
- package/providers/commercetools/src/providers/product.provider.ts +23 -14
- package/providers/commercetools/src/providers/search.provider.ts +43 -15
- package/providers/commercetools/src/schema/capabilities.schema.ts +1 -0
- package/providers/commercetools/src/schema/commercetools.schema.ts +18 -0
- package/providers/commercetools/src/schema/configuration.schema.ts +2 -1
- package/providers/commercetools/src/test/cart-payment.provider.spec.ts +149 -0
- package/providers/commercetools/src/test/cart.provider.spec.ts +69 -10
- package/providers/commercetools/src/test/category.provider.spec.ts +1 -14
- package/providers/commercetools/src/test/product.provider.spec.ts +27 -0
- package/providers/commercetools/src/test/test-utils.ts +22 -7
- package/providers/fake/src/core/initialize.ts +6 -8
- package/providers/fake/src/providers/cart.provider.ts +47 -3
- package/providers/fake/src/providers/price.provider.ts +1 -1
- package/providers/fake/src/providers/product.provider.ts +3 -0
- package/providers/fake/src/test/test-utils.ts +7 -2
- package/trpc/src/integration.spec.ts +24 -23
- package/trpc/src/test-utils.ts +31 -0
- package/trpc/src/transparent-client.spec.ts +22 -26
- package/providers/algolia/src/test/product.provider.spec.ts +0 -18
|
@@ -40,7 +40,7 @@ export class CommercetoolsIdentityProvider<
|
|
|
40
40
|
|
|
41
41
|
if (current.success) {
|
|
42
42
|
current.data.meta = {
|
|
43
|
-
cache: { hit: false, key: session.identity.id || 'anonymous' },
|
|
43
|
+
cache: { hit: false, key: session.identity.id.userId || 'anonymous' },
|
|
44
44
|
placeholder: false
|
|
45
45
|
};
|
|
46
46
|
return current.data;
|
|
@@ -66,16 +66,24 @@ export class CommercetoolsIdentityProvider<
|
|
|
66
66
|
const base = this.newModel();
|
|
67
67
|
|
|
68
68
|
if (remote && remote.access_token) {
|
|
69
|
+
base.id = { userId: this.extractCustomerIdFromScopes(remote.scope) };
|
|
70
|
+
|
|
71
|
+
base.keyring = base.keyring.filter(x => x.service !== 'commercetools');
|
|
72
|
+
base.keyring.push({
|
|
73
|
+
service: 'commercetools',
|
|
74
|
+
token: remote.access_token,
|
|
75
|
+
issued: new Date(),
|
|
76
|
+
expiry: new Date(new Date().getTime() + 3600 * 1000),
|
|
77
|
+
});
|
|
69
78
|
base.issued = new Date();
|
|
70
79
|
base.expiry = new Date();
|
|
71
80
|
base.expiry.setSeconds(base.expiry.getSeconds() + remote.expires_in);
|
|
72
|
-
base.id = this.extractCustomerIdFromScopes(remote.scope);
|
|
73
81
|
base.token = remote.access_token;
|
|
74
82
|
base.type = 'Registered';
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
base.meta = {
|
|
78
|
-
cache: { hit: false, key: base.id || 'anonymous' },
|
|
86
|
+
cache: { hit: false, key: base.id.userId || 'anonymous' },
|
|
79
87
|
placeholder: false
|
|
80
88
|
};
|
|
81
89
|
|
|
@@ -111,4 +119,4 @@ export class CommercetoolsIdentityProvider<
|
|
|
111
119
|
|
|
112
120
|
return id || '';
|
|
113
121
|
}
|
|
114
|
-
}
|
|
122
|
+
}
|
|
@@ -25,6 +25,16 @@ export class CommercetoolsInventoryProvider<
|
|
|
25
25
|
this.config = config;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
protected getClient(session: Session) {
|
|
29
|
+
const token = session.identity.keyring.find(x => x.service === 'commercetools')?.token;
|
|
30
|
+
const client = new CommercetoolsClient(this.config).getClient(
|
|
31
|
+
token
|
|
32
|
+
);
|
|
33
|
+
return client.withProjectKey({ projectKey: this.config.projectKey }).inventory();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
28
38
|
public override async getBySKU(
|
|
29
39
|
payload: InventoryQuery,
|
|
30
40
|
session: Session
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Price, PriceProvider,
|
|
1
|
+
import { Price, PriceProvider, Cache, Currency, TieredPriceSchema, TieredPrice } from '@reactionary/core';
|
|
2
|
+
import type { PriceQueryBySku, Session } from '@reactionary/core';
|
|
2
3
|
import z from 'zod';
|
|
3
4
|
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
4
5
|
import { CommercetoolsClient } from '../core/client';
|
|
@@ -14,11 +15,17 @@ export class CommercetoolsPriceProvider<
|
|
|
14
15
|
this.config = config;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
protected getClient(session: Session) {
|
|
20
|
+
const token = session.identity.keyring.find(x => x.service === 'commercetools')?.token;
|
|
21
|
+
const client = new CommercetoolsClient(this.config).getClient(
|
|
22
|
+
token
|
|
23
|
+
);
|
|
24
|
+
return client.withProjectKey({ projectKey: this.config.projectKey }).standalonePrices();
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
|
|
28
|
+
|
|
22
29
|
public override async getBySKUs(payload: PriceQueryBySku[], session: Session): Promise<T[]> {
|
|
23
30
|
|
|
24
31
|
const client = this.getClient(session);
|
|
@@ -40,7 +47,7 @@ export class CommercetoolsPriceProvider<
|
|
|
40
47
|
if (matched && matched.length > 0) {
|
|
41
48
|
result.push(this.parseSingle(matched[0], session));
|
|
42
49
|
} else {
|
|
43
|
-
result.push(this.
|
|
50
|
+
result.push(this.createEmptyPriceResult(p.sku.key, session.languageContext.currencyCode ));
|
|
44
51
|
}
|
|
45
52
|
}
|
|
46
53
|
|
|
@@ -70,7 +77,7 @@ export class CommercetoolsPriceProvider<
|
|
|
70
77
|
if (matched && matched.length > 0) {
|
|
71
78
|
return this.parseSingle(matched[0], session);
|
|
72
79
|
}
|
|
73
|
-
return this.
|
|
80
|
+
return this.createEmptyPriceResult(payload.sku.key, session.languageContext.currencyCode );
|
|
74
81
|
}
|
|
75
82
|
|
|
76
83
|
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ProductProvider,
|
|
3
3
|
Product,
|
|
4
|
-
ProductQueryById,
|
|
5
|
-
ProductQueryBySlug,
|
|
6
|
-
Session,
|
|
7
4
|
Cache,
|
|
8
5
|
} from '@reactionary/core';
|
|
9
6
|
import { CommercetoolsClient } from '../core/client';
|
|
10
7
|
import { z } from 'zod';
|
|
11
8
|
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
12
9
|
import { ProductProjection } from '@commercetools/platform-sdk';
|
|
10
|
+
import { traced } from '@reactionary/otel';
|
|
11
|
+
import type { ProductQueryById, ProductQueryBySlug, Session } from '@reactionary/core';
|
|
13
12
|
|
|
14
13
|
export class CommercetoolsProductProvider<
|
|
15
14
|
T extends Product = Product
|
|
@@ -22,28 +21,38 @@ export class CommercetoolsProductProvider<
|
|
|
22
21
|
this.config = config;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
protected getClient(session: Session) {
|
|
25
|
+
const token = session.identity.keyring.find(x => x.service === 'commercetools')?.token;
|
|
26
|
+
const client = new CommercetoolsClient(this.config).getClient(
|
|
27
|
+
token
|
|
28
|
+
);
|
|
29
|
+
return client.withProjectKey({ projectKey: this.config.projectKey }).productProjections();
|
|
27
30
|
}
|
|
28
31
|
|
|
32
|
+
@traced()
|
|
29
33
|
public override async getById(
|
|
30
34
|
payload: ProductQueryById,
|
|
31
35
|
session: Session
|
|
32
36
|
): Promise<T> {
|
|
33
37
|
const client = this.getClient(session);
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
try {
|
|
40
|
+
const remote = await client
|
|
41
|
+
.withId({ ID: payload.id })
|
|
42
|
+
.get()
|
|
43
|
+
.execute();
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
return this.parseSingle(remote.body, session);
|
|
46
|
+
} catch(error) {
|
|
47
|
+
return this.createEmptyProduct(payload.id);
|
|
48
|
+
}
|
|
41
49
|
}
|
|
42
50
|
|
|
51
|
+
@traced()
|
|
43
52
|
public override async getBySlug(
|
|
44
53
|
payload: ProductQueryBySlug,
|
|
45
54
|
session: Session
|
|
46
|
-
): Promise<T> {
|
|
55
|
+
): Promise<T | null> {
|
|
47
56
|
const client = this.getClient(session);
|
|
48
57
|
|
|
49
58
|
const remote = await client
|
|
@@ -55,10 +64,9 @@ export class CommercetoolsProductProvider<
|
|
|
55
64
|
})
|
|
56
65
|
.execute();
|
|
57
66
|
|
|
58
|
-
if (remote.body.
|
|
59
|
-
|
|
67
|
+
if (remote.body.count === 0) {
|
|
68
|
+
return null;
|
|
60
69
|
}
|
|
61
|
-
|
|
62
70
|
return this.parseSingle(remote.body.results[0], session);
|
|
63
71
|
}
|
|
64
72
|
|
|
@@ -66,6 +74,7 @@ export class CommercetoolsProductProvider<
|
|
|
66
74
|
const data = dataIn as ProductProjection;
|
|
67
75
|
const base = this.newModel();
|
|
68
76
|
|
|
77
|
+
|
|
69
78
|
base.identifier = { key: data.id };
|
|
70
79
|
base.name = data.name[session.languageContext.locale];
|
|
71
80
|
base.slug = data.slug[session.languageContext.locale];
|
|
@@ -1,35 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
SearchQueryByTerm,
|
|
1
|
+
import { SearchProvider } from '@reactionary/core';
|
|
2
|
+
import type {
|
|
4
3
|
SearchResult,
|
|
5
4
|
SearchResultProduct,
|
|
6
|
-
Session,
|
|
7
5
|
Cache,
|
|
6
|
+
SearchQueryByTerm,
|
|
7
|
+
Session,
|
|
8
8
|
} from '@reactionary/core';
|
|
9
|
+
|
|
9
10
|
import { CommercetoolsClient } from '../core/client';
|
|
10
11
|
import z from 'zod';
|
|
11
12
|
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
13
|
+
import { traced } from '@reactionary/otel';
|
|
12
14
|
|
|
13
15
|
export class CommercetoolsSearchProvider<
|
|
14
16
|
T extends SearchResult = SearchResult
|
|
15
17
|
> extends SearchProvider<T> {
|
|
16
18
|
protected config: CommercetoolsConfiguration;
|
|
17
19
|
|
|
18
|
-
constructor(
|
|
20
|
+
constructor(
|
|
21
|
+
config: CommercetoolsConfiguration,
|
|
22
|
+
schema: z.ZodType<T>,
|
|
23
|
+
cache: Cache
|
|
24
|
+
) {
|
|
19
25
|
super(schema, cache);
|
|
20
26
|
|
|
21
27
|
this.config = config;
|
|
22
28
|
}
|
|
23
29
|
|
|
30
|
+
@traced()
|
|
31
|
+
protected getClient(session: Session) {
|
|
32
|
+
const token = session.identity.keyring.find(x => x.service === 'commercetools')?.token;
|
|
33
|
+
const client = new CommercetoolsClient(this.config).getClient(
|
|
34
|
+
token
|
|
35
|
+
);
|
|
36
|
+
return client.withProjectKey({ projectKey: this.config.projectKey }).productProjections();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
24
40
|
public override async queryByTerm(
|
|
25
41
|
payload: SearchQueryByTerm,
|
|
26
42
|
session: Session
|
|
27
43
|
): Promise<T> {
|
|
28
|
-
const client =
|
|
44
|
+
const client = this.getClient(session);
|
|
29
45
|
|
|
30
46
|
const remote = await client
|
|
31
|
-
.withProjectKey({ projectKey: this.config.projectKey })
|
|
32
|
-
.productProjections()
|
|
33
47
|
.search()
|
|
34
48
|
.get({
|
|
35
49
|
queryArgs: {
|
|
@@ -43,9 +57,23 @@ export class CommercetoolsSearchProvider<
|
|
|
43
57
|
return this.parseSearchResult(remote, payload, session);
|
|
44
58
|
}
|
|
45
59
|
|
|
46
|
-
protected parseSearchResult(
|
|
60
|
+
protected parseSearchResult(
|
|
61
|
+
remote: unknown,
|
|
62
|
+
payload: SearchQueryByTerm,
|
|
63
|
+
session: Session
|
|
64
|
+
): T {
|
|
47
65
|
const result = this.newModel();
|
|
48
|
-
const remoteData = remote as {
|
|
66
|
+
const remoteData = remote as {
|
|
67
|
+
body: {
|
|
68
|
+
results: Array<{
|
|
69
|
+
id: string;
|
|
70
|
+
name: Record<string, string>;
|
|
71
|
+
slug?: Record<string, string>;
|
|
72
|
+
masterVariant: { images?: Array<{ url?: string }> };
|
|
73
|
+
}>;
|
|
74
|
+
total?: number;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
49
77
|
|
|
50
78
|
result.identifier = payload.search;
|
|
51
79
|
|
|
@@ -54,20 +82,20 @@ export class CommercetoolsSearchProvider<
|
|
|
54
82
|
identifier: { key: p.id },
|
|
55
83
|
name: p.name[session.languageContext.locale] || p.id,
|
|
56
84
|
slug: p.slug?.[session.languageContext.locale] || p.id,
|
|
57
|
-
image: p.masterVariant.images?.[0]?.url || 'https://placehold.co/400'
|
|
85
|
+
image: p.masterVariant.images?.[0]?.url || 'https://placehold.co/400',
|
|
58
86
|
};
|
|
59
87
|
|
|
60
88
|
result.products.push(product);
|
|
61
89
|
}
|
|
62
90
|
|
|
63
|
-
result.pages = Math.ceil(
|
|
91
|
+
result.pages = Math.ceil(
|
|
92
|
+
(remoteData.body.total || 0) / payload.search.pageSize
|
|
93
|
+
);
|
|
64
94
|
result.meta = {
|
|
65
95
|
cache: { hit: false, key: payload.search.term },
|
|
66
|
-
placeholder: false
|
|
96
|
+
placeholder: false,
|
|
67
97
|
};
|
|
68
98
|
|
|
69
99
|
return this.assert(result);
|
|
70
100
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
101
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CartIdentifierSchema, OrderIdentifierSchema, PaymentInstructionIdentifierSchema } from "@reactionary/core";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
|
|
4
|
+
export const CommercetoolsCartIdentifierSchema = CartIdentifierSchema.extend({
|
|
5
|
+
version: z.number().default(0)
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const CommercetoolsCartPaymentInstructionIdentifierSchema = PaymentInstructionIdentifierSchema.extend({
|
|
9
|
+
version: z.number().default(0),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export const CommercetoolsOrderIdentifierSchema = OrderIdentifierSchema.extend({
|
|
13
|
+
version: z.number().default(0)
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type CommercetoolsCartIdentifier = z.infer<typeof CommercetoolsCartIdentifierSchema>;
|
|
17
|
+
export type CommercetoolsCartPaymentInstructionIdentifier = z.infer<typeof CommercetoolsCartPaymentInstructionIdentifierSchema>;
|
|
18
|
+
export type CommercetoolsOrderIdentifier = z.infer<typeof CommercetoolsOrderIdentifierSchema>;
|
|
@@ -5,7 +5,8 @@ export const CommercetoolsConfigurationSchema = z.looseObject({
|
|
|
5
5
|
authUrl: z.string(),
|
|
6
6
|
apiUrl: z.string(),
|
|
7
7
|
clientId: z.string(),
|
|
8
|
-
clientSecret: z.string()
|
|
8
|
+
clientSecret: z.string(),
|
|
9
|
+
scopes: z.array(z.string()).default(() => []),
|
|
9
10
|
});
|
|
10
11
|
|
|
11
12
|
export type CommercetoolsConfiguration = z.infer<typeof CommercetoolsConfigurationSchema>;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import {
|
|
3
|
+
Cart,
|
|
4
|
+
CartMutationAddPaymentMethodSchema,
|
|
5
|
+
CartPaymentInstructionSchema,
|
|
6
|
+
CartSchema,
|
|
7
|
+
IdentitySchema,
|
|
8
|
+
NoOpCache,
|
|
9
|
+
Session,
|
|
10
|
+
} from '@reactionary/core';
|
|
11
|
+
import {
|
|
12
|
+
createAnonymousTestSession,
|
|
13
|
+
getCommercetoolsTestConfiguration,
|
|
14
|
+
} from './test-utils';
|
|
15
|
+
import { CommercetoolsCartProvider } from '../providers/cart.provider';
|
|
16
|
+
import { CommercetoolsIdentityProvider } from '../providers/identity.provider';
|
|
17
|
+
import { CommercetoolsCartPaymentProvider } from '../providers/cart-payment.provider';
|
|
18
|
+
import { CartPaymentMutationAddPayment } from 'core/src/schemas/mutations/cart-payment.mutation';
|
|
19
|
+
|
|
20
|
+
const testData = {
|
|
21
|
+
skuWithoutTiers: 'SGB-01',
|
|
22
|
+
skuWithTiers: 'GMCT-01',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
describe('Commercetools Cart Payment Provider', () => {
|
|
26
|
+
let provider: CommercetoolsCartPaymentProvider;
|
|
27
|
+
let cartProvider: CommercetoolsCartProvider;
|
|
28
|
+
let identityProvider: CommercetoolsIdentityProvider;
|
|
29
|
+
let session: Session;
|
|
30
|
+
|
|
31
|
+
beforeAll(() => {
|
|
32
|
+
provider = new CommercetoolsCartPaymentProvider(
|
|
33
|
+
getCommercetoolsTestConfiguration(),
|
|
34
|
+
CartPaymentInstructionSchema,
|
|
35
|
+
new NoOpCache()
|
|
36
|
+
);
|
|
37
|
+
cartProvider = new CommercetoolsCartProvider(
|
|
38
|
+
getCommercetoolsTestConfiguration(),
|
|
39
|
+
CartSchema,
|
|
40
|
+
new NoOpCache()
|
|
41
|
+
);
|
|
42
|
+
identityProvider = new CommercetoolsIdentityProvider(
|
|
43
|
+
getCommercetoolsTestConfiguration(),
|
|
44
|
+
IdentitySchema,
|
|
45
|
+
new NoOpCache()
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
session = createAnonymousTestSession();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('anonymous sessions', () => {
|
|
54
|
+
let cart: Cart;
|
|
55
|
+
|
|
56
|
+
beforeEach(async () => {
|
|
57
|
+
cart = await cartProvider.add(
|
|
58
|
+
{
|
|
59
|
+
cart: { key: '', version: 0 },
|
|
60
|
+
sku: {
|
|
61
|
+
key: testData.skuWithoutTiers,
|
|
62
|
+
},
|
|
63
|
+
quantity: 1,
|
|
64
|
+
},
|
|
65
|
+
session
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('a new cart will return 0 payment instructions', async () => {
|
|
70
|
+
const payments = await provider.getByCartIdentifier(
|
|
71
|
+
{ cart: cart.identifier, status: undefined },
|
|
72
|
+
session
|
|
73
|
+
);
|
|
74
|
+
expect(payments.length).toBe(0);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('can initiate a new payment', async () => {
|
|
78
|
+
const payment = await provider.initiatePaymentForCart(
|
|
79
|
+
{
|
|
80
|
+
cart: cart.identifier,
|
|
81
|
+
paymentInstruction: {
|
|
82
|
+
paymentMethod: {
|
|
83
|
+
method: 'stripe',
|
|
84
|
+
name: 'Stripe',
|
|
85
|
+
paymentProcessor: 'stripe',
|
|
86
|
+
},
|
|
87
|
+
amount: {
|
|
88
|
+
value: cart.price.grandTotal.value,
|
|
89
|
+
currency: cart.price.grandTotal.currency,
|
|
90
|
+
},
|
|
91
|
+
protocolData: [{ key: 'test-key', value: 'test-value' }],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
session
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(payment.identifier.key).toBeDefined();
|
|
98
|
+
|
|
99
|
+
// verify that we can fetch it again.
|
|
100
|
+
const payments = await provider.getByCartIdentifier(
|
|
101
|
+
{ cart: cart.identifier, status: undefined },
|
|
102
|
+
session
|
|
103
|
+
);
|
|
104
|
+
expect(payments.length).toBe(1);
|
|
105
|
+
expect(payments[0].identifier.key).toBe(payment.identifier.key);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
it('can cancel an in-progress payment', async () => {
|
|
110
|
+
const payment = await provider.initiatePaymentForCart(
|
|
111
|
+
{
|
|
112
|
+
cart: cart.identifier,
|
|
113
|
+
paymentInstruction: {
|
|
114
|
+
paymentMethod: {
|
|
115
|
+
method: 'stripe',
|
|
116
|
+
name: 'Stripe',
|
|
117
|
+
paymentProcessor: 'stripe',
|
|
118
|
+
},
|
|
119
|
+
amount: {
|
|
120
|
+
value: cart.price.grandTotal.value,
|
|
121
|
+
currency: cart.price.grandTotal.currency,
|
|
122
|
+
},
|
|
123
|
+
protocolData: [{ key: 'test-key', value: 'test-value' }],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
session
|
|
127
|
+
);
|
|
128
|
+
expect(payment.identifier.key).toBeDefined();
|
|
129
|
+
|
|
130
|
+
const cancelledPayment = await provider.cancelPaymentInstruction(
|
|
131
|
+
{
|
|
132
|
+
cart: cart.identifier,
|
|
133
|
+
paymentInstruction: payment.identifier
|
|
134
|
+
},
|
|
135
|
+
session
|
|
136
|
+
);
|
|
137
|
+
expect(cancelledPayment.status).toBe('canceled');
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
// verify that it is gone
|
|
141
|
+
const payments = await provider.getByCartIdentifier(
|
|
142
|
+
{ cart: cart.identifier, status: undefined },
|
|
143
|
+
session
|
|
144
|
+
);
|
|
145
|
+
expect(payments.length).toBe(0);
|
|
146
|
+
expect(payments[0].identifier.key).toBe(payment.identifier.key);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -3,6 +3,13 @@ import { CartSchema, CategorySchema, IdentitySchema, NoOpCache, ProductSchema, S
|
|
|
3
3
|
import { createAnonymousTestSession, getCommercetoolsTestConfiguration } from './test-utils';
|
|
4
4
|
import { CommercetoolsCartProvider } from '../providers/cart.provider';
|
|
5
5
|
import { CommercetoolsIdentityProvider } from '../providers/identity.provider';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const testData = {
|
|
9
|
+
skuWithoutTiers: 'SGB-01',
|
|
10
|
+
skuWithTiers: 'GMCT-01'
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
describe('Commercetools Cart Provider', () => {
|
|
7
14
|
let provider: CommercetoolsCartProvider;
|
|
8
15
|
let identityProvider: CommercetoolsIdentityProvider;
|
|
@@ -32,15 +39,15 @@ describe('Commercetools Cart Provider', () => {
|
|
|
32
39
|
it('should be able to add an item to a cart', async () => {
|
|
33
40
|
const cart = await provider.add({
|
|
34
41
|
cart: { key: '' },
|
|
35
|
-
|
|
36
|
-
key:
|
|
42
|
+
sku: {
|
|
43
|
+
key: testData.skuWithoutTiers,
|
|
37
44
|
},
|
|
38
45
|
quantity: 1
|
|
39
46
|
}, session);
|
|
40
47
|
|
|
41
48
|
expect(cart.identifier.key).toBeDefined();
|
|
42
49
|
expect(cart.items.length).toBe(1);
|
|
43
|
-
expect(cart.items[0].
|
|
50
|
+
expect(cart.items[0].sku.key).toBe(testData.skuWithoutTiers);
|
|
44
51
|
expect(cart.items[0].quantity).toBe(1);
|
|
45
52
|
|
|
46
53
|
expect(cart.items[0].price.totalPrice.value).toBeGreaterThan(0);
|
|
@@ -56,13 +63,38 @@ describe('Commercetools Cart Provider', () => {
|
|
|
56
63
|
|
|
57
64
|
});
|
|
58
65
|
|
|
66
|
+
it('can add multiple different items to a cart', async () => {
|
|
67
|
+
|
|
68
|
+
const cart = await provider.add({
|
|
69
|
+
cart: { key: '' },
|
|
70
|
+
sku: {
|
|
71
|
+
key: testData.skuWithoutTiers,
|
|
72
|
+
},
|
|
73
|
+
quantity: 1
|
|
74
|
+
}, session);
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
const updatedCart = await provider.add({
|
|
78
|
+
cart: cart.identifier,
|
|
79
|
+
sku: {
|
|
80
|
+
key: testData.skuWithTiers,
|
|
81
|
+
},
|
|
82
|
+
quantity: 2
|
|
83
|
+
}, session);
|
|
84
|
+
|
|
85
|
+
expect(updatedCart.items.length).toBe(2);
|
|
86
|
+
expect(updatedCart.items[0].sku.key).toBe(testData.skuWithoutTiers);
|
|
87
|
+
expect(updatedCart.items[0].quantity).toBe(1);
|
|
88
|
+
expect(updatedCart.items[1].sku.key).toBe(testData.skuWithTiers);
|
|
89
|
+
expect(updatedCart.items[1].quantity).toBe(2);
|
|
90
|
+
});
|
|
59
91
|
|
|
60
92
|
it('should be able to change quantity of an item in a cart', async () => {
|
|
61
93
|
|
|
62
94
|
const cart = await provider.add({
|
|
63
95
|
cart: { key: '' },
|
|
64
|
-
|
|
65
|
-
key:
|
|
96
|
+
sku: {
|
|
97
|
+
key: testData.skuWithoutTiers,
|
|
66
98
|
},
|
|
67
99
|
quantity: 1
|
|
68
100
|
}, session);
|
|
@@ -73,9 +105,9 @@ describe('Commercetools Cart Provider', () => {
|
|
|
73
105
|
quantity: 3
|
|
74
106
|
}, session);
|
|
75
107
|
|
|
76
|
-
|
|
108
|
+
|
|
77
109
|
expect(updatedCart.items.length).toBe(1);
|
|
78
|
-
expect(updatedCart.items[0].
|
|
110
|
+
expect(updatedCart.items[0].sku.key).toBe(testData.skuWithoutTiers);
|
|
79
111
|
expect(updatedCart.items[0].quantity).toBe(3);
|
|
80
112
|
|
|
81
113
|
expect(updatedCart.items[0].price.totalPrice.value).toBe(cart.items[0].price.totalPrice.value * 3);
|
|
@@ -88,8 +120,8 @@ describe('Commercetools Cart Provider', () => {
|
|
|
88
120
|
|
|
89
121
|
const cart = await provider.add({
|
|
90
122
|
cart: { key: '' },
|
|
91
|
-
|
|
92
|
-
key:
|
|
123
|
+
sku: {
|
|
124
|
+
key: testData.skuWithoutTiers,
|
|
93
125
|
},
|
|
94
126
|
quantity: 1
|
|
95
127
|
}, session);
|
|
@@ -98,10 +130,37 @@ describe('Commercetools Cart Provider', () => {
|
|
|
98
130
|
cart: cart.identifier,
|
|
99
131
|
item: cart.items[0].identifier,
|
|
100
132
|
}, session);
|
|
101
|
-
|
|
133
|
+
|
|
102
134
|
expect(updatedCart.items.length).toBe(0);
|
|
103
135
|
});
|
|
104
136
|
|
|
137
|
+
it('should be able to delete a cart', async () => {
|
|
138
|
+
|
|
139
|
+
const cart = await provider.add({
|
|
140
|
+
cart: { key: '' },
|
|
141
|
+
sku: {
|
|
142
|
+
key: testData.skuWithoutTiers,
|
|
143
|
+
},
|
|
144
|
+
quantity: 1
|
|
145
|
+
}, session);
|
|
146
|
+
|
|
147
|
+
expect(cart.items.length).toBe(1);
|
|
148
|
+
expect(cart.identifier.key).toBeTruthy();
|
|
149
|
+
|
|
150
|
+
const deletedCart = await provider.deleteCart({
|
|
151
|
+
cart: cart.identifier,
|
|
152
|
+
}, session);
|
|
153
|
+
|
|
154
|
+
expect(deletedCart.items.length).toBe(0);
|
|
155
|
+
expect(deletedCart.identifier.key).toBe('');
|
|
156
|
+
|
|
157
|
+
const originalCart = await provider.getById({
|
|
158
|
+
cart: cart.identifier,
|
|
159
|
+
}, session);
|
|
160
|
+
|
|
161
|
+
expect(originalCart.items.length).toBe(0);
|
|
162
|
+
});
|
|
163
|
+
|
|
105
164
|
/**
|
|
106
165
|
it('should be able to create a cart for an anonymous user, then login and merge the cart', async () => {
|
|
107
166
|
});
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import 'dotenv/config'
|
|
2
|
-
|
|
3
|
-
import { diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api';
|
|
4
|
-
|
|
5
|
-
import { CategorySchema, NoOpCache, ProductSchema, Session } from '@reactionary/core';
|
|
2
|
+
import { CategorySchema, NoOpCache, Session } from '@reactionary/core';
|
|
6
3
|
import { CommercetoolsCategoryProvider } from '../providers/category.provider';
|
|
7
4
|
import { createAnonymousTestSession, getCommercetoolsTestConfiguration } from './test-utils';
|
|
8
|
-
import { getTracer, shutdownOtel } from '@reactionary/otel';
|
|
9
5
|
|
|
10
6
|
const testData = {
|
|
11
7
|
topCategories: [
|
|
@@ -168,13 +164,4 @@ describe('Commercetools Category Provider', () => {
|
|
|
168
164
|
expect(result.meta.placeholder).toBe(true);
|
|
169
165
|
|
|
170
166
|
});
|
|
171
|
-
|
|
172
|
-
it('traces execution of getById', async () => {
|
|
173
|
-
const tracer = getTracer();
|
|
174
|
-
const span = tracer.startSpan('test-span');
|
|
175
|
-
const result = await provider.getById({ id: { key: 'home-decor'}}, session);
|
|
176
|
-
span.end();
|
|
177
|
-
await shutdownOtel();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
167
|
});
|
|
@@ -28,8 +28,35 @@ describe('Commercetools Product Provider', () => {
|
|
|
28
28
|
it('should be able to get a product by id', async () => {
|
|
29
29
|
const result = await provider.getById( { id: testData.product.id }, session);
|
|
30
30
|
|
|
31
|
+
expect(result).toBeTruthy();
|
|
31
32
|
expect(result.identifier.key).toBe(testData.product.id);
|
|
33
|
+
expect(result.meta.placeholder).toBe(false);
|
|
32
34
|
expect(result.name).toBe(testData.product.name);
|
|
33
35
|
expect(result.image).toBe(testData.product.image);
|
|
34
36
|
});
|
|
37
|
+
|
|
38
|
+
it('should be able to get a product by slug', async () => {
|
|
39
|
+
const result = await provider.getBySlug( { slug: 'sunnai-glass-bowl' }, session);
|
|
40
|
+
|
|
41
|
+
expect(result).toBeTruthy();
|
|
42
|
+
if (result) {
|
|
43
|
+
expect(result.meta.placeholder).toBe(false);
|
|
44
|
+
expect(result.identifier.key).toBe(testData.product.id);
|
|
45
|
+
expect(result.name).toBe(testData.product.name);
|
|
46
|
+
expect(result.image).toBe(testData.product.image);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should return null for unknown slug', async () => {
|
|
51
|
+
const result = await provider.getBySlug( { slug: 'unknown-slug' }, session);
|
|
52
|
+
|
|
53
|
+
expect(result).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should return a placeholder product for unknown id', async () => {
|
|
57
|
+
const result = await provider.getById( { id: 'unknown-id' }, session);
|
|
58
|
+
|
|
59
|
+
expect(result).toBeTruthy();
|
|
60
|
+
expect(result.meta.placeholder).toBe(true);
|
|
61
|
+
});
|
|
35
62
|
});
|