mirror of
https://github.com/vercel/commerce.git
synced 2025-05-18 07:26:59 +00:00
changed shopify fetch and graphQL fetch types to include useAdminApi flag, generated shopify admin api schema, changed RemoveItemHook of commerce/types/wishlist, created shopify wishlist api routes (addItem, removeItem, getWishlist), added shopify api operation getCustomerWishlist, updated index files of api/operations, utils and api/wishlist, created shopify wishlist types created getCustomerId function as a util in shopify added customerUpdate mutation, added customer query with admin api, added getCustomerId query
This commit is contained in:
parent
7fb6734274
commit
890836bc24
21629
package-lock.json
generated
Normal file
21629
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -11,12 +11,19 @@
|
||||
"dev": "turbo run dev",
|
||||
"start": "turbo run start",
|
||||
"types": "turbo run types",
|
||||
"prettier-fix": "prettier --write ."
|
||||
"prettier-fix": "prettier --write .",
|
||||
"generate": "graphql-codegen --config codegen.yml"
|
||||
},
|
||||
"devDependencies": {
|
||||
"husky": "^7.0.4",
|
||||
"prettier": "^2.5.1",
|
||||
"turbo": "^1.1.2"
|
||||
"turbo": "^1.1.2",
|
||||
"@graphql-codegen/typescript-document-nodes": "2.2.8",
|
||||
"@graphql-codegen/typescript": "2.4.8",
|
||||
"@graphql-codegen/typescript-operations": "2.3.5",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "2.1.1",
|
||||
"@graphql-codegen/introspection": "2.1.1",
|
||||
"@graphql-codegen/cli": "2.6.2"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
@ -24,4 +31,4 @@
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@1.22.17"
|
||||
}
|
||||
}
|
@ -160,7 +160,8 @@ export interface CommerceAPIConfig {
|
||||
fetch<Data = any, Variables = any>(
|
||||
query: string,
|
||||
queryData?: CommerceAPIFetchOptions<Variables>,
|
||||
fetchOptions?: FetchOptions
|
||||
fetchOptions?: FetchOptions,
|
||||
useAdminApi?: boolean
|
||||
): Promise<GraphQLFetcherResult<Data>>
|
||||
}
|
||||
|
||||
@ -170,7 +171,8 @@ export type GraphQLFetcher<
|
||||
> = (
|
||||
query: string,
|
||||
queryData?: CommerceAPIFetchOptions<Variables>,
|
||||
fetchOptions?: FetchOptions
|
||||
fetchOptions?: FetchOptions,
|
||||
useAdminApi?: boolean
|
||||
) => Promise<Data>
|
||||
|
||||
export interface GraphQLFetcherResult<Data = any> {
|
||||
|
@ -27,11 +27,10 @@ export type AddItemHook<T extends WishlistTypes = WishlistTypes> = {
|
||||
}
|
||||
|
||||
export type RemoveItemHook<T extends WishlistTypes = WishlistTypes> = {
|
||||
data: T['wishlist'] | null
|
||||
body: { itemId: string }
|
||||
fetcherInput: { itemId: string }
|
||||
actionInput: { id: string }
|
||||
input: { wishlist?: { includeProducts?: boolean } }
|
||||
data: T['wishlist']
|
||||
body: { item: T['itemBody'] }
|
||||
fetcherInput: { item: T['itemBody'] }
|
||||
actionInput: T['itemBody']
|
||||
}
|
||||
|
||||
export type WishlistSchema<T extends WishlistTypes = WishlistTypes> = {
|
||||
|
28080
packages/shopify/admin-schema.d.ts
vendored
Normal file
28080
packages/shopify/admin-schema.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
49938
packages/shopify/admin-schema.graphql
Normal file
49938
packages/shopify/admin-schema.graphql
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,20 +1,14 @@
|
||||
{
|
||||
"schema": {
|
||||
"https://${NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/api/2021-07/graphql.json": {
|
||||
"https://${NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/admin/api/2022-01/graphql.json": {
|
||||
"headers": {
|
||||
"X-Shopify-Storefront-Access-Token": "${NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN}"
|
||||
"X-Shopify-Access-Token": "${NEXT_PUBLIC_SHOPIFY_ADMIN_ACCESS_TOKEN}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"documents": [
|
||||
{
|
||||
"./src/**/*.{ts,tsx}": {
|
||||
"noRequire": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"documents": [{}],
|
||||
"generates": {
|
||||
"./schema.d.ts": {
|
||||
"./admin-schema.d.ts": {
|
||||
"plugins": ["typescript", "typescript-operations"],
|
||||
"config": {
|
||||
"scalars": {
|
||||
@ -22,7 +16,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"./schema.graphql": {
|
||||
"./admin-schema.graphql": {
|
||||
"plugins": ["schema-ast"]
|
||||
}
|
||||
},
|
||||
|
@ -51,6 +51,7 @@
|
||||
"dependencies": {
|
||||
"@vercel/commerce": "^0.0.1",
|
||||
"@vercel/fetch": "^6.1.1",
|
||||
"graphql": "^15.8.0",
|
||||
"lodash.debounce": "^4.0.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -1 +0,0 @@
|
||||
export default function (_commerce: any) {}
|
84
packages/shopify/src/api/endpoints/wishlist/add-item.ts
Normal file
84
packages/shopify/src/api/endpoints/wishlist/add-item.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import type { WishlistEndpoint } from '.'
|
||||
import { customerUpdateMutation, getCustomerId } from '../../../utils'
|
||||
import type {
|
||||
MutationCustomerUpdateArgs,
|
||||
Mutation,
|
||||
MetafieldInput,
|
||||
} from '../../../../admin-schema'
|
||||
import { WishlistItem } from './../../../types/wishlist'
|
||||
|
||||
const addWishlistItem: WishlistEndpoint['handlers']['addItem'] = async ({
|
||||
res,
|
||||
body: { item, customerToken },
|
||||
config,
|
||||
commerce,
|
||||
}) => {
|
||||
if (!item) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Missing item!' }],
|
||||
})
|
||||
}
|
||||
|
||||
if (customerToken) {
|
||||
const customerId =
|
||||
customerToken && (await getCustomerId({ customerToken, config }))
|
||||
|
||||
if (!customerId) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Invalid request' }],
|
||||
})
|
||||
}
|
||||
|
||||
let wishlistItems: WishlistItem[] = []
|
||||
let metafieldInput: MetafieldInput = {}
|
||||
|
||||
const { wishlist } = await commerce.getCustomerWishlist({
|
||||
variables: { customerId },
|
||||
config,
|
||||
})
|
||||
|
||||
if (wishlist) {
|
||||
wishlistItems = wishlist?.items?.map(({ product, ...rest }) => {
|
||||
return rest
|
||||
})!
|
||||
metafieldInput = { id: wishlist?.id! }
|
||||
} else {
|
||||
metafieldInput = { namespace: 'my_fields', key: 'wishlist' }
|
||||
}
|
||||
|
||||
wishlistItems?.push(item)
|
||||
const jsonString = JSON.stringify(wishlistItems)
|
||||
|
||||
try {
|
||||
const {
|
||||
data: { customerUpdate },
|
||||
} = await config.fetch<Mutation, MutationCustomerUpdateArgs>(
|
||||
customerUpdateMutation,
|
||||
{
|
||||
variables: {
|
||||
input: {
|
||||
id: customerId,
|
||||
metafields: [
|
||||
{
|
||||
...metafieldInput,
|
||||
value: jsonString,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{},
|
||||
true
|
||||
)
|
||||
|
||||
return res.status(200).json({ data: customerUpdate?.customer! })
|
||||
} catch (error: any) {
|
||||
console.log(error)
|
||||
res.status(500).json({ data: null, errors: [{ message: error }] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default addWishlistItem
|
40
packages/shopify/src/api/endpoints/wishlist/get-wishlist.ts
Normal file
40
packages/shopify/src/api/endpoints/wishlist/get-wishlist.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import type { Wishlist } from '../../../types/wishlist'
|
||||
import type { WishlistEndpoint } from '.'
|
||||
import { getCustomerId } from '../../../utils'
|
||||
import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../../../const'
|
||||
|
||||
// Return wishlist info
|
||||
const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
|
||||
req,
|
||||
res,
|
||||
config,
|
||||
commerce,
|
||||
}) => {
|
||||
const { cookies } = req
|
||||
const customerToken = cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE]
|
||||
let result: { data?: Wishlist } = {}
|
||||
|
||||
if (customerToken) {
|
||||
const customerId =
|
||||
customerToken && (await getCustomerId({ customerToken, config }))
|
||||
|
||||
if (!customerId) {
|
||||
// If the customerToken is invalid, then this request is too
|
||||
return res.status(404).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Wishlist not found' }],
|
||||
})
|
||||
}
|
||||
|
||||
const { wishlist } = await commerce.getCustomerWishlist({
|
||||
variables: { customerId },
|
||||
config,
|
||||
})
|
||||
|
||||
result = { data: wishlist }
|
||||
}
|
||||
|
||||
res.status(200).json({ data: result.data ?? null })
|
||||
}
|
||||
|
||||
export default getWishlist
|
24
packages/shopify/src/api/endpoints/wishlist/index.ts
Normal file
24
packages/shopify/src/api/endpoints/wishlist/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
|
||||
import wishlistEndpoint from '@vercel/commerce/api/endpoints/wishlist'
|
||||
import type { WishlistSchema } from '../../../types/wishlist'
|
||||
import type { ShopifyAPI } from '../..'
|
||||
import addItem from './add-item'
|
||||
import removeItem from './remove-item'
|
||||
import getWishlist from './get-wishlist'
|
||||
|
||||
export type WishlistAPI = GetAPISchema<ShopifyAPI, WishlistSchema>
|
||||
|
||||
export type WishlistEndpoint = WishlistAPI['endpoint']
|
||||
|
||||
export const handlers: WishlistEndpoint['handlers'] = {
|
||||
getWishlist,
|
||||
addItem,
|
||||
removeItem,
|
||||
}
|
||||
|
||||
const wishlistApi = createEndpoint<WishlistAPI>({
|
||||
handler: wishlistEndpoint,
|
||||
handlers,
|
||||
})
|
||||
|
||||
export default wishlistApi
|
76
packages/shopify/src/api/endpoints/wishlist/remove-item.ts
Normal file
76
packages/shopify/src/api/endpoints/wishlist/remove-item.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { WishlistItemBody } from './../../../../../commerce/src/types/wishlist'
|
||||
import type { WishlistEndpoint } from '.'
|
||||
import { customerUpdateMutation, getCustomerId } from '../../../utils'
|
||||
import type {
|
||||
MutationCustomerUpdateArgs,
|
||||
Mutation,
|
||||
} from '../../../../admin-schema'
|
||||
|
||||
const removeWishlistItem: WishlistEndpoint['handlers']['removeItem'] = async ({
|
||||
res,
|
||||
body: { item, customerToken },
|
||||
config,
|
||||
commerce,
|
||||
}) => {
|
||||
if (!item) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Missing item!' }],
|
||||
})
|
||||
}
|
||||
|
||||
if (customerToken) {
|
||||
const customerId =
|
||||
customerToken && (await getCustomerId({ customerToken, config }))
|
||||
|
||||
if (!customerId) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Invalid request' }],
|
||||
})
|
||||
}
|
||||
|
||||
const { wishlist } = await commerce.getCustomerWishlist({
|
||||
variables: { customerId },
|
||||
config,
|
||||
})
|
||||
|
||||
const WishlistItems: WishlistItemBody[] = wishlist?.items
|
||||
?.filter((wishlistItem) => wishlistItem.productId !== item.productId)
|
||||
.map(({ product, ...rest }) => {
|
||||
return rest
|
||||
})!
|
||||
|
||||
const jsonString = JSON.stringify(WishlistItems)
|
||||
|
||||
try {
|
||||
const {
|
||||
data: { customerUpdate },
|
||||
} = await config.fetch<Mutation, MutationCustomerUpdateArgs>(
|
||||
customerUpdateMutation,
|
||||
{
|
||||
variables: {
|
||||
input: {
|
||||
id: customerId,
|
||||
metafields: [
|
||||
{
|
||||
id: wishlist?.id,
|
||||
value: jsonString,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{},
|
||||
true
|
||||
)
|
||||
|
||||
return res.status(200).json({ data: customerUpdate?.customer! })
|
||||
} catch (error: any) {
|
||||
console.log(error)
|
||||
res.status(500).json({ data: null, errors: [{ message: error }] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default removeWishlistItem
|
92
packages/shopify/src/api/operations/get-customer-wishlist.ts
Normal file
92
packages/shopify/src/api/operations/get-customer-wishlist.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { ShopifyConfig, Provider } from './../index'
|
||||
import { getCustomerAdminQuery } from '../../utils/queries'
|
||||
import type {
|
||||
OperationContext,
|
||||
OperationOptions,
|
||||
} from '@vercel/commerce/api/operations'
|
||||
import type {
|
||||
GetCustomerWishlistOperation,
|
||||
Wishlist,
|
||||
} from '../../types/wishlist'
|
||||
import { Product } from '../../types/product'
|
||||
|
||||
export default function getCustomerWishlistOperation({
|
||||
commerce,
|
||||
}: OperationContext<Provider>) {
|
||||
async function getCustomerWishlist<
|
||||
T extends GetCustomerWishlistOperation
|
||||
>(opts: {
|
||||
variables: T['variables']
|
||||
config?: ShopifyConfig
|
||||
includeProducts?: boolean
|
||||
}): Promise<T['data']>
|
||||
|
||||
async function getCustomerWishlist<T extends GetCustomerWishlistOperation>(
|
||||
opts: {
|
||||
variables: T['variables']
|
||||
config?: ShopifyConfig
|
||||
includeProducts?: boolean
|
||||
} & OperationOptions
|
||||
): Promise<T['data']>
|
||||
|
||||
async function getCustomerWishlist<T extends GetCustomerWishlistOperation>({
|
||||
query = getCustomerAdminQuery,
|
||||
config,
|
||||
variables,
|
||||
includeProducts,
|
||||
}: {
|
||||
query?: string
|
||||
variables: T['variables']
|
||||
config?: ShopifyConfig
|
||||
includeProducts?: boolean
|
||||
}): Promise<T['data']> {
|
||||
const { fetch } = commerce.getConfig(config)
|
||||
const { data } = await fetch(
|
||||
query,
|
||||
{
|
||||
variables: {
|
||||
id: variables.customerId,
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
|
||||
if (!data.customer.metafield) {
|
||||
return data
|
||||
}
|
||||
|
||||
const tempWishlist =
|
||||
data.customer.metafield && JSON.parse(data.customer?.metafield?.value!)
|
||||
|
||||
const wishlist: Wishlist = {
|
||||
id: String(data.customer?.metafield?.id!),
|
||||
items: tempWishlist,
|
||||
}
|
||||
|
||||
const { products } = await commerce.getAllProducts({
|
||||
variables: { first: 250 },
|
||||
config,
|
||||
})
|
||||
|
||||
const wishlistItems = products.filter((item: Product) =>
|
||||
wishlist.items?.find(({ productId }) => item.id === productId)
|
||||
)
|
||||
|
||||
wishlist.items?.forEach((item) => {
|
||||
const product =
|
||||
item &&
|
||||
wishlistItems.find((wishlistItem) => wishlistItem.id === item.productId)
|
||||
|
||||
if (item && product) {
|
||||
item.product = product as any
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
wishlist,
|
||||
}
|
||||
}
|
||||
|
||||
return getCustomerWishlist
|
||||
}
|
@ -5,3 +5,4 @@ export { default as getAllProductPaths } from './get-all-product-paths'
|
||||
export { default as getProduct } from './get-product'
|
||||
export { default as getSiteInfo } from './get-site-info'
|
||||
export { default as login } from './login'
|
||||
export { default as getCustomerWishlist } from './get-customer-wishlist'
|
||||
|
@ -1 +1,34 @@
|
||||
import { Product } from '@vercel/commerce/types/product'
|
||||
import * as Core from '@vercel/commerce/types/wishlist'
|
||||
import { Customer } from '../../admin-schema'
|
||||
|
||||
export * from '@vercel/commerce/types/wishlist'
|
||||
|
||||
export type WishlistItem = NonNullable<Core.WishlistItemBody> & {
|
||||
product?: Product
|
||||
}
|
||||
|
||||
export type Wishlist = {
|
||||
id: string
|
||||
items?: WishlistItem[]
|
||||
}
|
||||
|
||||
export type WishlistTypes = {
|
||||
wishlist: Wishlist
|
||||
itemBody: Core.WishlistItemBody
|
||||
customer: Customer
|
||||
}
|
||||
|
||||
export type RemoveItemHook<T extends WishlistTypes = WishlistTypes> = {
|
||||
body: { item: T['itemBody'] }
|
||||
fetcherInput: { item: T['itemBody'] }
|
||||
actionInput: T['itemBody']
|
||||
}
|
||||
|
||||
export type WishlistSchema = Core.WishlistSchema<WishlistTypes>
|
||||
export type GetCustomerWishlistOperation =
|
||||
Core.GetCustomerWishlistOperation<WishlistTypes>
|
||||
|
||||
export type GetWishlistHook = Core.GetWishlistHook<WishlistTypes>
|
||||
|
||||
export * from '@vercel/commerce/types/wishlist'
|
||||
|
31
packages/shopify/src/utils/get-customer-id.ts
Normal file
31
packages/shopify/src/utils/get-customer-id.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { ShopifyConfig } from '../api'
|
||||
import { getCustomerIdQuery } from './queries'
|
||||
import type {
|
||||
GetCustomerIdQuery,
|
||||
GetCustomerQueryVariables,
|
||||
} from '../../schema'
|
||||
|
||||
export type customerId = {
|
||||
id: string
|
||||
}
|
||||
|
||||
const getCustomerId = async ({
|
||||
config,
|
||||
customerToken,
|
||||
}: {
|
||||
config: ShopifyConfig
|
||||
customerToken: string
|
||||
}): Promise<string | undefined | null> => {
|
||||
const { data } = await config.fetch<
|
||||
GetCustomerIdQuery,
|
||||
GetCustomerQueryVariables
|
||||
>(getCustomerIdQuery, {
|
||||
variables: {
|
||||
customerAccessToken: customerToken,
|
||||
},
|
||||
})
|
||||
|
||||
return String(data.customer?.id)
|
||||
}
|
||||
|
||||
export default getCustomerId
|
@ -2,6 +2,7 @@ export { default as handleFetchResponse } from './handle-fetch-response'
|
||||
export { default as getSearchVariables } from './get-search-variables'
|
||||
export { default as getSortVariables } from './get-sort-variables'
|
||||
export { default as getBrands } from './get-brands'
|
||||
export { default as getCustomerId } from './get-customer-id'
|
||||
export { default as getCategories } from './get-categories'
|
||||
export { default as getCheckoutId } from './get-checkout-id'
|
||||
export { default as checkoutCreate } from './checkout-create'
|
||||
|
22
packages/shopify/src/utils/mutations/customer-update.ts
Normal file
22
packages/shopify/src/utils/mutations/customer-update.ts
Normal file
@ -0,0 +1,22 @@
|
||||
const customerUpdateMutation = /* GraphQL */ `
|
||||
mutation customerUpdate($input: CustomerInput!) {
|
||||
customerUpdate(input: $input) {
|
||||
customer {
|
||||
id
|
||||
}
|
||||
userErrors {
|
||||
field
|
||||
message
|
||||
}
|
||||
customer {
|
||||
id
|
||||
metafield(key: "my_fields", namespace: "wishlist") {
|
||||
id
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export default customerUpdateMutation
|
@ -7,3 +7,4 @@ export { default as customerAccessTokenCreateMutation } from './customer-access-
|
||||
export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete'
|
||||
export { default as customerActivateMutation } from './customer-activate'
|
||||
export { default as customerActivateByUrlMutation } from './customer-activate-by-url'
|
||||
export { default as customerUpdateMutation } from './customer-update'
|
||||
|
@ -0,0 +1,11 @@
|
||||
export const getCustomerAdminQuery = /* GraphQL */ `
|
||||
query getCustomerAdmin($id: ID!) {
|
||||
customer(id: $id) {
|
||||
metafield(namespace: "my_fields", key: "wishlist") {
|
||||
id
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
export default getCustomerAdminQuery
|
@ -0,0 +1,8 @@
|
||||
export const getCustomerIdQuery = /* GraphQL */ `
|
||||
query getCustomerId($customerAccessToken: String!) {
|
||||
customer(customerAccessToken: $customerAccessToken) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
export default getCustomerIdQuery
|
@ -9,3 +9,5 @@ export { default as getAllPagesQuery } from './get-all-pages-query'
|
||||
export { default as getPageQuery } from './get-page-query'
|
||||
export { default as getCustomerQuery } from './get-customer-query'
|
||||
export { default as getSiteInfoQuery } from './get-site-info-query'
|
||||
export { default as getCustomerAdminQuery } from './get-customer-admin-query'
|
||||
export { default as getCustomerIdQuery } from './get-customer-id-query'
|
||||
|
Loading…
x
Reference in New Issue
Block a user