@faststore/api 1.2.23
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 +333 -0
- package/README.md +115 -0
- package/dist/__generated__/schema.d.ts +281 -0
- package/dist/api.cjs.development.js +2226 -0
- package/dist/api.cjs.development.js.map +1 -0
- package/dist/api.cjs.production.min.js +2 -0
- package/dist/api.cjs.production.min.js.map +1 -0
- package/dist/api.esm.js +2217 -0
- package/dist/api.esm.js.map +1 -0
- package/dist/index.d.ts +136 -0
- package/dist/index.js +8 -0
- package/dist/platforms/vtex/clients/commerce/index.d.ts +29 -0
- package/dist/platforms/vtex/clients/commerce/types/Brand.d.ts +8 -0
- package/dist/platforms/vtex/clients/commerce/types/CategoryTree.d.ts +9 -0
- package/dist/platforms/vtex/clients/commerce/types/OrderForm.d.ts +347 -0
- package/dist/platforms/vtex/clients/commerce/types/Simulation.d.ts +154 -0
- package/dist/platforms/vtex/clients/fetch.d.ts +1 -0
- package/dist/platforms/vtex/clients/index.d.ts +32 -0
- package/dist/platforms/vtex/clients/search/index.d.ts +22 -0
- package/dist/platforms/vtex/clients/search/types/AttributeSearchResult.d.ts +56 -0
- package/dist/platforms/vtex/clients/search/types/ProductSearchResult.d.ts +199 -0
- package/dist/platforms/vtex/index.d.ts +144 -0
- package/dist/platforms/vtex/loaders/index.d.ts +7 -0
- package/dist/platforms/vtex/loaders/simulation.d.ts +5 -0
- package/dist/platforms/vtex/loaders/sku.d.ts +6 -0
- package/dist/platforms/vtex/resolvers/aggregateOffer.d.ts +7 -0
- package/dist/platforms/vtex/resolvers/aggregateRating.d.ts +2 -0
- package/dist/platforms/vtex/resolvers/collection.d.ts +8 -0
- package/dist/platforms/vtex/resolvers/facet.d.ts +5 -0
- package/dist/platforms/vtex/resolvers/facetValue.d.ts +5 -0
- package/dist/platforms/vtex/resolvers/mutation.d.ts +58 -0
- package/dist/platforms/vtex/resolvers/offer.d.ts +11 -0
- package/dist/platforms/vtex/resolvers/organization.d.ts +2 -0
- package/dist/platforms/vtex/resolvers/product.d.ts +5 -0
- package/dist/platforms/vtex/resolvers/productGroup.d.ts +3 -0
- package/dist/platforms/vtex/resolvers/query.d.ts +51 -0
- package/dist/platforms/vtex/resolvers/review.d.ts +2 -0
- package/dist/platforms/vtex/resolvers/searchResult.d.ts +5 -0
- package/dist/platforms/vtex/resolvers/seo.d.ts +7 -0
- package/dist/platforms/vtex/resolvers/validateCart.d.ts +71 -0
- package/dist/platforms/vtex/utils/enhanceSku.d.ts +5 -0
- package/dist/platforms/vtex/utils/facets.d.ts +12 -0
- package/dist/platforms/vtex/utils/slugify.d.ts +1 -0
- package/dist/platforms/vtex/utils/sort.d.ts +10 -0
- package/dist/typeDefs/index.d.ts +1 -0
- package/package.json +44 -0
- package/src/__generated__/schema.ts +324 -0
- package/src/index.ts +33 -0
- package/src/platforms/vtex/clients/commerce/index.ts +100 -0
- package/src/platforms/vtex/clients/commerce/types/Brand.ts +8 -0
- package/src/platforms/vtex/clients/commerce/types/CategoryTree.ts +9 -0
- package/src/platforms/vtex/clients/commerce/types/OrderForm.ts +371 -0
- package/src/platforms/vtex/clients/commerce/types/Simulation.ts +170 -0
- package/src/platforms/vtex/clients/fetch.ts +13 -0
- package/src/platforms/vtex/clients/index.ts +15 -0
- package/src/platforms/vtex/clients/search/index.ts +83 -0
- package/src/platforms/vtex/clients/search/types/AttributeSearchResult.ts +61 -0
- package/src/platforms/vtex/clients/search/types/ProductSearchResult.ts +223 -0
- package/src/platforms/vtex/index.ts +62 -0
- package/src/platforms/vtex/loaders/index.ts +16 -0
- package/src/platforms/vtex/loaders/simulation.ts +42 -0
- package/src/platforms/vtex/loaders/sku.ts +61 -0
- package/src/platforms/vtex/resolvers/aggregateOffer.ts +20 -0
- package/src/platforms/vtex/resolvers/aggregateRating.ts +7 -0
- package/src/platforms/vtex/resolvers/collection.ts +43 -0
- package/src/platforms/vtex/resolvers/facet.ts +11 -0
- package/src/platforms/vtex/resolvers/facetValue.ts +11 -0
- package/src/platforms/vtex/resolvers/mutation.ts +5 -0
- package/src/platforms/vtex/resolvers/offer.ts +26 -0
- package/src/platforms/vtex/resolvers/organization.ts +5 -0
- package/src/platforms/vtex/resolvers/product.ts +75 -0
- package/src/platforms/vtex/resolvers/productGroup.ts +9 -0
- package/src/platforms/vtex/resolvers/query.ts +115 -0
- package/src/platforms/vtex/resolvers/review.ts +11 -0
- package/src/platforms/vtex/resolvers/searchResult.ts +46 -0
- package/src/platforms/vtex/resolvers/seo.ts +10 -0
- package/src/platforms/vtex/resolvers/validateCart.ts +166 -0
- package/src/platforms/vtex/utils/enhanceSku.ts +8 -0
- package/src/platforms/vtex/utils/facets.ts +31 -0
- package/src/platforms/vtex/utils/slugify.ts +4 -0
- package/src/platforms/vtex/utils/sort.ts +10 -0
- package/src/typeDefs/aggregateOffer.graphql +10 -0
- package/src/typeDefs/aggregateRating.graphql +4 -0
- package/src/typeDefs/author.graphql +3 -0
- package/src/typeDefs/brand.graphql +3 -0
- package/src/typeDefs/breadcrumb.graphql +10 -0
- package/src/typeDefs/cart.graphql +13 -0
- package/src/typeDefs/collection.graphql +26 -0
- package/src/typeDefs/facet.graphql +14 -0
- package/src/typeDefs/image.graphql +9 -0
- package/src/typeDefs/index.ts +47 -0
- package/src/typeDefs/mutation.graphql +4 -0
- package/src/typeDefs/offer.graphql +21 -0
- package/src/typeDefs/order.graphql +9 -0
- package/src/typeDefs/organization.graphql +7 -0
- package/src/typeDefs/pageInfo.graphql +8 -0
- package/src/typeDefs/product.graphql +25 -0
- package/src/typeDefs/productGroup.graphql +5 -0
- package/src/typeDefs/query.graphql +64 -0
- package/src/typeDefs/review.graphql +9 -0
- package/src/typeDefs/seo.graphql +6 -0
- package/src/typeDefs/status.graphql +5 -0
- package/src/typings/schema.d.ts +7 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { enhanceSku } from '../utils/enhanceSku'
|
|
2
|
+
import { transformSelectedFacet } from '../utils/facets'
|
|
3
|
+
import { SORT_MAP } from '../utils/sort'
|
|
4
|
+
import type {
|
|
5
|
+
QueryProductArgs,
|
|
6
|
+
QueryAllCollectionsArgs,
|
|
7
|
+
QueryAllProductsArgs,
|
|
8
|
+
QuerySearchArgs,
|
|
9
|
+
} from '../../../__generated__/schema'
|
|
10
|
+
import type { CategoryTree } from '../clients/commerce/types/CategoryTree'
|
|
11
|
+
import type { Context } from '../index'
|
|
12
|
+
|
|
13
|
+
export const Query = {
|
|
14
|
+
product: async (_: unknown, { locator }: QueryProductArgs, ctx: Context) => {
|
|
15
|
+
const {
|
|
16
|
+
loaders: { skuLoader },
|
|
17
|
+
} = ctx
|
|
18
|
+
|
|
19
|
+
return skuLoader.load(locator.map(transformSelectedFacet))
|
|
20
|
+
},
|
|
21
|
+
search: async (
|
|
22
|
+
_: unknown,
|
|
23
|
+
{ first, after: maybeAfter, sort, term, selectedFacets }: QuerySearchArgs
|
|
24
|
+
) => {
|
|
25
|
+
const after = maybeAfter ? Number(maybeAfter) : 0
|
|
26
|
+
const searchArgs = {
|
|
27
|
+
page: Math.ceil(after / first),
|
|
28
|
+
count: first,
|
|
29
|
+
query: term,
|
|
30
|
+
sort: SORT_MAP[sort ?? 'score_desc'],
|
|
31
|
+
selectedFacets: selectedFacets?.map(transformSelectedFacet) ?? [],
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return searchArgs
|
|
35
|
+
},
|
|
36
|
+
allProducts: async (
|
|
37
|
+
_: unknown,
|
|
38
|
+
{ first, after: maybeAfter }: QueryAllProductsArgs,
|
|
39
|
+
ctx: Context
|
|
40
|
+
) => {
|
|
41
|
+
const {
|
|
42
|
+
clients: { search },
|
|
43
|
+
} = ctx
|
|
44
|
+
|
|
45
|
+
const after = maybeAfter ? Number(maybeAfter) : 0
|
|
46
|
+
const products = await search.products({
|
|
47
|
+
page: Math.ceil(after / first),
|
|
48
|
+
count: first,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const skus = products.products
|
|
52
|
+
.map((product) => product.skus.map((sku) => enhanceSku(sku, product)))
|
|
53
|
+
.flat()
|
|
54
|
+
.filter((sku) => sku.sellers.length > 0)
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
pageInfo: {
|
|
58
|
+
hasNextPage: products.pagination.after.length > 0,
|
|
59
|
+
hasPreviousPage: products.pagination.before.length > 0,
|
|
60
|
+
startCursor: '0',
|
|
61
|
+
endCursor: products.total.toString(),
|
|
62
|
+
totalCount: products.total,
|
|
63
|
+
},
|
|
64
|
+
edges: skus.map((sku, index) => ({
|
|
65
|
+
node: sku,
|
|
66
|
+
cursor: (after + index).toString(),
|
|
67
|
+
})),
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
allCollections: async (
|
|
71
|
+
_: unknown,
|
|
72
|
+
__: QueryAllCollectionsArgs,
|
|
73
|
+
ctx: Context
|
|
74
|
+
) => {
|
|
75
|
+
const {
|
|
76
|
+
clients: { commerce },
|
|
77
|
+
} = ctx
|
|
78
|
+
|
|
79
|
+
const [brands, tree] = await Promise.all([
|
|
80
|
+
commerce.catalog.brand.list(),
|
|
81
|
+
commerce.catalog.category.tree(),
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
const categories: Array<CategoryTree & { level: number }> = []
|
|
85
|
+
const dfs = (node: CategoryTree, level: number) => {
|
|
86
|
+
categories.push({ ...node, level })
|
|
87
|
+
|
|
88
|
+
for (const child of node.children) {
|
|
89
|
+
dfs(child, level + 1)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for (const node of tree) {
|
|
94
|
+
dfs(node, 0)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const collections = [
|
|
98
|
+
...brands.map((x) => ({ ...x, type: 'brand' })),
|
|
99
|
+
...categories,
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
pageInfo: {
|
|
104
|
+
hasNextPage: false,
|
|
105
|
+
hasPreviousPage: false,
|
|
106
|
+
startCursor: '0',
|
|
107
|
+
endCursor: '0',
|
|
108
|
+
},
|
|
109
|
+
edges: collections.map((node, index) => ({
|
|
110
|
+
node,
|
|
111
|
+
cursor: index.toString(),
|
|
112
|
+
})),
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { enhanceSku } from '../utils/enhanceSku'
|
|
2
|
+
import type { Resolver } from '..'
|
|
3
|
+
import type { SearchArgs } from '../clients/search'
|
|
4
|
+
|
|
5
|
+
type Root = Omit<SearchArgs, 'type'>
|
|
6
|
+
|
|
7
|
+
export const StoreSearchResult: Record<string, Resolver<Root>> = {
|
|
8
|
+
products: async (searchArgs, _, ctx) => {
|
|
9
|
+
const {
|
|
10
|
+
clients: { search },
|
|
11
|
+
} = ctx
|
|
12
|
+
|
|
13
|
+
const products = await search.products(searchArgs)
|
|
14
|
+
|
|
15
|
+
const skus = products.products
|
|
16
|
+
.map((product) => {
|
|
17
|
+
const maybeSku = product.skus.find((x) => x.sellers.length > 0)
|
|
18
|
+
|
|
19
|
+
return maybeSku && enhanceSku(maybeSku, product)
|
|
20
|
+
})
|
|
21
|
+
.filter((sku) => !!sku)
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
pageInfo: {
|
|
25
|
+
hasNextPage: products.pagination.after.length > 0,
|
|
26
|
+
hasPreviousPage: products.pagination.before.length > 0,
|
|
27
|
+
startCursor: '0',
|
|
28
|
+
endCursor: products.total.toString(),
|
|
29
|
+
totalCount: products.total,
|
|
30
|
+
},
|
|
31
|
+
edges: skus.map((sku, index) => ({
|
|
32
|
+
node: sku,
|
|
33
|
+
cursor: index.toString(),
|
|
34
|
+
})),
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
facets: async (searchArgs, _, ctx) => {
|
|
38
|
+
const {
|
|
39
|
+
clients: { search: is },
|
|
40
|
+
} = ctx
|
|
41
|
+
|
|
42
|
+
const facets = await is.facets(searchArgs)
|
|
43
|
+
|
|
44
|
+
return facets.attributes
|
|
45
|
+
},
|
|
46
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Resolver } from '..'
|
|
2
|
+
|
|
3
|
+
type Root = { title?: string; description?: string }
|
|
4
|
+
|
|
5
|
+
export const StoreSeo: Record<string, Resolver<Root>> = {
|
|
6
|
+
title: ({ title }) => title ?? '',
|
|
7
|
+
description: ({ description }) => description ?? '',
|
|
8
|
+
titleTemplate: () => '',
|
|
9
|
+
canonical: () => '',
|
|
10
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import deepEquals from 'fast-deep-equal'
|
|
2
|
+
|
|
3
|
+
import type { IStoreCart, IStoreOffer } from '../../../__generated__/schema'
|
|
4
|
+
import type {
|
|
5
|
+
OrderForm,
|
|
6
|
+
OrderFormItem,
|
|
7
|
+
OrderFormInputItem,
|
|
8
|
+
} from '../clients/commerce/types/OrderForm'
|
|
9
|
+
import type { Context } from '..'
|
|
10
|
+
|
|
11
|
+
type Indexed<T> = T & { index?: number }
|
|
12
|
+
|
|
13
|
+
const getId = (item: IStoreOffer) =>
|
|
14
|
+
[item.itemOffered.sku, item.seller.identifier, item.price].join('::')
|
|
15
|
+
|
|
16
|
+
const orderFormItemToOffer = (
|
|
17
|
+
item: OrderFormItem,
|
|
18
|
+
index?: number
|
|
19
|
+
): Indexed<IStoreOffer> => ({
|
|
20
|
+
listPrice: item.listPrice / 100,
|
|
21
|
+
price: item.sellingPrice / 100,
|
|
22
|
+
quantity: item.quantity,
|
|
23
|
+
seller: { identifier: item.seller },
|
|
24
|
+
itemOffered: {
|
|
25
|
+
sku: item.id,
|
|
26
|
+
image: [],
|
|
27
|
+
name: item.name,
|
|
28
|
+
},
|
|
29
|
+
index,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const offerToOrderItemInput = (
|
|
33
|
+
offer: Indexed<IStoreOffer>
|
|
34
|
+
): OrderFormInputItem => ({
|
|
35
|
+
quantity: offer.quantity,
|
|
36
|
+
seller: offer.seller.identifier,
|
|
37
|
+
id: offer.itemOffered.sku,
|
|
38
|
+
index: offer.index,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const groupById = (offers: IStoreOffer[]): Map<string, IStoreOffer> =>
|
|
42
|
+
offers.reduce((acc, item) => {
|
|
43
|
+
const id = getId(item)
|
|
44
|
+
|
|
45
|
+
acc.set(id, acc.get(id) ?? item)
|
|
46
|
+
|
|
47
|
+
return acc
|
|
48
|
+
}, new Map<string, IStoreOffer>())
|
|
49
|
+
|
|
50
|
+
const equals = (of1: OrderForm, of2: OrderForm) => {
|
|
51
|
+
const pick = ({ orderFormId, messages, items, salesChannel }: OrderForm) => ({
|
|
52
|
+
orderFormId,
|
|
53
|
+
messages,
|
|
54
|
+
salesChannel,
|
|
55
|
+
items: items.map(
|
|
56
|
+
({ uniqueId, quantity, seller, sellingPrice, availability }) => ({
|
|
57
|
+
uniqueId,
|
|
58
|
+
quantity,
|
|
59
|
+
seller,
|
|
60
|
+
sellingPrice,
|
|
61
|
+
availability,
|
|
62
|
+
})
|
|
63
|
+
),
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
return deepEquals(pick(of1), pick(of2))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* This resolver implements the optimistic cart behavior. The main idea in here
|
|
71
|
+
* is that we receive a cart from the UI (as query params) and we validate it with
|
|
72
|
+
* the commerce platform. If the cart is valid, we return null, if the cart is
|
|
73
|
+
* invalid according to the commerce platform, we return the new cart the UI should use
|
|
74
|
+
* instead
|
|
75
|
+
*
|
|
76
|
+
* The algoritm is something like:
|
|
77
|
+
* 1. Fetch orderForm from VTEX
|
|
78
|
+
* 2. Compute delta changes between the orderForm and the UI's cart
|
|
79
|
+
* 3. Update the orderForm in VTEX platform accordingly
|
|
80
|
+
* 4. If any chages were made, send to the UI the new cart. Null otherwise
|
|
81
|
+
*/
|
|
82
|
+
export const validateCart = async (
|
|
83
|
+
_: unknown,
|
|
84
|
+
{
|
|
85
|
+
cart: {
|
|
86
|
+
order: { orderNumber, acceptedOffer },
|
|
87
|
+
},
|
|
88
|
+
}: { cart: IStoreCart },
|
|
89
|
+
ctx: Context
|
|
90
|
+
) => {
|
|
91
|
+
const {
|
|
92
|
+
clients: { commerce },
|
|
93
|
+
loaders: { skuLoader },
|
|
94
|
+
} = ctx
|
|
95
|
+
|
|
96
|
+
// Step1: Get OrderForm from VTEX Commerce
|
|
97
|
+
const orderForm = await commerce.checkout.orderForm({
|
|
98
|
+
id: orderNumber,
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// Step2: Process items from both browser and checkout so they have the same shape
|
|
102
|
+
const browserItemsById = groupById(acceptedOffer)
|
|
103
|
+
const originItemsById = groupById(orderForm.items.map(orderFormItemToOffer))
|
|
104
|
+
const browserItems = Array.from(browserItemsById.values()) // items on the user's browser
|
|
105
|
+
const originItems = Array.from(originItemsById.values()) // items on the VTEX platform backend
|
|
106
|
+
|
|
107
|
+
// Step3: Compute delta changes
|
|
108
|
+
const { itemsToAdd, itemsToUpdate } = browserItems.reduce(
|
|
109
|
+
(acc, item) => {
|
|
110
|
+
const maybeOriginItem = originItemsById.get(getId(item))
|
|
111
|
+
|
|
112
|
+
if (!maybeOriginItem) {
|
|
113
|
+
acc.itemsToAdd.push(item)
|
|
114
|
+
} else {
|
|
115
|
+
acc.itemsToUpdate.push({
|
|
116
|
+
...maybeOriginItem,
|
|
117
|
+
quantity: item.quantity,
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return acc
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
itemsToAdd: [] as IStoreOffer[],
|
|
125
|
+
itemsToUpdate: [] as IStoreOffer[],
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
const itemsToDelete = originItems
|
|
130
|
+
.filter((item) => !browserItemsById.has(getId(item)))
|
|
131
|
+
.map((item) => ({ ...item, quantity: 0 }))
|
|
132
|
+
|
|
133
|
+
const changes = [...itemsToAdd, ...itemsToUpdate, ...itemsToDelete].map(
|
|
134
|
+
offerToOrderItemInput
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
if (changes.length === 0) {
|
|
138
|
+
return null
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Step4: Apply delta changes to order form
|
|
142
|
+
const updatedOrderForm = await commerce.checkout.updateOrderFormItems({
|
|
143
|
+
id: orderForm.orderFormId,
|
|
144
|
+
orderItems: changes,
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// Step5: If no changes detected before/after updating orderForm, the order is validated
|
|
148
|
+
if (equals(orderForm, updatedOrderForm)) {
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Step6: There were changes, convert orderForm to StoreOrder
|
|
153
|
+
return {
|
|
154
|
+
order: {
|
|
155
|
+
orderNumber: updatedOrderForm.orderFormId,
|
|
156
|
+
acceptedOffer: updatedOrderForm.items.map((item) => ({
|
|
157
|
+
...item,
|
|
158
|
+
product: skuLoader.load([{ key: 'id', value: item.id }]), // TODO: add channel
|
|
159
|
+
})),
|
|
160
|
+
},
|
|
161
|
+
messages: updatedOrderForm.messages.map(({ text, status }) => ({
|
|
162
|
+
text,
|
|
163
|
+
status: status.toUpperCase(),
|
|
164
|
+
})),
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface SelectedFacet {
|
|
2
|
+
key: string
|
|
3
|
+
value: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const getIdFromSlug = (slug: string) => {
|
|
7
|
+
const id = slug.split('-').pop()
|
|
8
|
+
|
|
9
|
+
if (id == null) {
|
|
10
|
+
throw new Error('Error while extracting sku id from product slug')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return id
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Transform facets from the store to VTEX platform facets.
|
|
18
|
+
* For instance, the channel in Store becames trade-policy in VTEX's realm
|
|
19
|
+
* */
|
|
20
|
+
export const transformSelectedFacet = ({ key, value }: SelectedFacet) => {
|
|
21
|
+
switch (key) {
|
|
22
|
+
case 'channel':
|
|
23
|
+
return { key: 'trade-policy', value }
|
|
24
|
+
|
|
25
|
+
case 'slug':
|
|
26
|
+
return { key: 'id', value: getIdFromSlug(value) }
|
|
27
|
+
|
|
28
|
+
default:
|
|
29
|
+
return { key, value }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type StoreAggregateOffer {
|
|
2
|
+
# Highest spot price amongst all sellers
|
|
3
|
+
highPrice: Float!
|
|
4
|
+
# Lowest spot price amongst all sellers
|
|
5
|
+
lowPrice: Float!
|
|
6
|
+
# Number of sellers selling this sku
|
|
7
|
+
offerCount: Int!
|
|
8
|
+
priceCurrency: String!
|
|
9
|
+
offers: [StoreOffer!]!
|
|
10
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
enum StoreCollectionType {
|
|
2
|
+
Department
|
|
3
|
+
Category
|
|
4
|
+
Brand
|
|
5
|
+
Cluster
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type StoreCollectionFacet {
|
|
9
|
+
key: String!
|
|
10
|
+
value: String!
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type StoreCollectionMeta {
|
|
14
|
+
selectedFacets: [StoreCollectionFacet!]!
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type StoreCollection {
|
|
18
|
+
# Meta tag data
|
|
19
|
+
seo: StoreSeo!
|
|
20
|
+
# location for structured data
|
|
21
|
+
breadcrumbList: StoreBreadcrumbList!
|
|
22
|
+
meta: StoreCollectionMeta!
|
|
23
|
+
id: ID!
|
|
24
|
+
slug: String!
|
|
25
|
+
type: StoreCollectionType!
|
|
26
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { print } from 'graphql'
|
|
2
|
+
|
|
3
|
+
import AggregateOffer from './aggregateOffer.graphql'
|
|
4
|
+
import AggregateRating from './aggregateRating.graphql'
|
|
5
|
+
import Author from './author.graphql'
|
|
6
|
+
import Brand from './brand.graphql'
|
|
7
|
+
import Breadcrumb from './breadcrumb.graphql'
|
|
8
|
+
import Collection from './collection.graphql'
|
|
9
|
+
import Facet from './facet.graphql'
|
|
10
|
+
import Image from './image.graphql'
|
|
11
|
+
import Mutation from './mutation.graphql'
|
|
12
|
+
import Offer from './offer.graphql'
|
|
13
|
+
import Order from './order.graphql'
|
|
14
|
+
import Organization from './organization.graphql'
|
|
15
|
+
import PageInfo from './pageInfo.graphql'
|
|
16
|
+
import Product from './product.graphql'
|
|
17
|
+
import ProductGroup from './productGroup.graphql'
|
|
18
|
+
import Query from './query.graphql'
|
|
19
|
+
import Review from './review.graphql'
|
|
20
|
+
import Seo from './seo.graphql'
|
|
21
|
+
import Cart from './cart.graphql'
|
|
22
|
+
import Status from './status.graphql'
|
|
23
|
+
|
|
24
|
+
export const typeDefs = [
|
|
25
|
+
Query,
|
|
26
|
+
Mutation,
|
|
27
|
+
Brand,
|
|
28
|
+
Breadcrumb,
|
|
29
|
+
Collection,
|
|
30
|
+
Facet,
|
|
31
|
+
Image,
|
|
32
|
+
PageInfo,
|
|
33
|
+
Product,
|
|
34
|
+
Seo,
|
|
35
|
+
Offer,
|
|
36
|
+
AggregateRating,
|
|
37
|
+
Review,
|
|
38
|
+
Author,
|
|
39
|
+
ProductGroup,
|
|
40
|
+
Organization,
|
|
41
|
+
AggregateOffer,
|
|
42
|
+
Order,
|
|
43
|
+
Cart,
|
|
44
|
+
Status,
|
|
45
|
+
]
|
|
46
|
+
.map(print)
|
|
47
|
+
.join('\n')
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type StoreOffer {
|
|
2
|
+
listPrice: Float!
|
|
3
|
+
sellingPrice: Float!
|
|
4
|
+
priceCurrency: String!
|
|
5
|
+
# Also known as spotPrice
|
|
6
|
+
price: Float!
|
|
7
|
+
priceValidUntil: String!
|
|
8
|
+
itemCondition: String!
|
|
9
|
+
availability: String!
|
|
10
|
+
seller: StoreOrganization!
|
|
11
|
+
itemOffered: StoreProduct!
|
|
12
|
+
quantity: Int!
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
input IStoreOffer {
|
|
16
|
+
price: Float!
|
|
17
|
+
listPrice: Float!
|
|
18
|
+
seller: IStoreOrganization!
|
|
19
|
+
itemOffered: IStoreProduct!
|
|
20
|
+
quantity: Int!
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type StoreProduct {
|
|
2
|
+
# Meta tag data
|
|
3
|
+
seo: StoreSeo!
|
|
4
|
+
# Location for structured data
|
|
5
|
+
breadcrumbList: StoreBreadcrumbList!
|
|
6
|
+
# Where to retrieve this entity
|
|
7
|
+
slug: String!
|
|
8
|
+
name: String!
|
|
9
|
+
productID: String!
|
|
10
|
+
brand: StoreBrand!
|
|
11
|
+
description: String!
|
|
12
|
+
image: [StoreImage!]!
|
|
13
|
+
offers: StoreAggregateOffer!
|
|
14
|
+
sku: String!
|
|
15
|
+
gtin: String!
|
|
16
|
+
review: [StoreReview!]!
|
|
17
|
+
aggregateRating: StoreAggregateRating!
|
|
18
|
+
isVariantOf: StoreProductGroup!
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
input IStoreProduct {
|
|
22
|
+
sku: String!
|
|
23
|
+
name: String!
|
|
24
|
+
image: [IStoreImage!]!
|
|
25
|
+
}
|