@faststore/api 1.12.37 → 1.12.39
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/dist/api.cjs.development.js +144 -26
- package/dist/api.cjs.development.js.map +1 -1
- package/dist/api.cjs.production.min.js +1 -1
- package/dist/api.cjs.production.min.js.map +1 -1
- package/dist/api.esm.js +144 -26
- package/dist/api.esm.js.map +1 -1
- package/dist/platforms/vtex/clients/commerce/index.d.ts +3 -2
- package/dist/platforms/vtex/clients/commerce/types/Session.d.ts +4 -0
- package/dist/platforms/vtex/clients/index.d.ts +3 -2
- package/dist/platforms/vtex/resolvers/validateCart.d.ts +1 -1
- package/dist/platforms/vtex/utils/getCookies.d.ts +1 -0
- package/package.json +2 -2
- package/src/platforms/vtex/clients/commerce/index.ts +45 -18
- package/src/platforms/vtex/clients/commerce/types/Session.ts +5 -0
- package/src/platforms/vtex/clients/fetch.ts +10 -1
- package/src/platforms/vtex/resolvers/validateCart.ts +37 -20
- package/src/platforms/vtex/utils/getCookies.ts +8 -0
- package/src/typings/json.d.ts +4 -0
|
@@ -26,7 +26,7 @@ export declare const VtexCommerce: ({ account, environment }: Options, ctx: Cont
|
|
|
26
26
|
pagetype: (slug: string) => Promise<PortalPagetype>;
|
|
27
27
|
};
|
|
28
28
|
products: {
|
|
29
|
-
crossselling: ({ type, productId, groupByProduct }: {
|
|
29
|
+
crossselling: ({ type, productId, groupByProduct, }: {
|
|
30
30
|
type: ValueOf<typeof FACET_CROSS_SELLING_MAP>;
|
|
31
31
|
productId: string;
|
|
32
32
|
groupByProduct?: boolean | undefined;
|
|
@@ -35,7 +35,7 @@ export declare const VtexCommerce: ({ account, environment }: Options, ctx: Cont
|
|
|
35
35
|
};
|
|
36
36
|
checkout: {
|
|
37
37
|
simulation: (args: SimulationArgs, { salesChannel }?: SimulationOptions) => Promise<Simulation>;
|
|
38
|
-
shippingData: ({ id, body }: {
|
|
38
|
+
shippingData: ({ id, body, }: {
|
|
39
39
|
id: string;
|
|
40
40
|
body: unknown;
|
|
41
41
|
}) => Promise<OrderForm>;
|
|
@@ -60,6 +60,7 @@ export declare const VtexCommerce: ({ account, environment }: Options, ctx: Cont
|
|
|
60
60
|
address: ({ postalCode, country, }: AddressInput) => Promise<Address>;
|
|
61
61
|
};
|
|
62
62
|
session: (search: string) => Promise<Session>;
|
|
63
|
+
getSessionOrder: () => Promise<Session>;
|
|
63
64
|
subscribeToNewsletter: (data: {
|
|
64
65
|
name: string;
|
|
65
66
|
email: string;
|
|
@@ -5,6 +5,7 @@ export interface Session {
|
|
|
5
5
|
export interface Namespaces {
|
|
6
6
|
profile?: Profile;
|
|
7
7
|
store?: Store;
|
|
8
|
+
checkout?: Checkout;
|
|
8
9
|
}
|
|
9
10
|
export interface Value {
|
|
10
11
|
value: string;
|
|
@@ -22,3 +23,6 @@ export interface Profile {
|
|
|
22
23
|
firstName?: Value;
|
|
23
24
|
lastName?: Value;
|
|
24
25
|
}
|
|
26
|
+
export interface Checkout {
|
|
27
|
+
orderFormId?: Value;
|
|
28
|
+
}
|
|
@@ -20,7 +20,7 @@ export declare const getClients: (options: Options, ctx: Context) => {
|
|
|
20
20
|
pagetype: (slug: string) => Promise<import("./commerce/types/Portal").PortalPagetype>;
|
|
21
21
|
};
|
|
22
22
|
products: {
|
|
23
|
-
crossselling: ({ type, productId, groupByProduct }: {
|
|
23
|
+
crossselling: ({ type, productId, groupByProduct, }: {
|
|
24
24
|
type: "whoboughtalsobought" | "whosawalsosaw" | "similars" | "whosawalsobought" | "accessories" | "suggestions";
|
|
25
25
|
productId: string;
|
|
26
26
|
groupByProduct?: boolean | undefined;
|
|
@@ -29,7 +29,7 @@ export declare const getClients: (options: Options, ctx: Context) => {
|
|
|
29
29
|
};
|
|
30
30
|
checkout: {
|
|
31
31
|
simulation: (args: import("./commerce/types/Simulation").SimulationArgs, { salesChannel }?: import("./commerce/types/Simulation").SimulationOptions) => Promise<import("./commerce/types/Simulation").Simulation>;
|
|
32
|
-
shippingData: ({ id, body }: {
|
|
32
|
+
shippingData: ({ id, body, }: {
|
|
33
33
|
id: string;
|
|
34
34
|
body: unknown;
|
|
35
35
|
}) => Promise<import("./commerce/types/OrderForm").OrderForm>;
|
|
@@ -54,6 +54,7 @@ export declare const getClients: (options: Options, ctx: Context) => {
|
|
|
54
54
|
address: ({ postalCode, country, }: import("./commerce/types/Address").AddressInput) => Promise<import("./commerce/types/Address").Address>;
|
|
55
55
|
};
|
|
56
56
|
session: (search: string) => Promise<import("./commerce/types/Session").Session>;
|
|
57
|
+
getSessionOrder: () => Promise<import("./commerce/types/Session").Session>;
|
|
57
58
|
subscribeToNewsletter: (data: {
|
|
58
59
|
name: string;
|
|
59
60
|
email: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { MutationValidateCartArgs } from '../../../__generated__/schema';
|
|
2
1
|
import type { Context } from '..';
|
|
2
|
+
import type { MutationValidateCartArgs } from '../../../__generated__/schema';
|
|
3
3
|
/**
|
|
4
4
|
* This resolver implements the optimistic cart behavior. The main idea in here
|
|
5
5
|
* is that we receive a cart from the UI (as query params) and we validate it with
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getCookie: (name: string, cookie: string) => string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faststore/api",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.39",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"graphql": "^15.6.0"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "872ef5ad4ff1db650a89222f96ed7073434da66c"
|
|
50
50
|
}
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
} from './types/Simulation'
|
|
16
16
|
import type { Session } from './types/Session'
|
|
17
17
|
import type { Channel } from '../../utils/channel'
|
|
18
|
+
import { getCookie } from '../../utils/getCookies'
|
|
18
19
|
import type { SalesChannel } from './types/SalesChannel'
|
|
19
20
|
import { MasterDataResponse } from './types/Newsletter'
|
|
20
21
|
import type { Address, AddressInput } from './types/Address'
|
|
@@ -51,20 +52,22 @@ export const VtexCommerce = (
|
|
|
51
52
|
fetchAPI(`${base}/api/catalog_system/pub/portal/pagetype/${slug}`),
|
|
52
53
|
},
|
|
53
54
|
products: {
|
|
54
|
-
crossselling: (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
crossselling: ({
|
|
56
|
+
type,
|
|
57
|
+
productId,
|
|
58
|
+
groupByProduct = true,
|
|
59
|
+
}: {
|
|
60
|
+
type: ValueOf<typeof FACET_CROSS_SELLING_MAP>
|
|
61
|
+
productId: string
|
|
62
|
+
groupByProduct?: boolean
|
|
63
|
+
}): Promise<PortalProduct[]> => {
|
|
61
64
|
const params = new URLSearchParams({
|
|
62
65
|
sc: ctx.storage.channel.salesChannel,
|
|
63
66
|
groupByProduct: groupByProduct.toString(),
|
|
64
67
|
})
|
|
65
68
|
|
|
66
69
|
return fetchAPI(
|
|
67
|
-
`${base}/api/catalog_system/pub/products/crossselling/${type}/${productId}?${params}
|
|
70
|
+
`${base}/api/catalog_system/pub/products/crossselling/${type}/${productId}?${params}`
|
|
68
71
|
)
|
|
69
72
|
},
|
|
70
73
|
},
|
|
@@ -86,16 +89,20 @@ export const VtexCommerce = (
|
|
|
86
89
|
}
|
|
87
90
|
)
|
|
88
91
|
},
|
|
89
|
-
shippingData: (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
shippingData: ({
|
|
93
|
+
id,
|
|
94
|
+
body,
|
|
95
|
+
}: {
|
|
96
|
+
id: string
|
|
97
|
+
body: unknown
|
|
98
|
+
}): Promise<OrderForm> => {
|
|
92
99
|
return fetchAPI(
|
|
93
100
|
`${base}/api/checkout/pub/orderForm/${id}/attachments/shippingData`,
|
|
94
101
|
{
|
|
95
102
|
...BASE_INIT,
|
|
96
103
|
body: JSON.stringify(body),
|
|
97
|
-
}
|
|
98
|
-
)
|
|
104
|
+
}
|
|
105
|
+
)
|
|
99
106
|
},
|
|
100
107
|
orderForm: ({
|
|
101
108
|
id,
|
|
@@ -159,7 +166,7 @@ export const VtexCommerce = (
|
|
|
159
166
|
...BASE_INIT,
|
|
160
167
|
body: JSON.stringify({ value }),
|
|
161
168
|
method: 'PUT',
|
|
162
|
-
}
|
|
169
|
+
}
|
|
163
170
|
)
|
|
164
171
|
},
|
|
165
172
|
region: async ({
|
|
@@ -189,14 +196,34 @@ export const VtexCommerce = (
|
|
|
189
196
|
'items',
|
|
190
197
|
'profile.id,profile.email,profile.firstName,profile.lastName,store.channel,store.countryCode,store.cultureInfo,store.currencyCode,store.currencySymbol'
|
|
191
198
|
)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
199
|
+
if (getCookie('vtex_session', ctx.headers.cookie)) {
|
|
200
|
+
// cookie set
|
|
201
|
+
return fetchAPI(`${base}/api/sessions?${params.toString()}`, {
|
|
202
|
+
method: 'GET',
|
|
203
|
+
headers: {
|
|
204
|
+
'content-type': 'application/json',
|
|
205
|
+
cookie: ctx.headers.cookie,
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
} else {
|
|
209
|
+
// cookie unset -> create session
|
|
210
|
+
return fetchAPI(`${base}/api/sessions?${params.toString()}`, {
|
|
211
|
+
method: 'POST',
|
|
212
|
+
headers: {
|
|
213
|
+
'content-type': 'application/json',
|
|
214
|
+
cookie: ctx.headers.cookie,
|
|
215
|
+
},
|
|
216
|
+
body: '{}',
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
getSessionOrder: (): Promise<Session> => {
|
|
221
|
+
return fetchAPI(`${base}/api/sessions?items=checkout.orderFormId`, {
|
|
222
|
+
method: 'GET',
|
|
195
223
|
headers: {
|
|
196
224
|
'content-type': 'application/json',
|
|
197
225
|
cookie: ctx.headers.cookie,
|
|
198
226
|
},
|
|
199
|
-
body: '{}',
|
|
200
227
|
})
|
|
201
228
|
},
|
|
202
229
|
subscribeToNewsletter: (data: {
|
|
@@ -6,6 +6,7 @@ export interface Session {
|
|
|
6
6
|
export interface Namespaces {
|
|
7
7
|
profile?: Profile
|
|
8
8
|
store?: Store
|
|
9
|
+
checkout?: Checkout
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export interface Value {
|
|
@@ -26,3 +27,7 @@ export interface Profile {
|
|
|
26
27
|
firstName?: Value
|
|
27
28
|
lastName?: Value
|
|
28
29
|
}
|
|
30
|
+
|
|
31
|
+
export interface Checkout {
|
|
32
|
+
orderFormId?: Value
|
|
33
|
+
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import fetch from 'isomorphic-unfetch'
|
|
2
|
+
import packageJson from '../../../../package.json'
|
|
3
|
+
|
|
4
|
+
const USER_AGENT = `${packageJson.name}@${packageJson.version}`
|
|
2
5
|
|
|
3
6
|
export const fetchAPI = async (info: RequestInfo, init?: RequestInit) => {
|
|
4
|
-
const response = await fetch(info,
|
|
7
|
+
const response = await fetch(info, {
|
|
8
|
+
...init,
|
|
9
|
+
headers: {
|
|
10
|
+
...init?.headers,
|
|
11
|
+
'User-Agent': USER_AGENT,
|
|
12
|
+
},
|
|
13
|
+
})
|
|
5
14
|
|
|
6
15
|
if (response.ok) {
|
|
7
16
|
return response.status !== 204 ? response.json() : undefined
|
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import deepEquals from 'fast-deep-equal'
|
|
2
2
|
|
|
3
|
+
import { mutateChannelContext, mutateLocaleContext } from '../utils/contex'
|
|
4
|
+
import { getCookie } from '../utils/getCookies'
|
|
3
5
|
import { md5 } from '../utils/md5'
|
|
4
6
|
import {
|
|
5
7
|
attachmentToPropertyValue,
|
|
6
8
|
getPropertyId,
|
|
7
|
-
VALUE_REFERENCES
|
|
9
|
+
VALUE_REFERENCES
|
|
8
10
|
} from '../utils/propertyValue'
|
|
9
|
-
import { mutateChannelContext, mutateLocaleContext } from '../utils/contex'
|
|
10
11
|
|
|
12
|
+
import type { Context } from '..'
|
|
11
13
|
import type {
|
|
12
|
-
IStoreSession,
|
|
13
14
|
IStoreOffer,
|
|
14
15
|
IStoreOrder,
|
|
15
|
-
IStorePropertyValue,
|
|
16
|
-
|
|
17
|
-
MutationValidateCartArgs,
|
|
16
|
+
IStorePropertyValue, IStoreSession, Maybe,
|
|
17
|
+
MutationValidateCartArgs
|
|
18
18
|
} from '../../../__generated__/schema'
|
|
19
19
|
import type {
|
|
20
20
|
OrderForm,
|
|
21
21
|
OrderFormInputItem,
|
|
22
|
-
OrderFormItem
|
|
22
|
+
OrderFormItem
|
|
23
23
|
} from '../clients/commerce/types/OrderForm'
|
|
24
|
-
import type { Context } from '..'
|
|
25
24
|
|
|
26
25
|
type Indexed<T> = T & { index?: number }
|
|
27
26
|
|
|
@@ -200,6 +199,20 @@ const isOrderFormStale = (form: OrderForm) => {
|
|
|
200
199
|
return newEtag !== oldEtag
|
|
201
200
|
}
|
|
202
201
|
|
|
202
|
+
async function getOrderNumberFromSession(
|
|
203
|
+
headers: Record<string, string> = {},
|
|
204
|
+
commerce: Context['clients']['commerce']
|
|
205
|
+
) {
|
|
206
|
+
|
|
207
|
+
const cookieSession = getCookie('vtex_session', headers.cookie)
|
|
208
|
+
|
|
209
|
+
if (cookieSession) {
|
|
210
|
+
const { namespaces } = await commerce.getSessionOrder()
|
|
211
|
+
return namespaces.checkout?.orderFormId?.value
|
|
212
|
+
}
|
|
213
|
+
return ;
|
|
214
|
+
}
|
|
215
|
+
|
|
203
216
|
// Returns the regionalized orderForm
|
|
204
217
|
const getOrderForm = async (
|
|
205
218
|
id: string,
|
|
@@ -253,11 +266,11 @@ export const validateCart = async (
|
|
|
253
266
|
{ cart: { order }, session }: MutationValidateCartArgs,
|
|
254
267
|
ctx: Context
|
|
255
268
|
) => {
|
|
256
|
-
const {
|
|
257
|
-
const { orderNumber, acceptedOffer } = order
|
|
269
|
+
const { orderNumber: orderNumberFromCart, acceptedOffer } = order
|
|
258
270
|
const {
|
|
259
271
|
clients: { commerce },
|
|
260
272
|
loaders: { skuLoader },
|
|
273
|
+
headers,
|
|
261
274
|
} = ctx
|
|
262
275
|
|
|
263
276
|
const channel = session?.channel
|
|
@@ -271,22 +284,26 @@ export const validateCart = async (
|
|
|
271
284
|
mutateLocaleContext(ctx, locale)
|
|
272
285
|
}
|
|
273
286
|
|
|
287
|
+
const orderNumberFromSession = await getOrderNumberFromSession(
|
|
288
|
+
headers,
|
|
289
|
+
commerce
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
const orderNumber = orderNumberFromSession ?? orderNumberFromCart ?? ''
|
|
293
|
+
|
|
274
294
|
// Step1: Get OrderForm from VTEX Commerce
|
|
275
295
|
const orderForm = await getOrderForm(orderNumber, session, ctx)
|
|
276
296
|
|
|
277
297
|
// Step1.5: Check if another system changed the orderForm with this orderNumber
|
|
278
298
|
// If so, this means the user interacted with this cart elsewhere and expects
|
|
279
299
|
// to see this new cart state instead of what's stored on the user's browser.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
joinItems
|
|
300
|
+
const isStale = isOrderFormStale(orderForm)
|
|
301
|
+
|
|
302
|
+
if (isStale && orderNumber) {
|
|
303
|
+
const newOrderForm = await setOrderFormEtag(orderForm, commerce).then(
|
|
304
|
+
joinItems
|
|
286
305
|
)
|
|
287
|
-
|
|
288
|
-
return orderFormToCart(newOrderForm, skuLoader)
|
|
289
|
-
}
|
|
306
|
+
return orderFormToCart(newOrderForm, skuLoader)
|
|
290
307
|
}
|
|
291
308
|
|
|
292
309
|
// Step2: Process items from both browser and checkout so they have the same shape
|
|
@@ -349,7 +366,7 @@ export const validateCart = async (
|
|
|
349
366
|
})
|
|
350
367
|
// update orderForm etag so we know last time we touched this orderForm
|
|
351
368
|
.then((form) =>
|
|
352
|
-
|
|
369
|
+
setOrderFormEtag(form, commerce)
|
|
353
370
|
)
|
|
354
371
|
.then(joinItems)
|
|
355
372
|
|