@reactionary/source 0.0.27
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/.editorconfig +13 -0
- package/.github/workflows/pull-request.yml +37 -0
- package/.github/workflows/release.yml +49 -0
- package/.nvmrc +1 -0
- package/.prettierignore +6 -0
- package/.prettierrc +3 -0
- package/.verdaccio/config.yml +28 -0
- package/.vscode/extensions.json +9 -0
- package/README.md +39 -0
- package/core/README.md +11 -0
- package/core/eslint.config.mjs +23 -0
- package/core/package.json +8 -0
- package/core/project.json +33 -0
- package/core/src/cache/caching-strategy.ts +25 -0
- package/core/src/cache/redis-cache.ts +41 -0
- package/core/src/client/client.ts +39 -0
- package/core/src/index.ts +42 -0
- package/core/src/providers/analytics.provider.ts +10 -0
- package/core/src/providers/base.provider.ts +75 -0
- package/core/src/providers/cart.provider.ts +10 -0
- package/core/src/providers/identity.provider.ts +10 -0
- package/core/src/providers/inventory.provider.ts +11 -0
- package/core/src/providers/price.provider.ts +10 -0
- package/core/src/providers/product.provider.ts +11 -0
- package/core/src/providers/search.provider.ts +12 -0
- package/core/src/schemas/capabilities.schema.ts +13 -0
- package/core/src/schemas/models/analytics.model.ts +7 -0
- package/core/src/schemas/models/base.model.ts +19 -0
- package/core/src/schemas/models/cart.model.ts +17 -0
- package/core/src/schemas/models/currency.model.ts +187 -0
- package/core/src/schemas/models/identifiers.model.ts +45 -0
- package/core/src/schemas/models/identity.model.ts +15 -0
- package/core/src/schemas/models/inventory.model.ts +8 -0
- package/core/src/schemas/models/price.model.ts +17 -0
- package/core/src/schemas/models/product.model.ts +28 -0
- package/core/src/schemas/models/search.model.ts +35 -0
- package/core/src/schemas/mutations/analytics.mutation.ts +22 -0
- package/core/src/schemas/mutations/base.mutation.ts +7 -0
- package/core/src/schemas/mutations/cart.mutation.ts +30 -0
- package/core/src/schemas/mutations/identity.mutation.ts +18 -0
- package/core/src/schemas/mutations/inventory.mutation.ts +5 -0
- package/core/src/schemas/mutations/price.mutation.ts +5 -0
- package/core/src/schemas/mutations/product.mutation.ts +6 -0
- package/core/src/schemas/mutations/search.mutation.ts +5 -0
- package/core/src/schemas/queries/analytics.query.ts +5 -0
- package/core/src/schemas/queries/base.query.ts +7 -0
- package/core/src/schemas/queries/cart.query.ts +12 -0
- package/core/src/schemas/queries/identity.query.ts +10 -0
- package/core/src/schemas/queries/inventory.query.ts +9 -0
- package/core/src/schemas/queries/price.query.ts +12 -0
- package/core/src/schemas/queries/product.query.ts +18 -0
- package/core/src/schemas/queries/search.query.ts +12 -0
- package/core/src/schemas/session.schema.ts +9 -0
- package/core/tsconfig.json +23 -0
- package/core/tsconfig.lib.json +23 -0
- package/core/tsconfig.spec.json +28 -0
- package/eslint.config.mjs +46 -0
- package/examples/angular/e2e/example.spec.ts +9 -0
- package/examples/angular/eslint.config.mjs +41 -0
- package/examples/angular/playwright.config.ts +38 -0
- package/examples/angular/project.json +86 -0
- package/examples/angular/public/favicon.ico +0 -0
- package/examples/angular/src/app/app.component.html +6 -0
- package/examples/angular/src/app/app.component.scss +22 -0
- package/examples/angular/src/app/app.component.ts +14 -0
- package/examples/angular/src/app/app.config.ts +16 -0
- package/examples/angular/src/app/app.routes.ts +25 -0
- package/examples/angular/src/app/cart/cart.component.html +4 -0
- package/examples/angular/src/app/cart/cart.component.scss +14 -0
- package/examples/angular/src/app/cart/cart.component.ts +73 -0
- package/examples/angular/src/app/identity/identity.component.html +6 -0
- package/examples/angular/src/app/identity/identity.component.scss +18 -0
- package/examples/angular/src/app/identity/identity.component.ts +49 -0
- package/examples/angular/src/app/product/product.component.html +14 -0
- package/examples/angular/src/app/product/product.component.scss +11 -0
- package/examples/angular/src/app/product/product.component.ts +42 -0
- package/examples/angular/src/app/search/search.component.html +35 -0
- package/examples/angular/src/app/search/search.component.scss +129 -0
- package/examples/angular/src/app/search/search.component.ts +50 -0
- package/examples/angular/src/app/services/product.service.ts +35 -0
- package/examples/angular/src/app/services/search.service.ts +48 -0
- package/examples/angular/src/app/services/trpc.client.ts +27 -0
- package/examples/angular/src/index.html +13 -0
- package/examples/angular/src/main.ts +7 -0
- package/examples/angular/src/styles.scss +17 -0
- package/examples/angular/src/test-setup.ts +6 -0
- package/examples/angular/tsconfig.app.json +10 -0
- package/examples/angular/tsconfig.editor.json +6 -0
- package/examples/angular/tsconfig.json +32 -0
- package/examples/node/README.md +11 -0
- package/examples/node/eslint.config.mjs +22 -0
- package/examples/node/jest.config.ts +10 -0
- package/examples/node/package.json +10 -0
- package/examples/node/project.json +20 -0
- package/examples/node/src/index.ts +2 -0
- package/examples/node/src/initialize-algolia.spec.ts +29 -0
- package/examples/node/src/initialize-commercetools.spec.ts +31 -0
- package/examples/node/src/initialize-extended-providers.spec.ts +38 -0
- package/examples/node/src/initialize-mixed-providers.spec.ts +36 -0
- package/examples/node/src/providers/custom-algolia-product.provider.ts +18 -0
- package/examples/node/src/schemas/custom-product.schema.ts +8 -0
- package/examples/node/tsconfig.json +23 -0
- package/examples/node/tsconfig.lib.json +10 -0
- package/examples/node/tsconfig.spec.json +15 -0
- package/examples/trpc-node/eslint.config.mjs +3 -0
- package/examples/trpc-node/project.json +61 -0
- package/examples/trpc-node/src/assets/.gitkeep +0 -0
- package/examples/trpc-node/src/main.ts +55 -0
- package/examples/trpc-node/src/router-instance.ts +52 -0
- package/examples/trpc-node/tsconfig.app.json +9 -0
- package/examples/trpc-node/tsconfig.json +13 -0
- package/examples/vue/eslint.config.mjs +24 -0
- package/examples/vue/index.html +13 -0
- package/examples/vue/project.json +8 -0
- package/examples/vue/src/app/App.vue +275 -0
- package/examples/vue/src/main.ts +6 -0
- package/examples/vue/src/styles.scss +9 -0
- package/examples/vue/src/vue-shims.d.ts +5 -0
- package/examples/vue/tsconfig.app.json +14 -0
- package/examples/vue/tsconfig.json +20 -0
- package/examples/vue/vite.config.ts +31 -0
- package/jest.config.ts +6 -0
- package/jest.preset.js +3 -0
- package/migrations.json +11 -0
- package/nx.json +130 -0
- package/package.json +118 -0
- package/project.json +14 -0
- package/providers/algolia/README.md +11 -0
- package/providers/algolia/eslint.config.mjs +22 -0
- package/providers/algolia/jest.config.ts +10 -0
- package/providers/algolia/package.json +9 -0
- package/providers/algolia/project.json +33 -0
- package/providers/algolia/src/core/initialize.ts +20 -0
- package/providers/algolia/src/index.ts +7 -0
- package/providers/algolia/src/providers/product.provider.ts +25 -0
- package/providers/algolia/src/providers/search.provider.ts +125 -0
- package/providers/algolia/src/schema/capabilities.schema.ts +10 -0
- package/providers/algolia/src/schema/configuration.schema.ts +9 -0
- package/providers/algolia/src/schema/search.schema.ts +14 -0
- package/providers/algolia/src/test/product.provider.spec.ts +18 -0
- package/providers/algolia/src/test/search.provider.spec.ts +82 -0
- package/providers/algolia/tsconfig.json +23 -0
- package/providers/algolia/tsconfig.lib.json +10 -0
- package/providers/algolia/tsconfig.spec.json +15 -0
- package/providers/commercetools/README.md +11 -0
- package/providers/commercetools/eslint.config.mjs +22 -0
- package/providers/commercetools/jest.config.ts +10 -0
- package/providers/commercetools/package.json +10 -0
- package/providers/commercetools/project.json +33 -0
- package/providers/commercetools/src/core/client.ts +152 -0
- package/providers/commercetools/src/core/initialize.ts +40 -0
- package/providers/commercetools/src/index.ts +12 -0
- package/providers/commercetools/src/providers/cart.provider.ts +223 -0
- package/providers/commercetools/src/providers/identity.provider.ts +130 -0
- package/providers/commercetools/src/providers/inventory.provider.ts +82 -0
- package/providers/commercetools/src/providers/price.provider.ts +66 -0
- package/providers/commercetools/src/providers/product.provider.ts +90 -0
- package/providers/commercetools/src/providers/search.provider.ts +86 -0
- package/providers/commercetools/src/schema/capabilities.schema.ts +13 -0
- package/providers/commercetools/src/schema/configuration.schema.ts +11 -0
- package/providers/commercetools/src/test/product.provider.spec.ts +20 -0
- package/providers/commercetools/src/test/search.provider.spec.ts +18 -0
- package/providers/commercetools/tsconfig.json +23 -0
- package/providers/commercetools/tsconfig.lib.json +10 -0
- package/providers/commercetools/tsconfig.spec.json +15 -0
- package/providers/fake/README.md +7 -0
- package/providers/fake/eslint.config.mjs +22 -0
- package/providers/fake/package.json +9 -0
- package/providers/fake/project.json +33 -0
- package/providers/fake/src/core/initialize.ts +24 -0
- package/providers/fake/src/index.ts +8 -0
- package/providers/fake/src/providers/identity.provider.ts +91 -0
- package/providers/fake/src/providers/product.provider.ts +73 -0
- package/providers/fake/src/providers/search.provider.ts +142 -0
- package/providers/fake/src/schema/capabilities.schema.ts +10 -0
- package/providers/fake/src/schema/configuration.schema.ts +15 -0
- package/providers/fake/src/utilities/jitter.ts +14 -0
- package/providers/fake/tsconfig.json +20 -0
- package/providers/fake/tsconfig.lib.json +9 -0
- package/providers/posthog/README.md +7 -0
- package/providers/posthog/eslint.config.mjs +22 -0
- package/providers/posthog/package.json +11 -0
- package/providers/posthog/project.json +33 -0
- package/providers/posthog/src/core/initialize.ts +9 -0
- package/providers/posthog/src/index.ts +4 -0
- package/providers/posthog/src/schema/capabilities.schema.ts +8 -0
- package/providers/posthog/src/schema/configuration.schema.ts +8 -0
- package/providers/posthog/tsconfig.json +20 -0
- package/providers/posthog/tsconfig.lib.json +9 -0
- package/trpc/README.md +7 -0
- package/trpc/eslint.config.mjs +19 -0
- package/trpc/package.json +13 -0
- package/trpc/project.json +31 -0
- package/trpc/src/index.ts +64 -0
- package/trpc/tsconfig.json +13 -0
- package/trpc/tsconfig.lib.json +9 -0
- package/tsconfig.base.json +30 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Identity,
|
|
3
|
+
IdentityMutation,
|
|
4
|
+
IdentityMutationLogin,
|
|
5
|
+
IdentityProvider,
|
|
6
|
+
IdentityQuery,
|
|
7
|
+
Session,
|
|
8
|
+
} from '@reactionary/core';
|
|
9
|
+
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
10
|
+
import z from 'zod';
|
|
11
|
+
import { CommercetoolsClient } from '../core/client';
|
|
12
|
+
|
|
13
|
+
export class CommercetoolsIdentityProvider<
|
|
14
|
+
T extends Identity = Identity,
|
|
15
|
+
Q extends IdentityQuery = IdentityQuery,
|
|
16
|
+
M extends IdentityMutation = IdentityMutation
|
|
17
|
+
> extends IdentityProvider<T, Q, M> {
|
|
18
|
+
protected config: CommercetoolsConfiguration;
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
config: CommercetoolsConfiguration,
|
|
22
|
+
schema: z.ZodType<T>,
|
|
23
|
+
querySchema: z.ZodType<Q, Q>,
|
|
24
|
+
mutationSchema: z.ZodType<M, M>
|
|
25
|
+
) {
|
|
26
|
+
super(schema, querySchema, mutationSchema);
|
|
27
|
+
|
|
28
|
+
this.config = config;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
|
|
32
|
+
const results = [];
|
|
33
|
+
|
|
34
|
+
for (const query of queries) {
|
|
35
|
+
const result = await this.get(session);
|
|
36
|
+
|
|
37
|
+
results.push(result);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return results;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected override async process(
|
|
44
|
+
mutations: M[],
|
|
45
|
+
session: Session
|
|
46
|
+
): Promise<T> {
|
|
47
|
+
let result = this.newModel();
|
|
48
|
+
|
|
49
|
+
for (const mutation of mutations) {
|
|
50
|
+
switch (mutation.mutation) {
|
|
51
|
+
case 'login':
|
|
52
|
+
result = await this.login(mutation, session);
|
|
53
|
+
break;
|
|
54
|
+
case 'logout':
|
|
55
|
+
result = await this.logout(session);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected async get(session: Session): Promise<T> {
|
|
64
|
+
const client = new CommercetoolsClient(this.config);
|
|
65
|
+
const base = this.newModel();
|
|
66
|
+
|
|
67
|
+
if (session.identity.token) {
|
|
68
|
+
const remote = await client.introspect(session.identity.token);
|
|
69
|
+
|
|
70
|
+
if (remote.active) {
|
|
71
|
+
const current = this.schema.safeParse(session.identity);
|
|
72
|
+
|
|
73
|
+
if (current.success) {
|
|
74
|
+
return current.data;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
session.identity = base;
|
|
80
|
+
|
|
81
|
+
return base;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
protected async login(
|
|
85
|
+
payload: IdentityMutationLogin,
|
|
86
|
+
session: Session
|
|
87
|
+
): Promise<T> {
|
|
88
|
+
const client = new CommercetoolsClient(this.config);
|
|
89
|
+
const remote = await client.login(payload.username, payload.password);
|
|
90
|
+
const base = this.newModel();
|
|
91
|
+
|
|
92
|
+
if (remote && remote.access_token) {
|
|
93
|
+
base.issued = new Date();
|
|
94
|
+
base.expiry = new Date();
|
|
95
|
+
base.expiry.setSeconds(base.expiry.getSeconds() + remote.expires_in);
|
|
96
|
+
base.id = this.extractCustomerIdFromScopes(remote.scope);
|
|
97
|
+
base.token = remote.access_token;
|
|
98
|
+
base.type = 'Registered';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// TODO: error handling
|
|
102
|
+
|
|
103
|
+
session.identity = base;
|
|
104
|
+
|
|
105
|
+
return base;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
protected async logout(session: Session): Promise<T> {
|
|
109
|
+
const client = new CommercetoolsClient(this.config);
|
|
110
|
+
const base = this.newModel();
|
|
111
|
+
|
|
112
|
+
if (session.identity.token) {
|
|
113
|
+
const remote = await client.logout(session.identity.token);
|
|
114
|
+
|
|
115
|
+
// TODO: error handling
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
session.identity = base;
|
|
119
|
+
|
|
120
|
+
return base;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
protected extractCustomerIdFromScopes(scopes: string) {
|
|
124
|
+
const scopeList = scopes.split(' ');
|
|
125
|
+
const customerScope = scopeList.find((x) => x.startsWith('customer_id'));
|
|
126
|
+
const id = customerScope?.split(':')[1];
|
|
127
|
+
|
|
128
|
+
return id || '';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseCachingStrategy,
|
|
3
|
+
Inventory,
|
|
4
|
+
InventoryProvider,
|
|
5
|
+
InventoryQuery,
|
|
6
|
+
RedisCache,
|
|
7
|
+
Session,
|
|
8
|
+
} from '@reactionary/core';
|
|
9
|
+
import z from 'zod';
|
|
10
|
+
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
11
|
+
import { CommercetoolsClient } from '../core/client';
|
|
12
|
+
import { InventoryMutation } from 'core/src/schemas/mutations/inventory.mutation';
|
|
13
|
+
|
|
14
|
+
export class CommercetoolsInventoryProvider<
|
|
15
|
+
T extends Inventory = Inventory,
|
|
16
|
+
Q extends InventoryQuery = InventoryQuery,
|
|
17
|
+
M extends InventoryMutation = InventoryMutation
|
|
18
|
+
> extends InventoryProvider<T, Q, M> {
|
|
19
|
+
protected config: CommercetoolsConfiguration;
|
|
20
|
+
protected cache = new RedisCache(new BaseCachingStrategy());
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
config: CommercetoolsConfiguration,
|
|
24
|
+
schema: z.ZodType<T>,
|
|
25
|
+
querySchema: z.ZodType<Q, Q>,
|
|
26
|
+
mutationSchema: z.ZodType<M, M>
|
|
27
|
+
) {
|
|
28
|
+
super(schema, querySchema, mutationSchema);
|
|
29
|
+
|
|
30
|
+
this.config = config;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
|
|
34
|
+
const results = [];
|
|
35
|
+
|
|
36
|
+
for (const query of queries) {
|
|
37
|
+
let result = await this.cache.get(query, session, this.schema);
|
|
38
|
+
console.log('from cache: ', result);
|
|
39
|
+
|
|
40
|
+
if (!result) {
|
|
41
|
+
result = await this.get(query, session);
|
|
42
|
+
|
|
43
|
+
this.cache.put(query, session, result);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
results.push(result);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return results;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected override process(mutations: M[], session: Session): Promise<T> {
|
|
53
|
+
throw new Error('Method not implemented.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected async get(query: Q, session: Session): Promise<T> {
|
|
57
|
+
const client = new CommercetoolsClient(this.config).getClient(
|
|
58
|
+
session.identity?.token
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const remote = await client
|
|
62
|
+
.withProjectKey({ projectKey: this.config.projectKey })
|
|
63
|
+
.inventory()
|
|
64
|
+
.get({
|
|
65
|
+
queryArgs: {
|
|
66
|
+
where: 'sku=:sku',
|
|
67
|
+
'var.sku': query.sku,
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
.execute();
|
|
71
|
+
|
|
72
|
+
const base = this.newModel();
|
|
73
|
+
|
|
74
|
+
if (remote.body.results.length > 0) {
|
|
75
|
+
const inv = remote.body.results[0];
|
|
76
|
+
|
|
77
|
+
base.quantity = inv.availableQuantity;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return base;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { BaseCachingStrategy, BaseMutation, Price, PriceProvider, PriceQuery, RedisCache, Session } from '@reactionary/core';
|
|
2
|
+
import z from 'zod';
|
|
3
|
+
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
4
|
+
import { CommercetoolsClient } from '../core/client';
|
|
5
|
+
import { PriceMutation } from 'core/src/schemas/mutations/price.mutation';
|
|
6
|
+
|
|
7
|
+
export class CommercetoolsPriceProvider<
|
|
8
|
+
T extends Price = Price,
|
|
9
|
+
Q extends PriceQuery = PriceQuery,
|
|
10
|
+
M extends PriceMutation = PriceMutation
|
|
11
|
+
> extends PriceProvider<T, Q, M> {
|
|
12
|
+
protected config: CommercetoolsConfiguration;
|
|
13
|
+
|
|
14
|
+
constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>) {
|
|
15
|
+
super(schema, querySchema, mutationSchema);
|
|
16
|
+
|
|
17
|
+
this.config = config;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
|
|
21
|
+
const client = new CommercetoolsClient(this.config).getClient(
|
|
22
|
+
session.identity?.token
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const queryArgs = {
|
|
26
|
+
where: 'sku in :skus',
|
|
27
|
+
'var.skus': queries.map(x => x.sku.key),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const remote = await client
|
|
31
|
+
.withProjectKey({ projectKey: this.config.projectKey })
|
|
32
|
+
.standalonePrices()
|
|
33
|
+
.get({
|
|
34
|
+
queryArgs,
|
|
35
|
+
})
|
|
36
|
+
.execute();
|
|
37
|
+
|
|
38
|
+
const results = new Array<T>();
|
|
39
|
+
|
|
40
|
+
for (const query of queries) {
|
|
41
|
+
const base = this.newModel();
|
|
42
|
+
const matched = remote.body.results.find(x => x.sku === query.sku.key);
|
|
43
|
+
|
|
44
|
+
if (matched) {
|
|
45
|
+
base.value = {
|
|
46
|
+
cents: matched.value.centAmount,
|
|
47
|
+
currency: 'USD',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
base.identifier = {
|
|
52
|
+
sku: {
|
|
53
|
+
key: query.sku.key
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
results.push(base);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return results;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected override process(mutation: BaseMutation[], session: Session): Promise<T> {
|
|
64
|
+
throw new Error('Method not implemented.');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProductProvider,
|
|
3
|
+
ProductQuery,
|
|
4
|
+
Product,
|
|
5
|
+
Session,
|
|
6
|
+
BaseMutation,
|
|
7
|
+
ProductMutation,
|
|
8
|
+
} from '@reactionary/core';
|
|
9
|
+
import { CommercetoolsClient } from '../core/client';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
12
|
+
import { ProductProjection } from '@commercetools/platform-sdk';
|
|
13
|
+
|
|
14
|
+
export class CommercetoolsProductProvider<
|
|
15
|
+
T extends Product = Product,
|
|
16
|
+
Q extends ProductQuery = ProductQuery,
|
|
17
|
+
M extends ProductMutation = ProductMutation
|
|
18
|
+
> extends ProductProvider<T, Q, M> {
|
|
19
|
+
protected config: CommercetoolsConfiguration;
|
|
20
|
+
|
|
21
|
+
constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>) {
|
|
22
|
+
super(schema, querySchema, mutationSchema);
|
|
23
|
+
|
|
24
|
+
this.config = config;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected override async fetch(queries: ProductQuery[], session: Session): Promise<T[]> {
|
|
28
|
+
const ids = queries.filter((x) => x.query === 'id').map((x) => x.id);
|
|
29
|
+
const slugs = queries.filter((x) => x.query === 'slug').map((x) => x.slug);
|
|
30
|
+
|
|
31
|
+
const client = new CommercetoolsClient(this.config).createAnonymousClient();
|
|
32
|
+
|
|
33
|
+
const remote = await client
|
|
34
|
+
.withProjectKey({ projectKey: this.config.projectKey })
|
|
35
|
+
.productProjections()
|
|
36
|
+
.get({
|
|
37
|
+
queryArgs: {
|
|
38
|
+
where: 'slug(en-US in :slugs)',
|
|
39
|
+
'var.slugs': slugs
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
.execute();
|
|
43
|
+
|
|
44
|
+
console.log('remote: ', remote);
|
|
45
|
+
|
|
46
|
+
const results = new Array<T>;
|
|
47
|
+
|
|
48
|
+
for (const r of remote.body.results) {
|
|
49
|
+
const result = this.parse(r);
|
|
50
|
+
|
|
51
|
+
results.push(result);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return results;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
protected override process(
|
|
58
|
+
mutation: BaseMutation[],
|
|
59
|
+
session: Session
|
|
60
|
+
): Promise<T> {
|
|
61
|
+
throw new Error('Method not implemented.');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected parse(data: ProductProjection): T {
|
|
65
|
+
const base = this.newModel();
|
|
66
|
+
|
|
67
|
+
base.identifier.key = data.id;
|
|
68
|
+
base.name = data.name['en-US'];
|
|
69
|
+
base.slug = data.slug['en-US'];
|
|
70
|
+
|
|
71
|
+
if (data.description) {
|
|
72
|
+
base.description = data.description['en-US'];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (data.masterVariant.images) {
|
|
76
|
+
base.image = data.masterVariant.images[0].url;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const variants = [data.masterVariant, ...data.variants];
|
|
80
|
+
for (const variant of variants) {
|
|
81
|
+
base.skus.push({
|
|
82
|
+
identifier: {
|
|
83
|
+
key: variant.sku || '',
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return base;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SearchIdentifier,
|
|
3
|
+
SearchMutation,
|
|
4
|
+
SearchProvider,
|
|
5
|
+
SearchQuery,
|
|
6
|
+
SearchResult,
|
|
7
|
+
SearchResultProductSchema,
|
|
8
|
+
Session,
|
|
9
|
+
} from '@reactionary/core';
|
|
10
|
+
import { CommercetoolsClient } from '../core/client';
|
|
11
|
+
import z from 'zod';
|
|
12
|
+
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
13
|
+
|
|
14
|
+
export class CommercetoolsSearchProvider<
|
|
15
|
+
T extends SearchResult = SearchResult,
|
|
16
|
+
Q extends SearchQuery = SearchQuery,
|
|
17
|
+
M extends SearchMutation = SearchMutation
|
|
18
|
+
> extends SearchProvider<T, Q, M> {
|
|
19
|
+
protected config: CommercetoolsConfiguration;
|
|
20
|
+
|
|
21
|
+
constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>) {
|
|
22
|
+
super(schema, querySchema, mutationSchema);
|
|
23
|
+
|
|
24
|
+
this.config = config;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
|
|
28
|
+
const results = [];
|
|
29
|
+
|
|
30
|
+
for (const query of queries) {
|
|
31
|
+
const result = await this.get(query.search);
|
|
32
|
+
|
|
33
|
+
results.push(result);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return results;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
protected override process(mutations: M[], session: Session): Promise<T> {
|
|
40
|
+
throw new Error('Method not implemented.');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public async get(identifier: SearchIdentifier): Promise<T> {
|
|
44
|
+
const client = new CommercetoolsClient(this.config).createAnonymousClient();
|
|
45
|
+
|
|
46
|
+
const remote = await client
|
|
47
|
+
.withProjectKey({ projectKey: this.config.projectKey })
|
|
48
|
+
.productProjections()
|
|
49
|
+
.search()
|
|
50
|
+
.get({
|
|
51
|
+
queryArgs: {
|
|
52
|
+
limit: identifier.pageSize,
|
|
53
|
+
offset: identifier.pageSize * identifier.page,
|
|
54
|
+
['text.en-US']: identifier.term,
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
.execute();
|
|
58
|
+
|
|
59
|
+
const parsed = this.parse(remote, identifier);
|
|
60
|
+
|
|
61
|
+
return parsed;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public parse(remote: any, query: SearchIdentifier): T {
|
|
65
|
+
const result = super.newModel();
|
|
66
|
+
|
|
67
|
+
result.identifier = query;
|
|
68
|
+
|
|
69
|
+
for (const p of remote.body.results) {
|
|
70
|
+
const product = SearchResultProductSchema.parse({});
|
|
71
|
+
|
|
72
|
+
product.identifier.key = p.id;
|
|
73
|
+
product.name = p.name['en-US'];
|
|
74
|
+
|
|
75
|
+
if (p.masterVariant.images) {
|
|
76
|
+
product.image = p.masterVariant.images[0].url;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
result.products.push(product);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
result.pages = Math.ceil((remote.body.total || 0) / query.pageSize);
|
|
83
|
+
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CapabilitiesSchema, Price, PriceQuery } from "@reactionary/core";
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
export const CommercetoolsCapabilitiesSchema = CapabilitiesSchema.pick({
|
|
5
|
+
product: true,
|
|
6
|
+
search: true,
|
|
7
|
+
identity: true,
|
|
8
|
+
cart: true,
|
|
9
|
+
inventory: true,
|
|
10
|
+
price: true
|
|
11
|
+
}).partial();
|
|
12
|
+
|
|
13
|
+
export type CommercetoolsCapabilities = z.infer<typeof CommercetoolsCapabilitiesSchema>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const CommercetoolsConfigurationSchema = z.looseInterface({
|
|
4
|
+
projectKey: z.string(),
|
|
5
|
+
authUrl: z.string(),
|
|
6
|
+
apiUrl: z.string(),
|
|
7
|
+
clientId: z.string(),
|
|
8
|
+
clientSecret: z.string()
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export type CommercetoolsConfiguration = z.infer<typeof CommercetoolsConfigurationSchema>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ProductSchema } from '@reactionary/core';
|
|
2
|
+
import { CommercetoolsProductProvider } from '../providers/product.provider';
|
|
3
|
+
|
|
4
|
+
describe('Commercetools Product Provider', () => {
|
|
5
|
+
it('should be able to get a product by id', async () => {
|
|
6
|
+
const provider = new CommercetoolsProductProvider({
|
|
7
|
+
apiUrl: process.env['COMMERCETOOLS_API_URL'] || '',
|
|
8
|
+
authUrl: process.env['COMMERCETOOLS_AUTH_URL'] || '',
|
|
9
|
+
clientId: process.env['COMMERCETOOLS_CLIENT_ID'] || '',
|
|
10
|
+
clientSecret: process.env['COMMERCETOOLS_CLIENT_SECRET'] || '',
|
|
11
|
+
projectKey: process.env['COMMERCETOOLS_PROJECT_KEY'] || ''
|
|
12
|
+
}, ProductSchema);
|
|
13
|
+
|
|
14
|
+
const result = await provider.get({ id: '4d28f98d-c446-446e-b59a-d9f718e5b98a'});
|
|
15
|
+
|
|
16
|
+
expect(result.identifier.key).toBe('4d28f98d-c446-446e-b59a-d9f718e5b98a');
|
|
17
|
+
expect(result.name).toBe('Sunnai Glass Bowl');
|
|
18
|
+
expect(result.image).toBe('https://storage.googleapis.com/merchant-center-europe/sample-data/goodstore/Sunnai_Glass_Bowl-1.1.jpeg');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SearchResultSchema } from '@reactionary/core';
|
|
2
|
+
import { CommercetoolsSearchProvider } from '../providers/search.provider';
|
|
3
|
+
|
|
4
|
+
describe('Commercetools Search Provider', () => {
|
|
5
|
+
it('should be able to get a result by term', async () => {
|
|
6
|
+
const provider = new CommercetoolsSearchProvider({
|
|
7
|
+
apiUrl: process.env['COMMERCETOOLS_API_URL'] || '',
|
|
8
|
+
authUrl: process.env['COMMERCETOOLS_AUTH_URL'] || '',
|
|
9
|
+
clientId: process.env['COMMERCETOOLS_CLIENT_ID'] || '',
|
|
10
|
+
clientSecret: process.env['COMMERCETOOLS_CLIENT_SECRET'] || '',
|
|
11
|
+
projectKey: process.env['COMMERCETOOLS_PROJECT_KEY'] || '',
|
|
12
|
+
}, SearchResultSchema);
|
|
13
|
+
|
|
14
|
+
const result = await provider.get({ term: 'glass', page: 0, pageSize: 20, facets: [] });
|
|
15
|
+
|
|
16
|
+
expect(result.products.length).toBeGreaterThan(0);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"forceConsistentCasingInFileNames": true,
|
|
6
|
+
"strict": true,
|
|
7
|
+
"importHelpers": true,
|
|
8
|
+
"noImplicitOverride": true,
|
|
9
|
+
"noImplicitReturns": true,
|
|
10
|
+
"noFallthroughCasesInSwitch": true,
|
|
11
|
+
"noPropertyAccessFromIndexSignature": true
|
|
12
|
+
},
|
|
13
|
+
"files": [],
|
|
14
|
+
"include": [],
|
|
15
|
+
"references": [
|
|
16
|
+
{
|
|
17
|
+
"path": "./tsconfig.lib.json"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "./tsconfig.spec.json"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc",
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"moduleResolution": "node10",
|
|
7
|
+
"types": ["jest", "node"]
|
|
8
|
+
},
|
|
9
|
+
"include": [
|
|
10
|
+
"jest.config.ts",
|
|
11
|
+
"src/**/*.test.ts",
|
|
12
|
+
"src/**/*.spec.ts",
|
|
13
|
+
"src/**/*.d.ts"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import baseConfig from '../../eslint.config.mjs';
|
|
2
|
+
|
|
3
|
+
export default [
|
|
4
|
+
...baseConfig,
|
|
5
|
+
{
|
|
6
|
+
files: ['**/*.json'],
|
|
7
|
+
rules: {
|
|
8
|
+
'@nx/dependency-checks': [
|
|
9
|
+
'error',
|
|
10
|
+
{
|
|
11
|
+
ignoredFiles: [
|
|
12
|
+
'{projectRoot}/eslint.config.{js,cjs,mjs}',
|
|
13
|
+
'{projectRoot}/esbuild.config.{js,ts,mjs,mts}',
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
languageOptions: {
|
|
19
|
+
parser: await import('jsonc-eslint-parser'),
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "provider-fake",
|
|
3
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"sourceRoot": "providers/fake/src",
|
|
5
|
+
"projectType": "library",
|
|
6
|
+
"release": {
|
|
7
|
+
"version": {
|
|
8
|
+
"currentVersionResolver": "git-tag",
|
|
9
|
+
"fallbackCurrentVersionResolver": "disk",
|
|
10
|
+
"preserveLocalDependencyProtocols": false,
|
|
11
|
+
"manifestRootsToUpdate": ["dist/{projectRoot}"]
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"tags": [],
|
|
15
|
+
"targets": {
|
|
16
|
+
"build": {
|
|
17
|
+
"executor": "@nx/esbuild:esbuild",
|
|
18
|
+
"outputs": ["{options.outputPath}"],
|
|
19
|
+
"options": {
|
|
20
|
+
"outputPath": "dist/providers/fake",
|
|
21
|
+
"main": "providers/fake/src/index.ts",
|
|
22
|
+
"tsConfig": "providers/fake/tsconfig.lib.json",
|
|
23
|
+
"assets": ["providers/fake/*.md"],
|
|
24
|
+
"format": ["esm"]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"nx-release-publish": {
|
|
28
|
+
"options": {
|
|
29
|
+
"packageRoot": "dist/{projectRoot}"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Client, IdentityMutationSchema, IdentityQuerySchema, IdentitySchema, ProductMutationSchema, ProductQuerySchema, ProductSchema, SearchMutationSchema, SearchQuerySchema, SearchResultSchema } from "@reactionary/core";
|
|
2
|
+
import { FakeProductProvider } from "../providers/product.provider";
|
|
3
|
+
import { FakeSearchProvider } from "../providers/search.provider";
|
|
4
|
+
import { FakeConfiguration } from "../schema/configuration.schema";
|
|
5
|
+
import { FakeCapabilities } from "../schema/capabilities.schema";
|
|
6
|
+
import { FakeIdentityProvider } from "../providers/identity.provider";
|
|
7
|
+
|
|
8
|
+
export function withFakeCapabilities(configuration: FakeConfiguration, capabilities: FakeCapabilities) {
|
|
9
|
+
const client = {} as Partial<Client>;
|
|
10
|
+
|
|
11
|
+
if (capabilities.product) {
|
|
12
|
+
client.product = new FakeProductProvider(configuration, ProductSchema, ProductQuerySchema, ProductMutationSchema);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (capabilities.search) {
|
|
16
|
+
client.search = new FakeSearchProvider(configuration, SearchResultSchema, SearchQuerySchema, SearchMutationSchema);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (capabilities.identity) {
|
|
20
|
+
client.identity = new FakeIdentityProvider(configuration, IdentitySchema, IdentityQuerySchema, IdentityMutationSchema);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return client;
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './core/initialize';
|
|
2
|
+
|
|
3
|
+
export * from './providers/identity.provider';
|
|
4
|
+
export * from './providers/product.provider';
|
|
5
|
+
export * from './providers/search.provider';
|
|
6
|
+
|
|
7
|
+
export * from './schema/capabilities.schema';
|
|
8
|
+
export * from './schema/configuration.schema';
|