@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,223 @@
|
|
|
1
|
+
export interface ProductSearchResult {
|
|
2
|
+
total: number
|
|
3
|
+
products: Product[]
|
|
4
|
+
pagination: Pagination
|
|
5
|
+
sampling: boolean
|
|
6
|
+
options: Options
|
|
7
|
+
translated: boolean
|
|
8
|
+
locale: string
|
|
9
|
+
query: string
|
|
10
|
+
operator: string
|
|
11
|
+
fuzzy: string
|
|
12
|
+
correction: Correction
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Correction {
|
|
16
|
+
misspelled: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Options {
|
|
20
|
+
sorts: Sort[]
|
|
21
|
+
counts: Count[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Count {
|
|
25
|
+
count: number
|
|
26
|
+
proxyURL: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface Sort {
|
|
30
|
+
field: string
|
|
31
|
+
order: string
|
|
32
|
+
active?: boolean
|
|
33
|
+
proxyURL: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface Pagination {
|
|
37
|
+
count: number
|
|
38
|
+
current: Current
|
|
39
|
+
before: any[]
|
|
40
|
+
after: Current[]
|
|
41
|
+
perPage: number
|
|
42
|
+
next: Current
|
|
43
|
+
previous: First
|
|
44
|
+
first: First
|
|
45
|
+
last: Current
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface Current {
|
|
49
|
+
index: number
|
|
50
|
+
proxyURL: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface First {
|
|
54
|
+
index: number
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface Product {
|
|
58
|
+
unitMultiplier: number
|
|
59
|
+
year: number
|
|
60
|
+
extraData: ExtraDatum[]
|
|
61
|
+
release: number
|
|
62
|
+
discount: number
|
|
63
|
+
reference: string
|
|
64
|
+
split: Split
|
|
65
|
+
collections: Collection[]
|
|
66
|
+
price: number
|
|
67
|
+
customSort: number
|
|
68
|
+
stickers: Sticker[]
|
|
69
|
+
id: string
|
|
70
|
+
stock: number
|
|
71
|
+
brand: string
|
|
72
|
+
availableTradePolicies: string[]
|
|
73
|
+
categoryTrees: CategoryTree[]
|
|
74
|
+
images: Image[]
|
|
75
|
+
locationAttributes: any[]
|
|
76
|
+
tax: number
|
|
77
|
+
productScore: number
|
|
78
|
+
storeSplitAttribute: string
|
|
79
|
+
brandID: string
|
|
80
|
+
installment: Installment
|
|
81
|
+
name: string
|
|
82
|
+
boost: Boost
|
|
83
|
+
skus: Sku[]
|
|
84
|
+
link: string
|
|
85
|
+
wear: number
|
|
86
|
+
description: string
|
|
87
|
+
showIfNotAvailable: boolean
|
|
88
|
+
clusterHighlights: ClusterHighlights
|
|
89
|
+
categories: string[]
|
|
90
|
+
timestamp: number
|
|
91
|
+
product: string
|
|
92
|
+
oldPrice: number
|
|
93
|
+
productSpecifications: any[]
|
|
94
|
+
url: string
|
|
95
|
+
measurementUnit: string
|
|
96
|
+
categoryIDS: string[]
|
|
97
|
+
textAttributes: TextAttribute[]
|
|
98
|
+
numberAttributes: NumberAttribute[]
|
|
99
|
+
headSku: string
|
|
100
|
+
specificationGroups: string
|
|
101
|
+
extraInfo: ExtraInfo
|
|
102
|
+
oldPriceText: string
|
|
103
|
+
priceText: string
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface Boost {
|
|
107
|
+
newness: number
|
|
108
|
+
image: number
|
|
109
|
+
revenue: number
|
|
110
|
+
discount: number
|
|
111
|
+
productScore: number
|
|
112
|
+
click: number
|
|
113
|
+
availableSpecsCount: number
|
|
114
|
+
promotion: number
|
|
115
|
+
order: number
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface CategoryTree {
|
|
119
|
+
categoryNames: string[]
|
|
120
|
+
categoryIDS: string[]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface ClusterHighlights {
|
|
124
|
+
the140: string
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface Collection {
|
|
128
|
+
id: string
|
|
129
|
+
position: number
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface ExtraDatum {
|
|
133
|
+
value: string
|
|
134
|
+
key: string
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface ExtraInfo {
|
|
138
|
+
sellerID: string
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface Image {
|
|
142
|
+
name: string
|
|
143
|
+
value: string
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface Installment {
|
|
147
|
+
interest: boolean
|
|
148
|
+
count: number
|
|
149
|
+
paymentGroupName: string
|
|
150
|
+
value: number
|
|
151
|
+
paymentName: string
|
|
152
|
+
valueText?: string
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface NumberAttribute {
|
|
156
|
+
labelKey: string
|
|
157
|
+
value: number
|
|
158
|
+
key: string
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface Sku {
|
|
162
|
+
images: Image[]
|
|
163
|
+
nameComplete: string
|
|
164
|
+
complementName: string
|
|
165
|
+
policies: Policy[]
|
|
166
|
+
videos: any[]
|
|
167
|
+
reference: string
|
|
168
|
+
idWithSplit: string
|
|
169
|
+
ean: string
|
|
170
|
+
name: string
|
|
171
|
+
attributes: ExtraDatum[]
|
|
172
|
+
id: string
|
|
173
|
+
sellers: Seller[]
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface Policy {
|
|
177
|
+
id: string
|
|
178
|
+
sellers: Seller[]
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface Seller {
|
|
182
|
+
default: boolean
|
|
183
|
+
oldPrice?: number
|
|
184
|
+
price?: number
|
|
185
|
+
installment?: Installment
|
|
186
|
+
name: string
|
|
187
|
+
tax: number
|
|
188
|
+
teasers: any[]
|
|
189
|
+
id: string
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface Split {
|
|
193
|
+
labelValue: string
|
|
194
|
+
labelKey: string
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface Sticker {
|
|
198
|
+
image: string
|
|
199
|
+
name: string
|
|
200
|
+
location: string
|
|
201
|
+
target: string
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface TextAttribute {
|
|
205
|
+
joinedValue: string
|
|
206
|
+
isSku: boolean
|
|
207
|
+
joinedKey: string
|
|
208
|
+
joinedKeyTranslations: JoinedTranslations
|
|
209
|
+
isFilter: boolean
|
|
210
|
+
labelValue: string
|
|
211
|
+
id: string[]
|
|
212
|
+
labelKey: string
|
|
213
|
+
value: string
|
|
214
|
+
key: string
|
|
215
|
+
joinedValueTranslations: JoinedTranslations
|
|
216
|
+
valueID?: string
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface JoinedTranslations {
|
|
220
|
+
spanish: string
|
|
221
|
+
english: string
|
|
222
|
+
italian: string
|
|
223
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { getClients } from './clients'
|
|
2
|
+
import { getLoaders } from './loaders'
|
|
3
|
+
import { StoreAggregateOffer } from './resolvers/aggregateOffer'
|
|
4
|
+
import { StoreAggregateRating } from './resolvers/aggregateRating'
|
|
5
|
+
import { StoreCollection } from './resolvers/collection'
|
|
6
|
+
import { StoreFacet } from './resolvers/facet'
|
|
7
|
+
import { StoreFacetValue } from './resolvers/facetValue'
|
|
8
|
+
import { Mutation } from './resolvers/mutation'
|
|
9
|
+
import { StoreOffer } from './resolvers/offer'
|
|
10
|
+
import { StoreProduct } from './resolvers/product'
|
|
11
|
+
import { StoreProductGroup } from './resolvers/productGroup'
|
|
12
|
+
import { Query } from './resolvers/query'
|
|
13
|
+
import { StoreReview } from './resolvers/review'
|
|
14
|
+
import { StoreSearchResult } from './resolvers/searchResult'
|
|
15
|
+
import { StoreSeo } from './resolvers/seo'
|
|
16
|
+
import type { Loaders } from './loaders'
|
|
17
|
+
import type { Clients } from './clients'
|
|
18
|
+
|
|
19
|
+
export interface Options {
|
|
20
|
+
platform: 'vtex'
|
|
21
|
+
account: string
|
|
22
|
+
environment: 'vtexcommercestable' | 'vtexcommercebeta'
|
|
23
|
+
// Default sales channel to use for fetching products
|
|
24
|
+
channel: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Context {
|
|
28
|
+
clients: Clients
|
|
29
|
+
loaders: Loaders
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type Resolver<R = unknown, A = unknown> = (
|
|
33
|
+
root: R,
|
|
34
|
+
args: A,
|
|
35
|
+
ctx: Context,
|
|
36
|
+
info: any
|
|
37
|
+
) => any
|
|
38
|
+
|
|
39
|
+
const Resolvers = {
|
|
40
|
+
StoreCollection,
|
|
41
|
+
StoreAggregateOffer,
|
|
42
|
+
StoreProduct,
|
|
43
|
+
StoreSeo,
|
|
44
|
+
StoreFacet,
|
|
45
|
+
StoreFacetValue,
|
|
46
|
+
StoreOffer,
|
|
47
|
+
StoreAggregateRating,
|
|
48
|
+
StoreReview,
|
|
49
|
+
StoreProductGroup,
|
|
50
|
+
StoreSearchResult,
|
|
51
|
+
Query,
|
|
52
|
+
Mutation,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const getContextFactory = (options: Options) => (ctx: any) => {
|
|
56
|
+
ctx.clients = getClients(options)
|
|
57
|
+
ctx.loaders = getLoaders(options, ctx.clients)
|
|
58
|
+
|
|
59
|
+
return ctx
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const getResolvers = (_: Options) => Resolvers
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getSimulationLoader } from './simulation'
|
|
2
|
+
import { getSkuLoader } from './sku'
|
|
3
|
+
import type { Options } from '..'
|
|
4
|
+
import type { Clients } from '../clients'
|
|
5
|
+
|
|
6
|
+
export type Loaders = ReturnType<typeof getLoaders>
|
|
7
|
+
|
|
8
|
+
export const getLoaders = (options: Options, clients: Clients) => {
|
|
9
|
+
const skuLoader = getSkuLoader(options, clients)
|
|
10
|
+
const simulationLoader = getSimulationLoader(options, clients)
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
skuLoader,
|
|
14
|
+
simulationLoader,
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import DataLoader from 'dataloader'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
PayloadItem,
|
|
5
|
+
Simulation,
|
|
6
|
+
} from '../clients/commerce/types/Simulation'
|
|
7
|
+
import type { Options } from '..'
|
|
8
|
+
import type { Clients } from '../clients'
|
|
9
|
+
|
|
10
|
+
export const getSimulationLoader = (_: Options, clients: Clients) => {
|
|
11
|
+
const loader = async (items: readonly PayloadItem[][]) => {
|
|
12
|
+
const simulated = await clients.commerce.checkout.simulation({
|
|
13
|
+
items: [...items.flat()],
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const itemsIndices = items.reduce(
|
|
17
|
+
(acc, curr) => [...acc, curr.length + acc[acc.length - 1]],
|
|
18
|
+
[0]
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if (simulated.items.length !== itemsIndices[itemsIndices.length - 1]) {
|
|
22
|
+
const askedItems = itemsIndices[itemsIndices.length - 1]
|
|
23
|
+
const returnedItems = simulated.items.length
|
|
24
|
+
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Simulation asked for ${askedItems}, but received ${returnedItems} items`
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return items.map((__, index) => ({
|
|
31
|
+
...simulated,
|
|
32
|
+
items: simulated.items.slice(
|
|
33
|
+
itemsIndices[index],
|
|
34
|
+
itemsIndices[index + 1]
|
|
35
|
+
),
|
|
36
|
+
}))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return new DataLoader<PayloadItem[], Simulation>(loader, {
|
|
40
|
+
maxBatchSize: 20,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import DataLoader from 'dataloader'
|
|
2
|
+
|
|
3
|
+
import { enhanceSku } from '../utils/enhanceSku'
|
|
4
|
+
import type { EnhancedSku } from '../utils/enhanceSku'
|
|
5
|
+
import type { Options } from '..'
|
|
6
|
+
import type { Clients } from '../clients'
|
|
7
|
+
import type { SelectedFacet } from '../utils/facets'
|
|
8
|
+
|
|
9
|
+
export const getSkuLoader = (_: Options, clients: Clients) => {
|
|
10
|
+
const loader = async (facetsList: readonly SelectedFacet[][]) => {
|
|
11
|
+
const skuIds = facetsList.map((facets) => {
|
|
12
|
+
const maybeFacet = facets.find(({ key }) => key === 'id')
|
|
13
|
+
|
|
14
|
+
if (!maybeFacet) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
'Error while loading SKU. Needs to pass an id to selected facets'
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return maybeFacet.value
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const indexById = skuIds.reduce(
|
|
24
|
+
(acc, id, index) => ({ ...acc, [id]: index }),
|
|
25
|
+
{} as Record<string, number>
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const { products } = await clients.search.products({
|
|
29
|
+
query: `sku:${skuIds.join(';')}`,
|
|
30
|
+
page: 0,
|
|
31
|
+
count: skuIds.length,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
if (products.length !== skuIds.length) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Sku batching went wrong. Asked for ${skuIds.length} sku(s) but search api returned ${products.length} sku(s)`
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const sorted = new Array<EnhancedSku>(products.length)
|
|
41
|
+
|
|
42
|
+
// O(n*m) sort, where n = skuIds.length and m is the number of skus per product
|
|
43
|
+
for (const product of products) {
|
|
44
|
+
const sku = product.skus.find((item) => indexById[item.id] != null)
|
|
45
|
+
|
|
46
|
+
if (sku == null) {
|
|
47
|
+
throw new Error(`Could not find sku for product ${product.id}`)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const index = indexById[sku.id]
|
|
51
|
+
|
|
52
|
+
sorted[index] = enhanceSku(sku, product)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return sorted
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return new DataLoader<SelectedFacet[], EnhancedSku>(loader, {
|
|
59
|
+
maxBatchSize: 99, // Max allowed batch size of Search API
|
|
60
|
+
})
|
|
61
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { EnhancedSku } from '../utils/enhanceSku'
|
|
2
|
+
import type { Simulation } from '../clients/commerce/types/Simulation'
|
|
3
|
+
|
|
4
|
+
type Resolvers = (root: Simulation & { product: EnhancedSku }) => unknown
|
|
5
|
+
|
|
6
|
+
export const StoreAggregateOffer: Record<string, Resolvers> = {
|
|
7
|
+
highPrice: ({ items }) =>
|
|
8
|
+
items.reduce(
|
|
9
|
+
(acc, curr) => (acc > curr.sellingPrice ? acc : curr.sellingPrice),
|
|
10
|
+
items[0]?.sellingPrice ?? 0
|
|
11
|
+
) / 1e2,
|
|
12
|
+
lowPrice: ({ items }) =>
|
|
13
|
+
items.reduce(
|
|
14
|
+
(acc, curr) => (acc < curr.sellingPrice ? acc : curr.sellingPrice),
|
|
15
|
+
items[0]?.sellingPrice ?? 0
|
|
16
|
+
) / 1e2,
|
|
17
|
+
offerCount: ({ items }) => items.length,
|
|
18
|
+
priceCurrency: () => '',
|
|
19
|
+
offers: ({ items, product }) => items.map((item) => ({ ...item, product })),
|
|
20
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Resolver } from '..'
|
|
2
|
+
import type { Brand } from '../clients/commerce/types/Brand'
|
|
3
|
+
import type { CategoryTree } from '../clients/commerce/types/CategoryTree'
|
|
4
|
+
import { slugify } from '../utils/slugify'
|
|
5
|
+
|
|
6
|
+
type Root = Brand | (CategoryTree & { level: number })
|
|
7
|
+
|
|
8
|
+
const isBrand = (x: any): x is Brand => x.type === 'brand'
|
|
9
|
+
|
|
10
|
+
export const StoreCollection: Record<string, Resolver<Root>> = {
|
|
11
|
+
id: ({ id }) => id.toString(),
|
|
12
|
+
slug: ({ name }) => slugify(name),
|
|
13
|
+
seo: (root) =>
|
|
14
|
+
isBrand(root)
|
|
15
|
+
? {
|
|
16
|
+
title: root.title,
|
|
17
|
+
description: root.metaTagDescription,
|
|
18
|
+
}
|
|
19
|
+
: {
|
|
20
|
+
title: root.Title,
|
|
21
|
+
description: root.MetaTagDescription,
|
|
22
|
+
},
|
|
23
|
+
type: (root) =>
|
|
24
|
+
isBrand(root) ? 'Brand' : root.level === 0 ? 'Department' : 'Category',
|
|
25
|
+
meta: (root) =>
|
|
26
|
+
isBrand(root)
|
|
27
|
+
? {
|
|
28
|
+
selectedFacets: [{ key: 'brand', value: slugify(root.name) }],
|
|
29
|
+
}
|
|
30
|
+
: {
|
|
31
|
+
selectedFacets: new URL(root.url).pathname
|
|
32
|
+
.slice(1)
|
|
33
|
+
.split('/')
|
|
34
|
+
.map((segment, index) => ({
|
|
35
|
+
key: `category-${index + 1}`,
|
|
36
|
+
value: slugify(segment),
|
|
37
|
+
})),
|
|
38
|
+
},
|
|
39
|
+
breadcrumbList: () => ({
|
|
40
|
+
itemListElement: [],
|
|
41
|
+
numberOfItems: 0,
|
|
42
|
+
}),
|
|
43
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Resolver } from '..'
|
|
2
|
+
import type { Attribute } from '../clients/search/types/AttributeSearchResult'
|
|
3
|
+
|
|
4
|
+
type Root = Attribute
|
|
5
|
+
|
|
6
|
+
export const StoreFacet: Record<string, Resolver<Root>> = {
|
|
7
|
+
key: ({ key }) => key,
|
|
8
|
+
label: ({ label }) => label,
|
|
9
|
+
values: ({ values }) => values,
|
|
10
|
+
type: ({ type }) => (type === 'text' ? 'BOOLEAN' : 'RANGE'),
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Resolver } from '..'
|
|
2
|
+
import type { Value } from '../clients/search/types/AttributeSearchResult'
|
|
3
|
+
|
|
4
|
+
type Root = Value
|
|
5
|
+
|
|
6
|
+
export const StoreFacetValue: Record<string, Resolver<Root>> = {
|
|
7
|
+
value: ({ key, from, to }) => key ?? `${from}-to-${to}`,
|
|
8
|
+
label: ({ label }) => label ?? 'unknown',
|
|
9
|
+
selected: ({ active }) => active,
|
|
10
|
+
quantity: ({ count }) => count,
|
|
11
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EnhancedSku } from '../utils/enhanceSku'
|
|
2
|
+
import type { Resolver } from '..'
|
|
3
|
+
import type { Item } from '../clients/commerce/types/Simulation'
|
|
4
|
+
import type { OrderFormItem } from '../clients/commerce/types/OrderForm'
|
|
5
|
+
|
|
6
|
+
type Root =
|
|
7
|
+
| (Item & { product: EnhancedSku }) // when querying search/product
|
|
8
|
+
| (OrderFormItem & { product: Promise<EnhancedSku> }) // when querying order
|
|
9
|
+
|
|
10
|
+
export const StoreOffer: Record<string, Resolver<Root>> = {
|
|
11
|
+
priceCurrency: () => '',
|
|
12
|
+
priceValidUntil: ({ priceValidUntil }) => priceValidUntil ?? '',
|
|
13
|
+
itemCondition: () => 'https://schema.org/NewCondition',
|
|
14
|
+
availability: ({ availability }) =>
|
|
15
|
+
availability === 'available'
|
|
16
|
+
? 'https://schema.org/InStock'
|
|
17
|
+
: 'https://schema.org/OutOfStock',
|
|
18
|
+
seller: ({ seller }) => ({
|
|
19
|
+
identifier: seller,
|
|
20
|
+
}),
|
|
21
|
+
price: ({ sellingPrice }) => sellingPrice / 1e2, // TODO add spot price calculation
|
|
22
|
+
sellingPrice: ({ sellingPrice }) => sellingPrice / 1e2,
|
|
23
|
+
listPrice: ({ listPrice }) => listPrice / 1e2,
|
|
24
|
+
itemOffered: ({ product }) => product,
|
|
25
|
+
quantity: ({ quantity }) => quantity,
|
|
26
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Resolver } from '..'
|
|
2
|
+
import type { EnhancedSku } from '../utils/enhanceSku'
|
|
3
|
+
|
|
4
|
+
type Root = EnhancedSku
|
|
5
|
+
|
|
6
|
+
const DEFAULT_IMAGE = {
|
|
7
|
+
name: 'image',
|
|
8
|
+
value:
|
|
9
|
+
'https://storecomponents.vtexassets.com/assets/faststore/images/image___117a6d3e229a96ad0e0d0876352566e2.svg',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const getSlug = (link: string, id: string) => `${link}-${id}`
|
|
13
|
+
const getPath = (link: string, id: string) => `/${getSlug(link, id)}/p`
|
|
14
|
+
|
|
15
|
+
export const StoreProduct: Record<string, Resolver<Root>> = {
|
|
16
|
+
productID: ({ id }) => id,
|
|
17
|
+
name: ({ isVariantOf, name }) => name ?? isVariantOf.name,
|
|
18
|
+
slug: ({ isVariantOf: { link }, id }) => getSlug(link, id),
|
|
19
|
+
description: ({ isVariantOf: { description } }) => description,
|
|
20
|
+
seo: ({ isVariantOf: { name, description } }) => ({
|
|
21
|
+
title: name,
|
|
22
|
+
description,
|
|
23
|
+
}),
|
|
24
|
+
brand: ({ isVariantOf: { brand } }) => ({ name: brand }),
|
|
25
|
+
breadcrumbList: ({ isVariantOf: { categoryTrees, name, link }, id }) => ({
|
|
26
|
+
itemListElement: [
|
|
27
|
+
...categoryTrees.map(({ categoryNames }, index) => ({
|
|
28
|
+
name: categoryNames[categoryNames.length - 1],
|
|
29
|
+
item: `/${categoryNames.join('/').toLowerCase()}`,
|
|
30
|
+
position: index + 1,
|
|
31
|
+
})),
|
|
32
|
+
{
|
|
33
|
+
name,
|
|
34
|
+
item: getPath(link, id),
|
|
35
|
+
position: categoryTrees.length + 1,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
numberOfItems: categoryTrees.length,
|
|
39
|
+
}),
|
|
40
|
+
image: ({ isVariantOf, images }) =>
|
|
41
|
+
(images ?? isVariantOf.images ?? [DEFAULT_IMAGE]).map(
|
|
42
|
+
({ name, value }) => ({
|
|
43
|
+
alternateName: name ?? '',
|
|
44
|
+
url: value.replace('vteximg.com.br', 'vtexassets.com'),
|
|
45
|
+
})
|
|
46
|
+
),
|
|
47
|
+
sku: ({
|
|
48
|
+
isVariantOf: {
|
|
49
|
+
skus: [sku],
|
|
50
|
+
},
|
|
51
|
+
}) => sku.id,
|
|
52
|
+
gtin: ({ reference }) => reference ?? '',
|
|
53
|
+
review: () => [],
|
|
54
|
+
aggregateRating: () => ({}),
|
|
55
|
+
offers: async (product, _, ctx) => {
|
|
56
|
+
const {
|
|
57
|
+
loaders: { simulationLoader },
|
|
58
|
+
} = ctx
|
|
59
|
+
|
|
60
|
+
const { sellers, id } = product
|
|
61
|
+
|
|
62
|
+
// Unique seller ids
|
|
63
|
+
const sellerIds = sellers.map((seller) => seller.id)
|
|
64
|
+
const items = Array.from(new Set(sellerIds)).map((seller) => ({
|
|
65
|
+
quantity: 1,
|
|
66
|
+
seller,
|
|
67
|
+
id,
|
|
68
|
+
}))
|
|
69
|
+
|
|
70
|
+
const simulation = await simulationLoader.load(items)
|
|
71
|
+
|
|
72
|
+
return { ...simulation, product }
|
|
73
|
+
},
|
|
74
|
+
isVariantOf: ({ isVariantOf }) => isVariantOf,
|
|
75
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { enhanceSku } from '../utils/enhanceSku'
|
|
2
|
+
import type { Product } from '../clients/search/types/ProductSearchResult'
|
|
3
|
+
import type { Resolver } from '..'
|
|
4
|
+
|
|
5
|
+
export const StoreProductGroup: Record<string, Resolver<Product>> = {
|
|
6
|
+
hasVariant: (root) => root.skus.map((sku) => enhanceSku(sku, root)),
|
|
7
|
+
productGroupID: ({ product }) => product,
|
|
8
|
+
name: ({ name }) => name,
|
|
9
|
+
}
|