[WIP] Node.js provider for the API (#252)

* Adding multiple initial files

* Updated the default cart endpoint

* Fixes

* Updated CommerceAPI class for better usage

* Adding more migration changes

* Taking multiple steps into better API types

* Adding more experimental types

* Removed many testing types

* Adding types, fixes and other updates

* Updated commerce types

* Updated types for hooks now using the API

* Updated mutation types

* Simplified cart types for the provider

* Updated cart hooks

* Remove normalizers from the hooks

* Updated cart endpoint

* Removed cart handlers

* bug fixes

* Improve quantity input behavior in cart item

* Removed endpoints folder

* Making progress on api operations

* Moved method

* Moved types

* Changed the way ops are created

* Added customer endpoint

* Login endpoint

* Added logout endpoint

* Add missing logout files

* Added signup endpoint

* Removed customers old endpoints

* Moved endpoints to nested folder

* Removed old customer endpoint builders

* Updated login operation

* Updated login operation

* Added getAllPages operation

* Renamed endpoint operations to handlers

* Changed import

* Renamed operations to handlers in usage

* Moved getAllPages everywhere

* Moved getPage

* Updated getPage usage

* Moved getSiteInfo

* Added def types for product

* Updated type

* moved products catalog endpoint

* removed old catalog endpoint

* Moved wishlist

* Removed commerce.endpoint

* Replaced references to commerce.endpoint

* Updated catalog products

* Moved checkout api

* Added the get customer wishlist operation

* Removed old wishlist stuff

* Added getAllProductPaths operation

* updated reference to operation

* Moved getAllProducts

* Updated getProduct operation

* Removed old getConfig and references

* Removed is-allowed-method from BC

* Updated types for auth hooks

* Updated useCustomer and core types

* Updated useData and util hooks

* Updated useSearch hook

* Updated types for useWishlist

* Added index for types

* Fixes

* Updated urls to the API

* Renamed fetchInput to fetcherInput

* Updated fetch type

* Fixes in search hook

* Updated Shopify Provider Structure (#340)

* Add codegen, update fragments & schemas

* Update checkout-create.ts

* Update checkout-create.ts

* Update README.md

* Update product mutations & queries

* Uptate customer fetch types

* Update schemas

* Start updates

* Moved Page, AllPages & Site Info

* Moved product, all products (paths)

* Add translations, update operations & fixes

* Update api endpoints, types & fixes

* Add api checkout endpoint

* Updates

* Fixes

* Update commerce.config.json

Co-authored-by: B <curciobelen@gmail.com>

* Added category type and normalizer

* updated init script to exclude other providers

* Excluded swell and venture temporarily

* Fix category & color normalization

* Fixed category normalizer in shopify

* Don't use getSlug for category on /search

* Update colors.ts

Co-authored-by: cond0r <pinte_catalin@yahoo.com>
Co-authored-by: B <curciobelen@gmail.com>
This commit is contained in:
Luis Alvarez D
2021-06-01 03:18:10 -05:00
committed by GitHub
parent 0792eabd4c
commit a98c95d447
249 changed files with 4646 additions and 2981 deletions

View File

@@ -121,3 +121,15 @@ const pages = await getAllPages({
config,
})
```
## Code generation
This provider makes use of GraphQL code generation. The [schema.graphql](./schema.graphql) and [schema.d.ts](./schema.d.ts) files contain the generated types & schema introspection results.
When developing the provider, changes to any GraphQL operations should be followed by re-generation of the types and schema files:
From the project root dir, run:
```sh
yarn generate:shopify
```

View File

@@ -1 +0,0 @@
export default function () {}

View File

@@ -1 +0,0 @@
export default function () {}

View File

@@ -1 +0,0 @@
export default function () {}

View File

@@ -1 +0,0 @@
export default function () {}

View File

@@ -1 +0,0 @@
export default function () {}

View File

@@ -1 +0,0 @@
export default function () {}

View File

@@ -1 +0,0 @@
export default function () {}

View File

@@ -1 +0,0 @@
export default function () {}

View File

@@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@@ -1,24 +1,16 @@
import isAllowedMethod from '../utils/is-allowed-method'
import createApiHandler, {
ShopifyApiHandler,
} from '../utils/create-api-handler'
import {
SHOPIFY_CHECKOUT_ID_COOKIE,
SHOPIFY_CHECKOUT_URL_COOKIE,
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
} from '../../const'
import { getConfig } from '..'
import associateCustomerWithCheckoutMutation from '../../utils/mutations/associate-customer-with-checkout'
const METHODS = ['GET']
const checkoutApi: ShopifyApiHandler<any> = async (req, res, config) => {
if (!isAllowedMethod(req, res, METHODS)) return
config = getConfig()
} from '../../../const'
import associateCustomerWithCheckoutMutation from '../../../utils/mutations/associate-customer-with-checkout'
import type { CheckoutEndpoint } from '.'
const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({
req,
res,
config,
}) => {
const { cookies } = req
const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE]
const customerCookie = cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE]
@@ -43,4 +35,4 @@ const checkoutApi: ShopifyApiHandler<any> = async (req, res, config) => {
}
}
export default createApiHandler(checkoutApi, {}, {})
export default checkout

View File

@@ -0,0 +1,18 @@
import { GetAPISchema, createEndpoint } from '@commerce/api'
import checkoutEndpoint from '@commerce/api/endpoints/checkout'
import type { CheckoutSchema } from '../../../types/checkout'
import type { ShopifyAPI } from '../..'
import checkout from './checkout'
export type CheckoutAPI = GetAPISchema<ShopifyAPI, CheckoutSchema>
export type CheckoutEndpoint = CheckoutAPI['endpoint']
export const handlers: CheckoutEndpoint['handlers'] = { checkout }
const checkoutApi = createEndpoint<CheckoutAPI>({
handler: checkoutEndpoint,
handlers,
})
export default checkoutApi

View File

@@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@@ -0,0 +1 @@
export default function (_commerce: any) {}

View File

@@ -1,12 +1,20 @@
import type { CommerceAPIConfig } from '@commerce/api'
import {
CommerceAPI,
CommerceAPIConfig,
getCommerceApi as commerceApi,
} from '@commerce/api'
import {
API_URL,
API_TOKEN,
SHOPIFY_CHECKOUT_ID_COOKIE,
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
SHOPIFY_CHECKOUT_ID_COOKIE,
} from '../const'
import fetchGraphqlApi from './utils/fetch-graphql-api'
import * as operations from './operations'
if (!API_URL) {
throw new Error(
`The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store`
@@ -18,44 +26,30 @@ if (!API_TOKEN) {
`The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store`
)
}
import fetchGraphqlApi from './utils/fetch-graphql-api'
export interface ShopifyConfig extends CommerceAPIConfig {}
export class Config {
private config: ShopifyConfig
const ONE_DAY = 60 * 60 * 24
constructor(config: ShopifyConfig) {
this.config = config
}
getConfig(userConfig: Partial<ShopifyConfig> = {}) {
return Object.entries(userConfig).reduce<ShopifyConfig>(
(cfg, [key, value]) => Object.assign(cfg, { [key]: value }),
{ ...this.config }
)
}
setConfig(newConfig: Partial<ShopifyConfig>) {
Object.assign(this.config, newConfig)
}
}
const config = new Config({
locale: 'en-US',
const config: ShopifyConfig = {
commerceUrl: API_URL,
apiToken: API_TOKEN!,
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
cartCookieMaxAge: 60 * 60 * 24 * 30,
fetch: fetchGraphqlApi,
apiToken: API_TOKEN,
customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE,
})
export function getConfig(userConfig?: Partial<ShopifyConfig>) {
return config.getConfig(userConfig)
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
cartCookieMaxAge: ONE_DAY * 30,
fetch: fetchGraphqlApi,
}
export function setConfig(newConfig: Partial<ShopifyConfig>) {
return config.setConfig(newConfig)
export const provider = {
config,
operations,
}
export type Provider = typeof provider
export type ShopifyAPI<P extends Provider = Provider> = CommerceAPI<P>
export function getCommerceApi<P extends Provider>(
customProvider: P = provider as any
): ShopifyAPI<P> {
return commerceApi(customProvider)
}

View File

@@ -0,0 +1,67 @@
import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import {
GetAllPagesQuery,
GetAllPagesQueryVariables,
PageEdge,
} from '../../schema'
import { normalizePages } from '../../utils'
import type { ShopifyConfig, Provider } from '..'
import type { GetAllPagesOperation, Page } from '../../types/page'
import getAllPagesQuery from '../../utils/queries/get-all-pages-query'
export default function getAllPagesOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllPages<T extends GetAllPagesOperation>(opts?: {
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']>
async function getAllPages<T extends GetAllPagesOperation>(
opts: {
config?: Partial<ShopifyConfig>
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getAllPages<T extends GetAllPagesOperation>({
query = getAllPagesQuery,
config,
variables,
}: {
url?: string
config?: Partial<ShopifyConfig>
variables?: GetAllPagesQueryVariables
preview?: boolean
query?: string
} = {}): Promise<T['data']> {
const { fetch, locale, locales = ['en-US'] } = commerce.getConfig(config)
const { data } = await fetch<GetAllPagesQuery, GetAllPagesQueryVariables>(
query,
{
variables,
},
{
...(locale && {
headers: {
'Accept-Language': locale,
},
}),
}
)
return {
pages: locales.reduce<Page[]>(
(arr, locale) =>
arr.concat(normalizePages(data.pages.edges as PageEdge[], locale)),
[]
),
}
}
return getAllPages
}

View File

@@ -0,0 +1,55 @@
import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import { GetAllProductPathsOperation } from '../../types/product'
import {
GetAllProductPathsQuery,
GetAllProductPathsQueryVariables,
ProductEdge,
} from '../../schema'
import type { ShopifyConfig, Provider } from '..'
import { getAllProductsQuery } from '../../utils'
export default function getAllProductPathsOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllProductPaths<
T extends GetAllProductPathsOperation
>(opts?: {
variables?: T['variables']
config?: ShopifyConfig
}): Promise<T['data']>
async function getAllProductPaths<T extends GetAllProductPathsOperation>(
opts: {
variables?: T['variables']
config?: ShopifyConfig
} & OperationOptions
): Promise<T['data']>
async function getAllProductPaths<T extends GetAllProductPathsOperation>({
query = getAllProductsQuery,
config,
variables,
}: {
query?: string
config?: ShopifyConfig
variables?: T['variables']
} = {}): Promise<T['data']> {
config = commerce.getConfig(config)
const { data } = await config.fetch<
GetAllProductPathsQuery,
GetAllProductPathsQueryVariables
>(query, { variables })
return {
products: data.products.edges.map(({ node: { handle } }) => ({
path: `/${handle}`,
})),
}
}
return getAllProductPaths
}

View File

@@ -0,0 +1,67 @@
import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import { GetAllProductsOperation } from '../../types/product'
import {
GetAllProductsQuery,
GetAllProductsQueryVariables,
Product as ShopifyProduct,
} from '../../schema'
import type { ShopifyConfig, Provider } from '..'
import getAllProductsQuery from '../../utils/queries/get-all-products-query'
import { normalizeProduct } from '../../utils'
export default function getAllProductsOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllProducts<T extends GetAllProductsOperation>(opts?: {
variables?: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']>
async function getAllProducts<T extends GetAllProductsOperation>(
opts: {
variables?: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getAllProducts<T extends GetAllProductsOperation>({
query = getAllProductsQuery,
variables,
config,
}: {
query?: string
variables?: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
} = {}): Promise<T['data']> {
const { fetch, locale } = commerce.getConfig(config)
const { data } = await fetch<
GetAllProductsQuery,
GetAllProductsQueryVariables
>(
query,
{ variables },
{
...(locale && {
headers: {
'Accept-Language': locale,
},
}),
}
)
return {
products: data.products.edges.map(({ node }) =>
normalizeProduct(node as ShopifyProduct)
),
}
}
return getAllProducts
}

View File

@@ -0,0 +1,64 @@
import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import { normalizePage } from '../../utils'
import type { ShopifyConfig, Provider } from '..'
import {
GetPageQuery,
GetPageQueryVariables,
Page as ShopifyPage,
} from '../../schema'
import { GetPageOperation } from '../../types/page'
import getPageQuery from '../../utils/queries/get-page-query'
export default function getPageOperation({
commerce,
}: OperationContext<Provider>) {
async function getPage<T extends GetPageOperation>(opts: {
variables: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']>
async function getPage<T extends GetPageOperation>(
opts: {
variables: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getPage<T extends GetPageOperation>({
query = getPageQuery,
variables,
config,
}: {
query?: string
variables: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']> {
const { fetch, locale = 'en-US' } = commerce.getConfig(config)
const {
data: { node: page },
} = await fetch<GetPageQuery, GetPageQueryVariables>(
query,
{
variables,
},
{
...(locale && {
headers: {
'Accept-Language': locale,
},
}),
}
)
return page ? { page: normalizePage(page as ShopifyPage, locale) } : {}
}
return getPage
}

View File

@@ -0,0 +1,63 @@
import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import { GetProductOperation } from '../../types/product'
import { normalizeProduct, getProductQuery } from '../../utils'
import type { ShopifyConfig, Provider } from '..'
import { GetProductBySlugQuery, Product as ShopifyProduct } from '../../schema'
export default function getProductOperation({
commerce,
}: OperationContext<Provider>) {
async function getProduct<T extends GetProductOperation>(opts: {
variables: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']>
async function getProduct<T extends GetProductOperation>(
opts: {
variables: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getProduct<T extends GetProductOperation>({
query = getProductQuery,
variables,
config: cfg,
}: {
query?: string
variables: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']> {
const { fetch, locale } = commerce.getConfig(cfg)
const {
data: { productByHandle },
} = await fetch<GetProductBySlugQuery>(
query,
{
variables,
},
{
...(locale && {
headers: {
'Accept-Language': locale,
},
}),
}
)
return {
...(productByHandle && {
product: normalizeProduct(productByHandle as ShopifyProduct),
}),
}
}
return getProduct
}

View File

@@ -0,0 +1,62 @@
import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import { GetSiteInfoQueryVariables } from '../../schema'
import type { ShopifyConfig, Provider } from '..'
import { GetSiteInfoOperation } from '../../types/site'
import { getCategories, getBrands, getSiteInfoQuery } from '../../utils'
export default function getSiteInfoOperation({
commerce,
}: OperationContext<Provider>) {
async function getSiteInfo<T extends GetSiteInfoOperation>(opts?: {
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']>
async function getSiteInfo<T extends GetSiteInfoOperation>(
opts: {
config?: Partial<ShopifyConfig>
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getSiteInfo<T extends GetSiteInfoOperation>({
query = getSiteInfoQuery,
config,
variables,
}: {
query?: string
config?: Partial<ShopifyConfig>
preview?: boolean
variables?: GetSiteInfoQueryVariables
} = {}): Promise<T['data']> {
const cfg = commerce.getConfig(config)
const categories = await getCategories(cfg)
const brands = await getBrands(cfg)
/*
const { fetch, locale } = cfg
const { data } = await fetch<GetSiteInfoQuery, GetSiteInfoQueryVariables>(
query,
{ variables },
{
...(locale && {
headers: {
'Accept-Language': locale,
},
}),
}
)
*/
return {
categories,
brands,
}
}
return getSiteInfo
}

View File

@@ -0,0 +1,7 @@
export { default as getAllPages } from './get-all-pages'
export { default as getPage } from './get-page'
export { default as getAllProducts } from './get-all-products'
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'

View File

@@ -0,0 +1,48 @@
import type { ServerResponse } from 'http'
import type { OperationContext } from '@commerce/api/operations'
import type { LoginOperation } from '../../types/login'
import type { ShopifyConfig, Provider } from '..'
import {
customerAccessTokenCreateMutation,
setCustomerToken,
throwUserErrors,
} from '../../utils'
import { CustomerAccessTokenCreateMutation } from '../../schema'
export default function loginOperation({
commerce,
}: OperationContext<Provider>) {
async function login<T extends LoginOperation>({
query = customerAccessTokenCreateMutation,
variables,
config,
}: {
query?: string
variables: T['variables']
res: ServerResponse
config?: ShopifyConfig
}): Promise<T['data']> {
config = commerce.getConfig(config)
const {
data: { customerAccessTokenCreate },
} = await config.fetch<CustomerAccessTokenCreateMutation>(query, {
variables,
})
throwUserErrors(customerAccessTokenCreate?.customerUserErrors)
const customerAccessToken = customerAccessTokenCreate?.customerAccessToken
const accessToken = customerAccessToken?.accessToken
if (accessToken) {
setCustomerToken(accessToken)
}
return {
result: customerAccessToken?.accessToken,
}
}
return login
}

View File

@@ -1,58 +0,0 @@
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
import { ShopifyConfig, getConfig } from '..'
export type ShopifyApiHandler<
T = any,
H extends ShopifyHandlers = {},
Options extends {} = {}
> = (
req: NextApiRequest,
res: NextApiResponse<ShopifyApiResponse<T>>,
config: ShopifyConfig,
handlers: H,
// Custom configs that may be used by a particular handler
options: Options
) => void | Promise<void>
export type ShopifyHandler<T = any, Body = null> = (options: {
req: NextApiRequest
res: NextApiResponse<ShopifyApiResponse<T>>
config: ShopifyConfig
body: Body
}) => void | Promise<void>
export type ShopifyHandlers<T = any> = {
[k: string]: ShopifyHandler<T, any>
}
export type ShopifyApiResponse<T> = {
data: T | null
errors?: { message: string; code?: string }[]
}
export default function createApiHandler<
T = any,
H extends ShopifyHandlers = {},
Options extends {} = {}
>(
handler: ShopifyApiHandler<T, H, Options>,
handlers: H,
defaultOptions: Options
) {
return function getApiHandler({
config,
operations,
options,
}: {
config?: ShopifyConfig
operations?: Partial<H>
options?: Options extends {} ? Partial<Options> : never
} = {}): NextApiHandler {
const ops = { ...operations, ...handlers }
const opts = { ...defaultOptions, ...options }
return function apiHandler(req, res) {
return handler(req, res, getConfig(config), ops, opts)
}
}
}

View File

@@ -1,41 +0,0 @@
import { ProductEdge } from '../../schema'
import { ShopifyConfig } from '..'
const fetchAllProducts = async ({
config,
query,
variables,
acc = [],
cursor,
}: {
config: ShopifyConfig
query: string
acc?: ProductEdge[]
variables?: any
cursor?: string
}): Promise<ProductEdge[]> => {
const { data } = await config.fetch(query, {
variables: { ...variables, cursor },
})
const edges: ProductEdge[] = data.products?.edges ?? []
const hasNextPage = data.products?.pageInfo?.hasNextPage
acc = acc.concat(edges)
if (hasNextPage) {
const cursor = edges.pop()?.cursor
if (cursor) {
return fetchAllProducts({
config,
query,
variables,
acc,
cursor,
})
}
}
return acc
}
export default fetchAllProducts

View File

@@ -1,28 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next'
export default function isAllowedMethod(
req: NextApiRequest,
res: NextApiResponse,
allowedMethods: string[]
) {
const methods = allowedMethods.includes('OPTIONS')
? allowedMethods
: [...allowedMethods, 'OPTIONS']
if (!req.method || !methods.includes(req.method)) {
res.status(405)
res.setHeader('Allow', methods.join(', '))
res.end()
return false
}
if (req.method === 'OPTIONS') {
res.status(200)
res.setHeader('Allow', methods.join(', '))
res.setHeader('Content-Length', '0')
res.end()
return false
}
return true
}

View File

@@ -1,2 +0,0 @@
export type WishlistItem = { product: any; id: number }
export default function () {}

View File

@@ -1,31 +1,22 @@
import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types'
import { CommerceError, ValidationError } from '@commerce/utils/errors'
import useCustomer from '../customer/use-customer'
import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create'
import {
CustomerAccessTokenCreateInput,
CustomerUserError,
Mutation,
MutationCheckoutCreateArgs,
} from '../schema'
import { CommerceError } from '@commerce/utils/errors'
import useLogin, { UseLogin } from '@commerce/auth/use-login'
import { setCustomerToken, throwUserErrors } from '../utils'
import type { LoginHook } from '../types/login'
import useCustomer from '../customer/use-customer'
import {
setCustomerToken,
throwUserErrors,
customerAccessTokenCreateMutation,
} from '../utils'
import { Mutation, MutationCustomerAccessTokenCreateArgs } from '../schema'
export default useLogin as UseLogin<typeof handler>
const getErrorMessage = ({ code, message }: CustomerUserError) => {
switch (code) {
case 'UNIDENTIFIED_CUSTOMER':
message = 'Cannot find an account that matches the provided credentials'
break
}
return message
}
export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = {
export const handler: MutationHook<LoginHook> = {
fetchOptions: {
query: createCustomerAccessTokenMutation,
query: customerAccessTokenCreateMutation,
},
async fetcher({ input: { email, password }, options, fetch }) {
if (!(email && password)) {
@@ -37,7 +28,7 @@ export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = {
const { customerAccessTokenCreate } = await fetch<
Mutation,
MutationCheckoutCreateArgs
MutationCustomerAccessTokenCreateArgs
>({
...options,
variables: {

View File

@@ -1,13 +1,14 @@
import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types'
import useLogout, { UseLogout } from '@commerce/auth/use-logout'
import type { LogoutHook } from '../types/logout'
import useCustomer from '../customer/use-customer'
import customerAccessTokenDeleteMutation from '../utils/mutations/customer-access-token-delete'
import { getCustomerToken, setCustomerToken } from '../utils/customer-token'
export default useLogout as UseLogout<typeof handler>
export const handler: MutationHook<null> = {
export const handler: MutationHook<LogoutHook> = {
fetchOptions: {
query: customerAccessTokenDeleteMutation,
},

View File

@@ -1,25 +1,20 @@
import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types'
import { CommerceError, ValidationError } from '@commerce/utils/errors'
import { CommerceError } from '@commerce/utils/errors'
import useSignup, { UseSignup } from '@commerce/auth/use-signup'
import type { SignupHook } from '../types/signup'
import useCustomer from '../customer/use-customer'
import {
CustomerCreateInput,
Mutation,
MutationCustomerCreateArgs,
} from '../schema'
import { Mutation, MutationCustomerCreateArgs } from '../schema'
import { customerCreateMutation } from '../utils/mutations'
import { handleAutomaticLogin, throwUserErrors } from '../utils'
import {
handleAutomaticLogin,
throwUserErrors,
customerCreateMutation,
} from '../utils'
export default useSignup as UseSignup<typeof handler>
export const handler: MutationHook<
null,
{},
CustomerCreateInput,
CustomerCreateInput
> = {
export const handler: MutationHook<SignupHook> = {
fetchOptions: {
query: customerCreateMutation,
},

View File

@@ -2,18 +2,19 @@ import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
import type { AddItemHook } from '../types/cart'
import useCart from './use-cart'
import {
checkoutLineItemAddMutation,
getCheckoutId,
checkoutToCart,
} from '../utils'
import { Cart, CartItemBody } from '../types'
import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema'
export default useAddItem as UseAddItem<typeof handler>
export const handler: MutationHook<Cart, {}, CartItemBody> = {
export const handler: MutationHook<AddItemHook> = {
fetchOptions: {
query: checkoutLineItemAddMutation,
},

View File

@@ -1,22 +1,20 @@
import { useMemo } from 'react'
import useCommerceCart, {
FetchCartInput,
UseCart,
} from '@commerce/cart/use-cart'
import useCommerceCart, { UseCart } from '@commerce/cart/use-cart'
import { Cart } from '../types'
import { SWRHook } from '@commerce/utils/types'
import { checkoutCreate, checkoutToCart } from '../utils'
import getCheckoutQuery from '../utils/queries/get-checkout-query'
import { GetCartHook } from '../types/cart'
import {
GetCheckoutQuery,
GetCheckoutQueryVariables,
CheckoutDetailsFragment,
} from '../schema'
export default useCommerceCart as UseCart<typeof handler>
export const handler: SWRHook<
Cart | null,
{},
FetchCartInput,
{ isEmpty?: boolean }
> = {
export const handler: SWRHook<GetCartHook> = {
fetchOptions: {
query: getCheckoutQuery,
},

View File

@@ -3,31 +3,29 @@ import type {
MutationHookContext,
HookFetcherContext,
} from '@commerce/utils/types'
import { RemoveCartItemBody } from '@commerce/types'
import { ValidationError } from '@commerce/utils/errors'
import useRemoveItem, {
RemoveItemInput as RemoveItemInputBase,
UseRemoveItem,
} from '@commerce/cart/use-remove-item'
import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item'
import type { Cart, LineItem, RemoveItemHook } from '../types/cart'
import useCart from './use-cart'
export type RemoveItemFn<T = any> = T extends LineItem
? (input?: RemoveItemActionInput<T>) => Promise<Cart | null | undefined>
: (input: RemoveItemActionInput<T>) => Promise<Cart | null>
export type RemoveItemActionInput<T = any> = T extends LineItem
? Partial<RemoveItemHook['actionInput']>
: RemoveItemHook['actionInput']
export default useRemoveItem as UseRemoveItem<typeof handler>
import {
checkoutLineItemRemoveMutation,
getCheckoutId,
checkoutToCart,
} from '../utils'
import { Cart, LineItem } from '../types'
import { Mutation, MutationCheckoutLineItemsRemoveArgs } from '../schema'
export type RemoveItemFn<T = any> = T extends LineItem
? (input?: RemoveItemInput<T>) => Promise<Cart | null>
: (input: RemoveItemInput<T>) => Promise<Cart | null>
export type RemoveItemInput<T = any> = T extends LineItem
? Partial<RemoveItemInputBase>
: RemoveItemInputBase
export default useRemoveItem as UseRemoveItem<typeof handler>
export const handler = {
fetchOptions: {
query: checkoutLineItemRemoveMutation,
@@ -36,16 +34,14 @@ export const handler = {
input: { itemId },
options,
fetch,
}: HookFetcherContext<RemoveCartItemBody>) {
}: HookFetcherContext<RemoveItemHook>) {
const data = await fetch<Mutation, MutationCheckoutLineItemsRemoveArgs>({
...options,
variables: { checkoutId: getCheckoutId(), lineItemIds: [itemId] },
})
return checkoutToCart(data.checkoutLineItemsRemove)
},
useHook: ({
fetch,
}: MutationHookContext<Cart | null, RemoveCartItemBody>) => <
useHook: ({ fetch }: MutationHookContext<RemoveItemHook>) => <
T extends LineItem | undefined = undefined
>(
ctx: { item?: T } = {}

View File

@@ -5,21 +5,21 @@ import type {
MutationHookContext,
} from '@commerce/utils/types'
import { ValidationError } from '@commerce/utils/errors'
import useUpdateItem, {
UpdateItemInput as UpdateItemInputBase,
UseUpdateItem,
} from '@commerce/cart/use-update-item'
import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item'
import useCart from './use-cart'
import { handler as removeItemHandler } from './use-remove-item'
import type { Cart, LineItem, UpdateCartItemBody } from '../types'
import { checkoutToCart } from '../utils'
import { getCheckoutId, checkoutLineItemUpdateMutation } from '../utils'
import type { UpdateItemHook, LineItem } from '../types/cart'
import {
getCheckoutId,
checkoutLineItemUpdateMutation,
checkoutToCart,
} from '../utils'
import { Mutation, MutationCheckoutLineItemsUpdateArgs } from '../schema'
export type UpdateItemInput<T = any> = T extends LineItem
? Partial<UpdateItemInputBase<LineItem>>
: UpdateItemInputBase<LineItem>
export type UpdateItemActionInput<T = any> = T extends LineItem
? Partial<UpdateItemHook['actionInput']>
: UpdateItemHook['actionInput']
export default useUpdateItem as UseUpdateItem<typeof handler>
@@ -31,7 +31,7 @@ export const handler = {
input: { itemId, item },
options,
fetch,
}: HookFetcherContext<UpdateCartItemBody>) {
}: HookFetcherContext<UpdateItemHook>) {
if (Number.isInteger(item.quantity)) {
// Also allow the update hook to remove an item if the quantity is lower than 1
if (item.quantity! < 1) {
@@ -64,9 +64,7 @@ export const handler = {
return checkoutToCart(checkoutLineItemsUpdate)
},
useHook: ({
fetch,
}: MutationHookContext<Cart | null, UpdateCartItemBody>) => <
useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => <
T extends LineItem | undefined = undefined
>(
ctx: {
@@ -78,7 +76,7 @@ export const handler = {
const { mutate } = useCart() as any
return useCallback(
debounce(async (input: UpdateItemInput<T>) => {
debounce(async (input: UpdateItemActionInput<T>) => {
const itemId = input.id ?? item?.id
const productId = input.productId ?? item?.productId
const variantId = input.productId ?? item?.variantId

View File

@@ -0,0 +1,32 @@
{
"schema": {
"https://${NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/api/2021-01/graphql.json": {
"headers": {
"X-Shopify-Storefront-Access-Token": "${NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN}"
}
}
},
"documents": [
{
"./framework/shopify/**/*.{ts,tsx}": {
"noRequire": true
}
}
],
"generates": {
"./framework/shopify/schema.d.ts": {
"plugins": ["typescript", "typescript-operations"],
"config": {
"scalars": {
"ID": "string"
}
}
},
"./framework/shopify/schema.graphql": {
"plugins": ["schema-ast"]
}
},
"hooks": {
"afterAllFileWrite": ["prettier --write"]
}
}

View File

@@ -1,42 +0,0 @@
import { getConfig, ShopifyConfig } from '../api'
import { PageEdge } from '../schema'
import { getAllPagesQuery } from '../utils/queries'
type Variables = {
first?: number
}
type ReturnType = {
pages: Page[]
}
export type Page = {
id: string
name: string
url: string
sort_order?: number
body: string
}
const getAllPages = async (options?: {
variables?: Variables
config: ShopifyConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config)
const { locale } = config
const { data } = await config.fetch(getAllPagesQuery, { variables })
const pages = data.pages?.edges?.map(
({ node: { title: name, handle, ...node } }: PageEdge) => ({
...node,
url: `/${locale}/${handle}`,
name,
})
)
return { pages }
}
export default getAllPages

View File

@@ -1,37 +0,0 @@
import { getConfig, ShopifyConfig } from '../api'
import getPageQuery from '../utils/queries/get-page-query'
import { Page } from './get-all-pages'
type Variables = {
id: string
}
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
const getPage = async (options: {
variables: Variables
config: ShopifyConfig
preview?: boolean
}): Promise<GetPageResult> => {
let { config, variables } = options ?? {}
config = getConfig(config)
const { locale } = config
const { data } = await config.fetch(getPageQuery, {
variables,
})
const page = data.node
return {
page: page
? {
...page,
name: page.title,
url: `/${locale}/${page.handle}`,
}
: null,
}
}
export default getPage

View File

@@ -1,31 +0,0 @@
import { Category } from '@commerce/types'
import { getConfig, ShopifyConfig } from '../api'
import getCategories from '../utils/get-categories'
import getVendors, { Brands } from '../utils/get-vendors'
export type GetSiteInfoResult<
T extends { categories: any[]; brands: any[] } = {
categories: Category[]
brands: Brands
}
> = T
const getSiteInfo = async (options?: {
variables?: any
config: ShopifyConfig
preview?: boolean
}): Promise<GetSiteInfoResult> => {
let { config } = options ?? {}
config = getConfig(config)
const categories = await getCategories(config)
const brands = await getVendors(config)
return {
categories,
brands,
}
}
export default getSiteInfo

View File

@@ -1,24 +0,0 @@
import { getConfig, ShopifyConfig } from '../api'
import getCustomerIdQuery from '../utils/queries/get-customer-id-query'
import Cookies from 'js-cookie'
async function getCustomerId({
customerToken: customerAccesToken,
config,
}: {
customerToken: string
config?: ShopifyConfig
}): Promise<number | undefined> {
config = getConfig(config)
const { data } = await config.fetch(getCustomerIdQuery, {
variables: {
customerAccesToken:
customerAccesToken || Cookies.get(config.customerCookie),
},
})
return data.customer?.id
}
export default getCustomerId

View File

@@ -1,18 +1,19 @@
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
import { Customer } from '@commerce/types'
import type { CustomerHook } from '../types/customer'
import { SWRHook } from '@commerce/utils/types'
import { getCustomerQuery, getCustomerToken } from '../utils'
import { GetCustomerQuery, GetCustomerQueryVariables } from '../schema'
export default useCustomer as UseCustomer<typeof handler>
export const handler: SWRHook<Customer | null> = {
export const handler: SWRHook<CustomerHook> = {
fetchOptions: {
query: getCustomerQuery,
},
async fetcher({ options, fetch }) {
const customerAccessToken = getCustomerToken()
if (customerAccessToken) {
const data = await fetch({
const data = await fetch<GetCustomerQuery, GetCustomerQueryVariables>({
...options,
variables: { customerAccessToken: getCustomerToken() },
})

View File

@@ -8,13 +8,17 @@ const fetcher: Fetcher = async ({
variables,
query,
}) => {
const { locale, ...vars } = variables ?? {}
return handleFetchResponse(
await fetch(url, {
method,
body: JSON.stringify({ query, variables }),
body: JSON.stringify({ query, variables: vars }),
headers: {
'X-Shopify-Storefront-Access-Token': API_TOKEN!,
'Content-Type': 'application/json',
...(locale && {
'Accept-Language': locale,
}),
},
})
)

View File

@@ -36,4 +36,4 @@ export function CommerceProvider({ children, ...config }: ShopifyProps) {
)
}
export const useCommerce = () => useCoreCommerce()
export const useCommerce = () => useCoreCommerce<ShopifyProvider>()

View File

@@ -1,29 +0,0 @@
import { CollectionEdge } from '../schema'
import { getConfig, ShopifyConfig } from '../api'
import getAllCollectionsQuery from '../utils/queries/get-all-collections-query'
const getAllCollections = async (options?: {
variables?: any
config: ShopifyConfig
preview?: boolean
}) => {
let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config)
const { data } = await config.fetch(getAllCollectionsQuery, { variables })
const edges = data.collections?.edges ?? []
const categories = edges.map(
({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({
entityId,
name,
path: `/${handle}`,
})
)
return {
categories,
}
}
export default getAllCollections

View File

@@ -1,41 +0,0 @@
import { getConfig, ShopifyConfig } from '../api'
import { ProductEdge } from '../schema'
import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query'
type ProductPath = {
path: string
}
export type ProductPathNode = {
node: ProductPath
}
type ReturnType = {
products: ProductPathNode[]
}
const getAllProductPaths = async (options?: {
variables?: any
config?: ShopifyConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 100, sortKey: 'BEST_SELLING' } } =
options ?? {}
config = getConfig(config)
const { data } = await config.fetch(getAllProductsPathsQuery, {
variables,
})
return {
products: data.products?.edges?.map(
({ node: { handle } }: ProductEdge) => ({
node: {
path: `/${handle}`,
},
})
),
}
}
export default getAllProductPaths

View File

@@ -1,39 +0,0 @@
import { GraphQLFetcherResult } from '@commerce/api'
import { getConfig, ShopifyConfig } from '../api'
import { ProductEdge } from '../schema'
import { getAllProductsQuery } from '../utils/queries'
import { normalizeProduct } from '../utils/normalize'
import { Product } from '@commerce/types'
type Variables = {
first?: number
field?: string
}
type ReturnType = {
products: Product[]
}
const getAllProducts = async (options: {
variables?: Variables
config?: ShopifyConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config)
const { data }: GraphQLFetcherResult = await config.fetch(
getAllProductsQuery,
{ variables }
)
const products = data.products?.edges?.map(({ node: p }: ProductEdge) =>
normalizeProduct(p)
)
return {
products,
}
}
export default getAllProducts

View File

@@ -1,31 +0,0 @@
import { GraphQLFetcherResult } from '@commerce/api'
import { getConfig, ShopifyConfig } from '../api'
import { normalizeProduct, getProductQuery } from '../utils'
type Variables = {
slug: string
}
type ReturnType = {
product: any
}
const getProduct = async (options: {
variables: Variables
config: ShopifyConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables } = options ?? {}
config = getConfig(config)
const { data }: GraphQLFetcherResult = await config.fetch(getProductQuery, {
variables,
})
const { productByHandle } = data
return {
product: productByHandle ? normalizeProduct(productByHandle) : null,
}
}
export default getProduct

View File

@@ -1,7 +1,14 @@
import { SWRHook } from '@commerce/utils/types'
import useSearch, { UseSearch } from '@commerce/product/use-search'
import { ProductEdge } from '../schema'
import {
CollectionEdge,
GetAllProductsQuery,
GetProductsFromCollectionQueryVariables,
Product as ShopifyProduct,
ProductEdge,
} from '../schema'
import {
getAllProductsQuery,
getCollectionProductsQuery,
@@ -9,56 +16,59 @@ import {
normalizeProduct,
} from '../utils'
import { Product } from '@commerce/types'
export default useSearch as UseSearch<typeof handler>
import type { SearchProductsHook } from '../types/product'
export type SearchProductsInput = {
search?: string
categoryId?: string
brandId?: string
categoryId?: number
brandId?: number
sort?: string
locale?: string
}
export type SearchProductsData = {
products: Product[]
found: boolean
}
export default useSearch as UseSearch<typeof handler>
export const handler: SWRHook<
SearchProductsData,
SearchProductsInput,
SearchProductsInput
> = {
export const handler: SWRHook<SearchProductsHook> = {
fetchOptions: {
query: getAllProductsQuery,
},
async fetcher({ input, options, fetch }) {
const { categoryId, brandId } = input
const method = options?.method
const variables = getSearchVariables(input)
let products
const data = await fetch({
query: categoryId ? getCollectionProductsQuery : options.query,
method: options?.method,
variables: getSearchVariables(input),
})
let edges
// change the query to getCollectionProductsQuery when categoryId is set
if (categoryId) {
edges = data.node?.products?.edges ?? []
if (brandId) {
edges = edges.filter(
({ node: { vendor } }: ProductEdge) =>
vendor.replace(/\s+/g, '-').toLowerCase() === brandId
)
}
const data = await fetch<
CollectionEdge,
GetProductsFromCollectionQueryVariables
>({
query: getCollectionProductsQuery,
method,
variables,
})
// filter on client when brandId & categoryId are set since is not available on collection product query
products = brandId
? data.node.products.edges.filter(
({ node: { vendor } }: ProductEdge) =>
vendor.replace(/\s+/g, '-').toLowerCase() === brandId
)
: data.node.products.edges
} else {
edges = data.products?.edges ?? []
const data = await fetch<GetAllProductsQuery>({
query: options.query,
method,
variables,
})
products = data.products.edges
}
return {
products: edges.map(({ node }: ProductEdge) => normalizeProduct(node)),
found: !!edges.length,
products: products?.map(({ node }) =>
normalizeProduct(node as ShopifyProduct)
),
found: !!products?.length,
}
},
useHook: ({ useData }) => (input = {}) => {
@@ -68,6 +78,7 @@ export const handler: SWRHook<
['categoryId', input.categoryId],
['brandId', input.brandId],
['sort', input.sort],
['locale', input.locale],
],
swrOptions: {
revalidateOnFocus: false,

View File

@@ -37,7 +37,7 @@ export type ApiVersion = {
displayName: Scalars['String']
/** The unique identifier of an ApiVersion. All supported API versions have a date-based (YYYY-MM) or `unstable` handle. */
handle: Scalars['String']
/** Whether the version is supported by Shopify. */
/** Whether the version is actively supported by Shopify. Supported API versions are guaranteed to be stable. Unsupported API versions include unstable, release candidate, and end-of-life versions that are marked as unsupported. For more information, refer to [Versioning](https://shopify.dev/concepts/about-apis/versioning). */
supported: Scalars['Boolean']
}
@@ -306,17 +306,17 @@ export enum BlogSortKeys {
/** Card brand, such as Visa or Mastercard, which can be used for payments. */
export enum CardBrand {
/** Visa */
/** Visa. */
Visa = 'VISA',
/** Mastercard */
/** Mastercard. */
Mastercard = 'MASTERCARD',
/** Discover */
/** Discover. */
Discover = 'DISCOVER',
/** American Express */
/** American Express. */
AmericanExpress = 'AMERICAN_EXPRESS',
/** Diners Club */
/** Diners Club. */
DinersClub = 'DINERS_CLUB',
/** JCB */
/** JCB. */
Jcb = 'JCB',
}
@@ -1195,6 +1195,8 @@ export enum CountryCode {
Am = 'AM',
/** Aruba. */
Aw = 'AW',
/** Ascension Island. */
Ac = 'AC',
/** Australia. */
Au = 'AU',
/** Austria. */
@@ -1613,6 +1615,8 @@ export enum CountryCode {
To = 'TO',
/** Trinidad & Tobago. */
Tt = 'TT',
/** Tristan da Cunha. */
Ta = 'TA',
/** Tunisia. */
Tn = 'TN',
/** Turkey. */
@@ -1687,7 +1691,7 @@ export type CreditCard = {
export type CreditCardPaymentInput = {
/** The amount of the payment. */
amount: Scalars['Money']
/** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */
/** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. For more information, refer to [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests). */
idempotencyKey: Scalars['String']
/** The billing address for the payment. */
billingAddress: MailingAddressInput
@@ -1704,7 +1708,7 @@ export type CreditCardPaymentInput = {
export type CreditCardPaymentInputV2 = {
/** The amount and currency of the payment. */
paymentAmount: MoneyInput
/** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */
/** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. For more information, refer to [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests). */
idempotencyKey: Scalars['String']
/** The billing address for the payment. */
billingAddress: MailingAddressInput
@@ -1766,10 +1770,6 @@ export enum CurrencyCode {
Bhd = 'BHD',
/** Burundian Franc (BIF). */
Bif = 'BIF',
/** Belarusian Ruble (BYN). */
Byn = 'BYN',
/** Belarusian Ruble (BYR). */
Byr = 'BYR',
/** Belize Dollar (BZD). */
Bzd = 'BZD',
/** Bermudian Dollar (BMD). */
@@ -1816,26 +1816,18 @@ export enum CurrencyCode {
Czk = 'CZK',
/** Danish Kroner (DKK). */
Dkk = 'DKK',
/** Djiboutian Franc (DJF). */
Djf = 'DJF',
/** Dominican Peso (DOP). */
Dop = 'DOP',
/** East Caribbean Dollar (XCD). */
Xcd = 'XCD',
/** Egyptian Pound (EGP). */
Egp = 'EGP',
/** Eritrean Nakfa (ERN). */
Ern = 'ERN',
/** Ethiopian Birr (ETB). */
Etb = 'ETB',
/** Falkland Islands Pounds (FKP). */
Fkp = 'FKP',
/** CFP Franc (XPF). */
Xpf = 'XPF',
/** Fijian Dollars (FJD). */
Fjd = 'FJD',
/** Gibraltar Pounds (GIP). */
Gip = 'GIP',
/** Gambian Dalasi (GMD). */
Gmd = 'GMD',
/** Ghanaian Cedi (GHS). */
@@ -1846,8 +1838,6 @@ export enum CurrencyCode {
Gyd = 'GYD',
/** Georgian Lari (GEL). */
Gel = 'GEL',
/** Guinean Franc (GNF). */
Gnf = 'GNF',
/** Haitian Gourde (HTG). */
Htg = 'HTG',
/** Honduran Lempira (HNL). */
@@ -1864,8 +1854,6 @@ export enum CurrencyCode {
Idr = 'IDR',
/** Israeli New Shekel (NIS). */
Ils = 'ILS',
/** Iranian Rial (IRR). */
Irr = 'IRR',
/** Iraqi Dinar (IQD). */
Iqd = 'IQD',
/** Jamaican Dollars (JMD). */
@@ -1880,8 +1868,6 @@ export enum CurrencyCode {
Kzt = 'KZT',
/** Kenyan Shilling (KES). */
Kes = 'KES',
/** Kiribati Dollar (KID). */
Kid = 'KID',
/** Kuwaiti Dinar (KWD). */
Kwd = 'KWD',
/** Kyrgyzstani Som (KGS). */
@@ -1896,8 +1882,6 @@ export enum CurrencyCode {
Lsl = 'LSL',
/** Liberian Dollar (LRD). */
Lrd = 'LRD',
/** Libyan Dinar (LYD). */
Lyd = 'LYD',
/** Lithuanian Litai (LTL). */
Ltl = 'LTL',
/** Malagasy Ariary (MGA). */
@@ -1910,8 +1894,6 @@ export enum CurrencyCode {
Mwk = 'MWK',
/** Maldivian Rufiyaa (MVR). */
Mvr = 'MVR',
/** Mauritanian Ouguiya (MRU). */
Mru = 'MRU',
/** Mexican Pesos (MXN). */
Mxn = 'MXN',
/** Malaysian Ringgits (MYR). */
@@ -1966,8 +1948,6 @@ export enum CurrencyCode {
Rwf = 'RWF',
/** Samoan Tala (WST). */
Wst = 'WST',
/** Saint Helena Pounds (SHP). */
Shp = 'SHP',
/** Saudi Riyal (SAR). */
Sar = 'SAR',
/** Sao Tome And Principe Dobra (STD). */
@@ -1976,14 +1956,10 @@ export enum CurrencyCode {
Rsd = 'RSD',
/** Seychellois Rupee (SCR). */
Scr = 'SCR',
/** Sierra Leonean Leone (SLL). */
Sll = 'SLL',
/** Singapore Dollars (SGD). */
Sgd = 'SGD',
/** Sudanese Pound (SDG). */
Sdg = 'SDG',
/** Somali Shilling (SOS). */
Sos = 'SOS',
/** Syrian Pound (SYP). */
Syp = 'SYP',
/** South African Rand (ZAR). */
@@ -2008,12 +1984,8 @@ export enum CurrencyCode {
Twd = 'TWD',
/** Thai baht (THB). */
Thb = 'THB',
/** Tajikistani Somoni (TJS). */
Tjs = 'TJS',
/** Tanzanian Shilling (TZS). */
Tzs = 'TZS',
/** Tongan Pa'anga (TOP). */
Top = 'TOP',
/** Trinidad and Tobago Dollars (TTD). */
Ttd = 'TTD',
/** Tunisian Dinar (TND). */
@@ -2034,10 +2006,6 @@ export enum CurrencyCode {
Uzs = 'UZS',
/** Vanuatu Vatu (VUV). */
Vuv = 'VUV',
/** Venezuelan Bolivares (VEF). */
Vef = 'VEF',
/** Venezuelan Bolivares (VES). */
Ves = 'VES',
/** Vietnamese đồng (VND). */
Vnd = 'VND',
/** West African CFA franc (XOF). */
@@ -2046,6 +2014,42 @@ export enum CurrencyCode {
Yer = 'YER',
/** Zambian Kwacha (ZMW). */
Zmw = 'ZMW',
/** Belarusian Ruble (BYN). */
Byn = 'BYN',
/** Belarusian Ruble (BYR). */
Byr = 'BYR',
/** Djiboutian Franc (DJF). */
Djf = 'DJF',
/** Eritrean Nakfa (ERN). */
Ern = 'ERN',
/** Falkland Islands Pounds (FKP). */
Fkp = 'FKP',
/** Gibraltar Pounds (GIP). */
Gip = 'GIP',
/** Guinean Franc (GNF). */
Gnf = 'GNF',
/** Iranian Rial (IRR). */
Irr = 'IRR',
/** Kiribati Dollar (KID). */
Kid = 'KID',
/** Libyan Dinar (LYD). */
Lyd = 'LYD',
/** Mauritanian Ouguiya (MRU). */
Mru = 'MRU',
/** Sierra Leonean Leone (SLL). */
Sll = 'SLL',
/** Saint Helena Pounds (SHP). */
Shp = 'SHP',
/** Somali Shilling (SOS). */
Sos = 'SOS',
/** Tajikistani Somoni (TJS). */
Tjs = 'TJS',
/** Tongan Pa'anga (TOP). */
Top = 'TOP',
/** Venezuelan Bolivares (VEF). */
Vef = 'VEF',
/** Venezuelan Bolivares (VES). */
Ves = 'VES',
}
/** A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. */
@@ -3817,7 +3821,11 @@ export type Payment = Node & {
errorMessage?: Maybe<Scalars['String']>
/** Globally unique identifier. */
id: Scalars['ID']
/** A client-side generated token to identify a payment and perform idempotent operations. */
/**
* A client-side generated token to identify a payment and perform idempotent operations.
* For more information, refer to
* [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests).
*/
idempotencyKey?: Maybe<Scalars['String']>
/** The URL where the customer needs to be redirected so they can complete the 3D Secure payment flow. */
nextActionUrl?: Maybe<Scalars['URL']>
@@ -4386,7 +4394,9 @@ export type QueryRoot = {
collections: CollectionConnection
/** Find a customer by its access token. */
customer?: Maybe<Customer>
/** Returns a specific node by ID. */
node?: Maybe<Node>
/** Returns the list of nodes with the given IDs. */
nodes: Array<Maybe<Node>>
/** Find a page by its handle. */
pageByHandle?: Maybe<Page>
@@ -4768,7 +4778,7 @@ export type StringEdge = {
export type TokenizedPaymentInput = {
/** The amount of the payment. */
amount: Scalars['Money']
/** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */
/** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. For more information, refer to [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests). */
idempotencyKey: Scalars['String']
/** The billing address for the payment. */
billingAddress: MailingAddressInput
@@ -4789,7 +4799,7 @@ export type TokenizedPaymentInput = {
export type TokenizedPaymentInputV2 = {
/** The amount and currency of the payment. */
paymentAmount: MoneyInput
/** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */
/** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. For more information, refer to [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests). */
idempotencyKey: Scalars['String']
/** The billing address for the payment. */
billingAddress: MailingAddressInput
@@ -4810,7 +4820,7 @@ export type TokenizedPaymentInputV2 = {
export type TokenizedPaymentInputV3 = {
/** The amount and currency of the payment. */
paymentAmount: MoneyInput
/** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */
/** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. For more information, refer to [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests). */
idempotencyKey: Scalars['String']
/** The billing address for the payment. */
billingAddress: MailingAddressInput
@@ -4847,18 +4857,32 @@ export type Transaction = {
test: Scalars['Boolean']
}
/** The different kinds of order transactions. */
export enum TransactionKind {
/** An authorization and capture performed together in a single step. */
Sale = 'SALE',
/** A transfer of the money that was reserved during the authorization stage. */
Capture = 'CAPTURE',
/**
* An amount reserved against the cardholder's funding source.
* Money does not change hands until the authorization is captured.
*/
Authorization = 'AUTHORIZATION',
/** An authorization for a payment taken with an EMV credit card reader. */
EmvAuthorization = 'EMV_AUTHORIZATION',
/** Money returned to the customer when they have paid too much. */
Change = 'CHANGE',
}
/** Transaction statuses describe the status of a transaction. */
export enum TransactionStatus {
/** The transaction is pending. */
Pending = 'PENDING',
/** The transaction succeeded. */
Success = 'SUCCESS',
/** The transaction failed. */
Failure = 'FAILURE',
/** There was an error while processing the transaction. */
Error = 'ERROR',
}
@@ -4967,19 +4991,596 @@ export enum WeightUnit {
Ounces = 'OUNCES',
}
export type Unnamed_1_QueryVariables = Exact<{
export type AssociateCustomerWithCheckoutMutationVariables = Exact<{
checkoutId: Scalars['ID']
customerAccessToken: Scalars['String']
}>
export type AssociateCustomerWithCheckoutMutation = {
__typename?: 'Mutation'
} & {
checkoutCustomerAssociateV2?: Maybe<
{ __typename?: 'CheckoutCustomerAssociateV2Payload' } & {
checkout?: Maybe<{ __typename?: 'Checkout' } & Pick<Checkout, 'id'>>
checkoutUserErrors: Array<
{ __typename?: 'CheckoutUserError' } & Pick<
CheckoutUserError,
'code' | 'field' | 'message'
>
>
customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id'>>
}
>
}
export type CheckoutCreateMutationVariables = Exact<{
input?: Maybe<CheckoutCreateInput>
}>
export type CheckoutCreateMutation = { __typename?: 'Mutation' } & {
checkoutCreate?: Maybe<
{ __typename?: 'CheckoutCreatePayload' } & {
checkoutUserErrors: Array<
{ __typename?: 'CheckoutUserError' } & Pick<
CheckoutUserError,
'code' | 'field' | 'message'
>
>
checkout?: Maybe<{ __typename?: 'Checkout' } & CheckoutDetailsFragment>
}
>
}
export type CheckoutLineItemAddMutationVariables = Exact<{
checkoutId: Scalars['ID']
lineItems: Array<CheckoutLineItemInput> | CheckoutLineItemInput
}>
export type CheckoutLineItemAddMutation = { __typename?: 'Mutation' } & {
checkoutLineItemsAdd?: Maybe<
{ __typename?: 'CheckoutLineItemsAddPayload' } & {
checkoutUserErrors: Array<
{ __typename?: 'CheckoutUserError' } & Pick<
CheckoutUserError,
'code' | 'field' | 'message'
>
>
checkout?: Maybe<{ __typename?: 'Checkout' } & CheckoutDetailsFragment>
}
>
}
export type CheckoutLineItemRemoveMutationVariables = Exact<{
checkoutId: Scalars['ID']
lineItemIds: Array<Scalars['ID']> | Scalars['ID']
}>
export type CheckoutLineItemRemoveMutation = { __typename?: 'Mutation' } & {
checkoutLineItemsRemove?: Maybe<
{ __typename?: 'CheckoutLineItemsRemovePayload' } & {
checkoutUserErrors: Array<
{ __typename?: 'CheckoutUserError' } & Pick<
CheckoutUserError,
'code' | 'field' | 'message'
>
>
checkout?: Maybe<{ __typename?: 'Checkout' } & CheckoutDetailsFragment>
}
>
}
export type CheckoutLineItemUpdateMutationVariables = Exact<{
checkoutId: Scalars['ID']
lineItems: Array<CheckoutLineItemUpdateInput> | CheckoutLineItemUpdateInput
}>
export type CheckoutLineItemUpdateMutation = { __typename?: 'Mutation' } & {
checkoutLineItemsUpdate?: Maybe<
{ __typename?: 'CheckoutLineItemsUpdatePayload' } & {
checkoutUserErrors: Array<
{ __typename?: 'CheckoutUserError' } & Pick<
CheckoutUserError,
'code' | 'field' | 'message'
>
>
checkout?: Maybe<{ __typename?: 'Checkout' } & CheckoutDetailsFragment>
}
>
}
export type CustomerAccessTokenCreateMutationVariables = Exact<{
input: CustomerAccessTokenCreateInput
}>
export type CustomerAccessTokenCreateMutation = { __typename?: 'Mutation' } & {
customerAccessTokenCreate?: Maybe<
{ __typename?: 'CustomerAccessTokenCreatePayload' } & {
customerAccessToken?: Maybe<
{ __typename?: 'CustomerAccessToken' } & Pick<
CustomerAccessToken,
'accessToken' | 'expiresAt'
>
>
customerUserErrors: Array<
{ __typename?: 'CustomerUserError' } & Pick<
CustomerUserError,
'code' | 'field' | 'message'
>
>
}
>
}
export type CustomerAccessTokenDeleteMutationVariables = Exact<{
customerAccessToken: Scalars['String']
}>
export type CustomerAccessTokenDeleteMutation = { __typename?: 'Mutation' } & {
customerAccessTokenDelete?: Maybe<
{ __typename?: 'CustomerAccessTokenDeletePayload' } & Pick<
CustomerAccessTokenDeletePayload,
'deletedAccessToken' | 'deletedCustomerAccessTokenId'
> & {
userErrors: Array<
{ __typename?: 'UserError' } & Pick<UserError, 'field' | 'message'>
>
}
>
}
export type CustomerActivateByUrlMutationVariables = Exact<{
activationUrl: Scalars['URL']
password: Scalars['String']
}>
export type CustomerActivateByUrlMutation = { __typename?: 'Mutation' } & {
customerActivateByUrl?: Maybe<
{ __typename?: 'CustomerActivateByUrlPayload' } & {
customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id'>>
customerAccessToken?: Maybe<
{ __typename?: 'CustomerAccessToken' } & Pick<
CustomerAccessToken,
'accessToken' | 'expiresAt'
>
>
customerUserErrors: Array<
{ __typename?: 'CustomerUserError' } & Pick<
CustomerUserError,
'code' | 'field' | 'message'
>
>
}
>
}
export type CustomerActivateMutationVariables = Exact<{
id: Scalars['ID']
input: CustomerActivateInput
}>
export type CustomerActivateMutation = { __typename?: 'Mutation' } & {
customerActivate?: Maybe<
{ __typename?: 'CustomerActivatePayload' } & {
customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id'>>
customerAccessToken?: Maybe<
{ __typename?: 'CustomerAccessToken' } & Pick<
CustomerAccessToken,
'accessToken' | 'expiresAt'
>
>
customerUserErrors: Array<
{ __typename?: 'CustomerUserError' } & Pick<
CustomerUserError,
'code' | 'field' | 'message'
>
>
}
>
}
export type CustomerCreateMutationVariables = Exact<{
input: CustomerCreateInput
}>
export type CustomerCreateMutation = { __typename?: 'Mutation' } & {
customerCreate?: Maybe<
{ __typename?: 'CustomerCreatePayload' } & {
customerUserErrors: Array<
{ __typename?: 'CustomerUserError' } & Pick<
CustomerUserError,
'code' | 'field' | 'message'
>
>
customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id'>>
}
>
}
export type GetSiteCollectionsQueryVariables = Exact<{
first: Scalars['Int']
}>
export type Unnamed_1_Query = { __typename?: 'QueryRoot' } & {
pages: { __typename?: 'PageConnection' } & {
export type GetSiteCollectionsQuery = { __typename?: 'QueryRoot' } & {
collections: { __typename?: 'CollectionConnection' } & {
edges: Array<
{ __typename?: 'PageEdge' } & {
node: { __typename?: 'Page' } & Pick<
Page,
'id' | 'title' | 'handle' | 'body' | 'bodySummary' | 'url'
{ __typename?: 'CollectionEdge' } & {
node: { __typename?: 'Collection' } & Pick<
Collection,
'id' | 'title' | 'handle'
>
}
>
}
}
export type GetAllPagesQueryVariables = Exact<{
first?: Maybe<Scalars['Int']>
}>
export type GetAllPagesQuery = { __typename?: 'QueryRoot' } & {
pages: { __typename?: 'PageConnection' } & {
edges: Array<
{ __typename?: 'PageEdge' } & {
node: { __typename?: 'Page' } & Pick<Page, 'id' | 'title' | 'handle'>
}
>
}
}
export type GetAllProductVendorsQueryVariables = Exact<{
first?: Maybe<Scalars['Int']>
cursor?: Maybe<Scalars['String']>
}>
export type GetAllProductVendorsQuery = { __typename?: 'QueryRoot' } & {
products: { __typename?: 'ProductConnection' } & {
pageInfo: { __typename?: 'PageInfo' } & Pick<
PageInfo,
'hasNextPage' | 'hasPreviousPage'
>
edges: Array<
{ __typename?: 'ProductEdge' } & Pick<ProductEdge, 'cursor'> & {
node: { __typename?: 'Product' } & Pick<Product, 'vendor'>
}
>
}
}
export type GetAllProductPathsQueryVariables = Exact<{
first?: Maybe<Scalars['Int']>
cursor?: Maybe<Scalars['String']>
}>
export type GetAllProductPathsQuery = { __typename?: 'QueryRoot' } & {
products: { __typename?: 'ProductConnection' } & {
pageInfo: { __typename?: 'PageInfo' } & Pick<
PageInfo,
'hasNextPage' | 'hasPreviousPage'
>
edges: Array<
{ __typename?: 'ProductEdge' } & Pick<ProductEdge, 'cursor'> & {
node: { __typename?: 'Product' } & Pick<Product, 'handle'>
}
>
}
}
export type ProductConnectionFragment = { __typename?: 'ProductConnection' } & {
pageInfo: { __typename?: 'PageInfo' } & Pick<
PageInfo,
'hasNextPage' | 'hasPreviousPage'
>
edges: Array<
{ __typename?: 'ProductEdge' } & {
node: { __typename?: 'Product' } & Pick<
Product,
'id' | 'title' | 'vendor' | 'handle'
> & {
priceRange: { __typename?: 'ProductPriceRange' } & {
minVariantPrice: { __typename?: 'MoneyV2' } & Pick<
MoneyV2,
'amount' | 'currencyCode'
>
}
images: { __typename?: 'ImageConnection' } & {
pageInfo: { __typename?: 'PageInfo' } & Pick<
PageInfo,
'hasNextPage' | 'hasPreviousPage'
>
edges: Array<
{ __typename?: 'ImageEdge' } & {
node: { __typename?: 'Image' } & Pick<
Image,
'originalSrc' | 'altText' | 'width' | 'height'
>
}
>
}
}
}
>
}
export type GetAllProductsQueryVariables = Exact<{
first?: Maybe<Scalars['Int']>
query?: Maybe<Scalars['String']>
sortKey?: Maybe<ProductSortKeys>
reverse?: Maybe<Scalars['Boolean']>
}>
export type GetAllProductsQuery = { __typename?: 'QueryRoot' } & {
products: { __typename?: 'ProductConnection' } & ProductConnectionFragment
}
export type CheckoutDetailsFragment = { __typename?: 'Checkout' } & Pick<
Checkout,
'id' | 'webUrl' | 'completedAt' | 'createdAt' | 'taxesIncluded'
> & {
subtotalPriceV2: { __typename?: 'MoneyV2' } & Pick<
MoneyV2,
'amount' | 'currencyCode'
>
totalTaxV2: { __typename?: 'MoneyV2' } & Pick<
MoneyV2,
'amount' | 'currencyCode'
>
totalPriceV2: { __typename?: 'MoneyV2' } & Pick<
MoneyV2,
'amount' | 'currencyCode'
>
lineItems: { __typename?: 'CheckoutLineItemConnection' } & {
pageInfo: { __typename?: 'PageInfo' } & Pick<
PageInfo,
'hasNextPage' | 'hasPreviousPage'
>
edges: Array<
{ __typename?: 'CheckoutLineItemEdge' } & {
node: { __typename?: 'CheckoutLineItem' } & Pick<
CheckoutLineItem,
'id' | 'title' | 'quantity'
> & {
variant?: Maybe<
{ __typename?: 'ProductVariant' } & Pick<
ProductVariant,
'id' | 'sku' | 'title'
> & {
image?: Maybe<
{ __typename?: 'Image' } & Pick<
Image,
'originalSrc' | 'altText' | 'width' | 'height'
>
>
priceV2: { __typename?: 'MoneyV2' } & Pick<
MoneyV2,
'amount' | 'currencyCode'
>
compareAtPriceV2?: Maybe<
{ __typename?: 'MoneyV2' } & Pick<
MoneyV2,
'amount' | 'currencyCode'
>
>
product: { __typename?: 'Product' } & Pick<
Product,
'handle'
>
}
>
}
}
>
}
}
export type GetCheckoutQueryVariables = Exact<{
checkoutId: Scalars['ID']
}>
export type GetCheckoutQuery = { __typename?: 'QueryRoot' } & {
node?: Maybe<
| { __typename?: 'AppliedGiftCard' }
| { __typename?: 'Article' }
| { __typename?: 'Blog' }
| ({ __typename?: 'Checkout' } & CheckoutDetailsFragment)
| { __typename?: 'CheckoutLineItem' }
| { __typename?: 'Collection' }
| { __typename?: 'Comment' }
| { __typename?: 'ExternalVideo' }
| { __typename?: 'MailingAddress' }
| { __typename?: 'MediaImage' }
| { __typename?: 'Metafield' }
| { __typename?: 'Model3d' }
| { __typename?: 'Order' }
| { __typename?: 'Page' }
| { __typename?: 'Payment' }
| { __typename?: 'Product' }
| { __typename?: 'ProductOption' }
| { __typename?: 'ProductVariant' }
| { __typename?: 'ShopPolicy' }
| { __typename?: 'Video' }
>
}
export type GetProductsFromCollectionQueryVariables = Exact<{
categoryId: Scalars['ID']
first?: Maybe<Scalars['Int']>
sortKey?: Maybe<ProductCollectionSortKeys>
reverse?: Maybe<Scalars['Boolean']>
}>
export type GetProductsFromCollectionQuery = { __typename?: 'QueryRoot' } & {
node?: Maybe<
| ({ __typename?: 'AppliedGiftCard' } & Pick<AppliedGiftCard, 'id'>)
| ({ __typename?: 'Article' } & Pick<Article, 'id'>)
| ({ __typename?: 'Blog' } & Pick<Blog, 'id'>)
| ({ __typename?: 'Checkout' } & Pick<Checkout, 'id'>)
| ({ __typename?: 'CheckoutLineItem' } & Pick<CheckoutLineItem, 'id'>)
| ({ __typename?: 'Collection' } & Pick<Collection, 'id'> & {
products: {
__typename?: 'ProductConnection'
} & ProductConnectionFragment
})
| ({ __typename?: 'Comment' } & Pick<Comment, 'id'>)
| ({ __typename?: 'ExternalVideo' } & Pick<ExternalVideo, 'id'>)
| ({ __typename?: 'MailingAddress' } & Pick<MailingAddress, 'id'>)
| ({ __typename?: 'MediaImage' } & Pick<MediaImage, 'id'>)
| ({ __typename?: 'Metafield' } & Pick<Metafield, 'id'>)
| ({ __typename?: 'Model3d' } & Pick<Model3d, 'id'>)
| ({ __typename?: 'Order' } & Pick<Order, 'id'>)
| ({ __typename?: 'Page' } & Pick<Page, 'id'>)
| ({ __typename?: 'Payment' } & Pick<Payment, 'id'>)
| ({ __typename?: 'Product' } & Pick<Product, 'id'>)
| ({ __typename?: 'ProductOption' } & Pick<ProductOption, 'id'>)
| ({ __typename?: 'ProductVariant' } & Pick<ProductVariant, 'id'>)
| ({ __typename?: 'ShopPolicy' } & Pick<ShopPolicy, 'id'>)
| ({ __typename?: 'Video' } & Pick<Video, 'id'>)
>
}
export type GetCustomerIdQueryVariables = Exact<{
customerAccessToken: Scalars['String']
}>
export type GetCustomerIdQuery = { __typename?: 'QueryRoot' } & {
customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id'>>
}
export type GetCustomerQueryVariables = Exact<{
customerAccessToken: Scalars['String']
}>
export type GetCustomerQuery = { __typename?: 'QueryRoot' } & {
customer?: Maybe<
{ __typename?: 'Customer' } & Pick<
Customer,
| 'id'
| 'firstName'
| 'lastName'
| 'displayName'
| 'email'
| 'phone'
| 'tags'
| 'acceptsMarketing'
| 'createdAt'
>
>
}
export type GetPageQueryVariables = Exact<{
id: Scalars['ID']
}>
export type GetPageQuery = { __typename?: 'QueryRoot' } & {
node?: Maybe<
| ({ __typename?: 'AppliedGiftCard' } & Pick<AppliedGiftCard, 'id'>)
| ({ __typename?: 'Article' } & Pick<Article, 'id'>)
| ({ __typename?: 'Blog' } & Pick<Blog, 'id'>)
| ({ __typename?: 'Checkout' } & Pick<Checkout, 'id'>)
| ({ __typename?: 'CheckoutLineItem' } & Pick<CheckoutLineItem, 'id'>)
| ({ __typename?: 'Collection' } & Pick<Collection, 'id'>)
| ({ __typename?: 'Comment' } & Pick<Comment, 'id'>)
| ({ __typename?: 'ExternalVideo' } & Pick<ExternalVideo, 'id'>)
| ({ __typename?: 'MailingAddress' } & Pick<MailingAddress, 'id'>)
| ({ __typename?: 'MediaImage' } & Pick<MediaImage, 'id'>)
| ({ __typename?: 'Metafield' } & Pick<Metafield, 'id'>)
| ({ __typename?: 'Model3d' } & Pick<Model3d, 'id'>)
| ({ __typename?: 'Order' } & Pick<Order, 'id'>)
| ({ __typename?: 'Page' } & Pick<
Page,
'title' | 'handle' | 'body' | 'bodySummary' | 'id'
>)
| ({ __typename?: 'Payment' } & Pick<Payment, 'id'>)
| ({ __typename?: 'Product' } & Pick<Product, 'id'>)
| ({ __typename?: 'ProductOption' } & Pick<ProductOption, 'id'>)
| ({ __typename?: 'ProductVariant' } & Pick<ProductVariant, 'id'>)
| ({ __typename?: 'ShopPolicy' } & Pick<ShopPolicy, 'id'>)
| ({ __typename?: 'Video' } & Pick<Video, 'id'>)
>
}
export type GetProductBySlugQueryVariables = Exact<{
slug: Scalars['String']
}>
export type GetProductBySlugQuery = { __typename?: 'QueryRoot' } & {
productByHandle?: Maybe<
{ __typename?: 'Product' } & Pick<
Product,
| 'id'
| 'handle'
| 'title'
| 'productType'
| 'vendor'
| 'description'
| 'descriptionHtml'
> & {
options: Array<
{ __typename?: 'ProductOption' } & Pick<
ProductOption,
'id' | 'name' | 'values'
>
>
priceRange: { __typename?: 'ProductPriceRange' } & {
maxVariantPrice: { __typename?: 'MoneyV2' } & Pick<
MoneyV2,
'amount' | 'currencyCode'
>
minVariantPrice: { __typename?: 'MoneyV2' } & Pick<
MoneyV2,
'amount' | 'currencyCode'
>
}
variants: { __typename?: 'ProductVariantConnection' } & {
pageInfo: { __typename?: 'PageInfo' } & Pick<
PageInfo,
'hasNextPage' | 'hasPreviousPage'
>
edges: Array<
{ __typename?: 'ProductVariantEdge' } & {
node: { __typename?: 'ProductVariant' } & Pick<
ProductVariant,
'id' | 'title' | 'sku'
> & {
selectedOptions: Array<
{ __typename?: 'SelectedOption' } & Pick<
SelectedOption,
'name' | 'value'
>
>
priceV2: { __typename?: 'MoneyV2' } & Pick<
MoneyV2,
'amount' | 'currencyCode'
>
compareAtPriceV2?: Maybe<
{ __typename?: 'MoneyV2' } & Pick<
MoneyV2,
'amount' | 'currencyCode'
>
>
}
}
>
}
images: { __typename?: 'ImageConnection' } & {
pageInfo: { __typename?: 'PageInfo' } & Pick<
PageInfo,
'hasNextPage' | 'hasPreviousPage'
>
edges: Array<
{ __typename?: 'ImageEdge' } & {
node: { __typename?: 'Image' } & Pick<
Image,
'originalSrc' | 'altText' | 'width' | 'height'
>
}
>
}
}
>
}
export type GetSiteInfoQueryVariables = Exact<{ [key: string]: never }>
export type GetSiteInfoQuery = { __typename?: 'QueryRoot' } & {
shop: { __typename?: 'Shop' } & Pick<Shop, 'name'>
}

View File

@@ -13,6 +13,16 @@ directive @accessRestricted(
reason: String = null
) on FIELD_DEFINITION | OBJECT
"""
Contextualize data.
"""
directive @inContext(
"""
The country code for context.
"""
country: CountryCode!
) on QUERY | MUTATION
"""
A version of the API.
"""
@@ -28,7 +38,7 @@ type ApiVersion {
handle: String!
"""
Whether the version is supported by Shopify.
Whether the version is actively supported by Shopify. Supported API versions are guaranteed to be stable. Unsupported API versions include unstable, release candidate, and end-of-life versions that are marked as unsupported. For more information, refer to [Versioning](https://shopify.dev/concepts/about-apis/versioning).
"""
supported: Boolean!
}
@@ -547,32 +557,32 @@ Card brand, such as Visa or Mastercard, which can be used for payments.
"""
enum CardBrand {
"""
Visa
Visa.
"""
VISA
"""
Mastercard
Mastercard.
"""
MASTERCARD
"""
Discover
Discover.
"""
DISCOVER
"""
American Express
American Express.
"""
AMERICAN_EXPRESS
"""
Diners Club
Diners Club.
"""
DINERS_CLUB
"""
JCB
JCB.
"""
JCB
}
@@ -2142,6 +2152,11 @@ enum CountryCode {
"""
AW
"""
Ascension Island.
"""
AC
"""
Australia.
"""
@@ -3187,6 +3202,11 @@ enum CountryCode {
"""
TT
"""
Tristan da Cunha.
"""
TA
"""
Tunisia.
"""
@@ -3354,7 +3374,7 @@ input CreditCardPaymentInput {
amount: Money!
"""
A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one.
A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. For more information, refer to [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests).
"""
idempotencyKey: String!
@@ -3385,7 +3405,7 @@ input CreditCardPaymentInputV2 {
paymentAmount: MoneyInput!
"""
A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one.
A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. For more information, refer to [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests).
"""
idempotencyKey: String!
@@ -3529,16 +3549,6 @@ enum CurrencyCode {
"""
BIF
"""
Belarusian Ruble (BYN).
"""
BYN
"""
Belarusian Ruble (BYR).
"""
BYR
"""
Belize Dollar (BZD).
"""
@@ -3654,11 +3664,6 @@ enum CurrencyCode {
"""
DKK
"""
Djiboutian Franc (DJF).
"""
DJF
"""
Dominican Peso (DOP).
"""
@@ -3674,21 +3679,11 @@ enum CurrencyCode {
"""
EGP
"""
Eritrean Nakfa (ERN).
"""
ERN
"""
Ethiopian Birr (ETB).
"""
ETB
"""
Falkland Islands Pounds (FKP).
"""
FKP
"""
CFP Franc (XPF).
"""
@@ -3699,11 +3694,6 @@ enum CurrencyCode {
"""
FJD
"""
Gibraltar Pounds (GIP).
"""
GIP
"""
Gambian Dalasi (GMD).
"""
@@ -3729,11 +3719,6 @@ enum CurrencyCode {
"""
GEL
"""
Guinean Franc (GNF).
"""
GNF
"""
Haitian Gourde (HTG).
"""
@@ -3774,11 +3759,6 @@ enum CurrencyCode {
"""
ILS
"""
Iranian Rial (IRR).
"""
IRR
"""
Iraqi Dinar (IQD).
"""
@@ -3814,11 +3794,6 @@ enum CurrencyCode {
"""
KES
"""
Kiribati Dollar (KID).
"""
KID
"""
Kuwaiti Dinar (KWD).
"""
@@ -3854,11 +3829,6 @@ enum CurrencyCode {
"""
LRD
"""
Libyan Dinar (LYD).
"""
LYD
"""
Lithuanian Litai (LTL).
"""
@@ -3889,11 +3859,6 @@ enum CurrencyCode {
"""
MVR
"""
Mauritanian Ouguiya (MRU).
"""
MRU
"""
Mexican Pesos (MXN).
"""
@@ -4029,11 +3994,6 @@ enum CurrencyCode {
"""
WST
"""
Saint Helena Pounds (SHP).
"""
SHP
"""
Saudi Riyal (SAR).
"""
@@ -4054,11 +4014,6 @@ enum CurrencyCode {
"""
SCR
"""
Sierra Leonean Leone (SLL).
"""
SLL
"""
Singapore Dollars (SGD).
"""
@@ -4069,11 +4024,6 @@ enum CurrencyCode {
"""
SDG
"""
Somali Shilling (SOS).
"""
SOS
"""
Syrian Pound (SYP).
"""
@@ -4134,21 +4084,11 @@ enum CurrencyCode {
"""
THB
"""
Tajikistani Somoni (TJS).
"""
TJS
"""
Tanzanian Shilling (TZS).
"""
TZS
"""
Tongan Pa'anga (TOP).
"""
TOP
"""
Trinidad and Tobago Dollars (TTD).
"""
@@ -4199,16 +4139,6 @@ enum CurrencyCode {
"""
VUV
"""
Venezuelan Bolivares (VEF).
"""
VEF
"""
Venezuelan Bolivares (VES).
"""
VES
"""
Vietnamese đồng (VND).
"""
@@ -4228,6 +4158,96 @@ enum CurrencyCode {
Zambian Kwacha (ZMW).
"""
ZMW
"""
Belarusian Ruble (BYN).
"""
BYN
"""
Belarusian Ruble (BYR).
"""
BYR
"""
Djiboutian Franc (DJF).
"""
DJF
"""
Eritrean Nakfa (ERN).
"""
ERN
"""
Falkland Islands Pounds (FKP).
"""
FKP
"""
Gibraltar Pounds (GIP).
"""
GIP
"""
Guinean Franc (GNF).
"""
GNF
"""
Iranian Rial (IRR).
"""
IRR
"""
Kiribati Dollar (KID).
"""
KID
"""
Libyan Dinar (LYD).
"""
LYD
"""
Mauritanian Ouguiya (MRU).
"""
MRU
"""
Sierra Leonean Leone (SLL).
"""
SLL
"""
Saint Helena Pounds (SHP).
"""
SHP
"""
Somali Shilling (SOS).
"""
SOS
"""
Tajikistani Somoni (TJS).
"""
TJS
"""
Tongan Pa'anga (TOP).
"""
TOP
"""
Venezuelan Bolivares (VEF).
"""
VEF
"""
Venezuelan Bolivares (VES).
"""
VES
}
"""
@@ -7355,6 +7375,8 @@ type Payment implements Node {
"""
A client-side generated token to identify a payment and perform idempotent operations.
For more information, refer to
[Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests).
"""
idempotencyKey: String
@@ -8589,12 +8611,20 @@ type QueryRoot {
"""
customerAccessToken: String!
): Customer
"""
Returns a specific node by ID.
"""
node(
"""
The ID of the Node to return.
"""
id: ID!
): Node
"""
Returns the list of nodes with the given IDs.
"""
nodes(
"""
The IDs of the Nodes to return.
@@ -9246,7 +9276,7 @@ input TokenizedPaymentInput {
amount: Money!
"""
A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one.
A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. For more information, refer to [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests).
"""
idempotencyKey: String!
@@ -9287,7 +9317,7 @@ input TokenizedPaymentInputV2 {
paymentAmount: MoneyInput!
"""
A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one.
A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. For more information, refer to [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests).
"""
idempotencyKey: String!
@@ -9328,7 +9358,7 @@ input TokenizedPaymentInputV3 {
paymentAmount: MoneyInput!
"""
A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one.
A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. For more information, refer to [Idempotent requests](https://shopify.dev/concepts/about-apis/idempotent-requests).
"""
idempotencyKey: String!
@@ -9393,18 +9423,59 @@ type Transaction {
test: Boolean!
}
"""
The different kinds of order transactions.
"""
enum TransactionKind {
"""
An authorization and capture performed together in a single step.
"""
SALE
"""
A transfer of the money that was reserved during the authorization stage.
"""
CAPTURE
"""
An amount reserved against the cardholder's funding source.
Money does not change hands until the authorization is captured.
"""
AUTHORIZATION
"""
An authorization for a payment taken with an EMV credit card reader.
"""
EMV_AUTHORIZATION
"""
Money returned to the customer when they have paid too much.
"""
CHANGE
}
"""
Transaction statuses describe the status of a transaction.
"""
enum TransactionStatus {
"""
The transaction is pending.
"""
PENDING
"""
The transaction succeeded.
"""
SUCCESS
"""
The transaction failed.
"""
FAILURE
"""
There was an error while processing the transaction.
"""
ERROR
}

View File

@@ -1,43 +0,0 @@
import * as Core from '@commerce/types'
import { CheckoutLineItem } from './schema'
export type ShopifyCheckout = {
id: string
webUrl: string
lineItems: CheckoutLineItem[]
}
export type Cart = Core.Cart & {
lineItems: LineItem[]
}
export interface LineItem extends Core.LineItem {
options?: any[]
}
/**
* Cart mutations
*/
export type OptionSelections = {
option_id: number
option_value: number | string
}
export type CartItemBody = Core.CartItemBody & {
productId: string // The product id is always required for BC
optionSelections?: OptionSelections
}
export type GetCartHandlerBody = Core.GetCartHandlerBody
export type AddCartItemBody = Core.AddCartItemBody<CartItemBody>
export type AddCartItemHandlerBody = Core.AddCartItemHandlerBody<CartItemBody>
export type UpdateCartItemBody = Core.UpdateCartItemBody<CartItemBody>
export type UpdateCartItemHandlerBody = Core.UpdateCartItemHandlerBody<CartItemBody>
export type RemoveCartItemBody = Core.RemoveCartItemBody
export type RemoveCartItemHandlerBody = Core.RemoveCartItemHandlerBody

View File

@@ -0,0 +1,32 @@
import * as Core from '@commerce/types/cart'
export * from '@commerce/types/cart'
export type ShopifyCart = {}
/**
* Extend core cart types
*/
export type Cart = Core.Cart & {
lineItems: Core.LineItem[]
url?: string
}
export type CartTypes = Core.CartTypes
export type CartHooks = Core.CartHooks<CartTypes>
export type GetCartHook = CartHooks['getCart']
export type AddItemHook = CartHooks['addItem']
export type UpdateItemHook = CartHooks['updateItem']
export type RemoveItemHook = CartHooks['removeItem']
export type CartSchema = Core.CartSchema<CartTypes>
export type CartHandlers = Core.CartHandlers<CartTypes>
export type GetCartHandler = CartHandlers['getCart']
export type AddItemHandler = CartHandlers['addItem']
export type UpdateItemHandler = CartHandlers['updateItem']
export type RemoveItemHandler = CartHandlers['removeItem']

View File

@@ -0,0 +1 @@
export * from '@commerce/types/checkout'

View File

@@ -0,0 +1 @@
export * from '@commerce/types/common'

View File

@@ -0,0 +1,5 @@
import * as Core from '@commerce/types/customer'
export * from '@commerce/types/customer'
export type CustomerSchema = Core.CustomerSchema

View File

@@ -0,0 +1,25 @@
import * as Cart from './cart'
import * as Checkout from './checkout'
import * as Common from './common'
import * as Customer from './customer'
import * as Login from './login'
import * as Logout from './logout'
import * as Page from './page'
import * as Product from './product'
import * as Signup from './signup'
import * as Site from './site'
import * as Wishlist from './wishlist'
export type {
Cart,
Checkout,
Common,
Customer,
Login,
Logout,
Page,
Product,
Signup,
Site,
Wishlist,
}

View File

@@ -0,0 +1,8 @@
import * as Core from '@commerce/types/login'
import type { CustomerAccessTokenCreateInput } from '../schema'
export * from '@commerce/types/login'
export type LoginOperation = Core.LoginOperation & {
variables: CustomerAccessTokenCreateInput
}

View File

@@ -0,0 +1 @@
export * from '@commerce/types/logout'

View File

@@ -0,0 +1,11 @@
import * as Core from '@commerce/types/page'
export * from '@commerce/types/page'
export type Page = Core.Page
export type PageTypes = {
page: Page
}
export type GetAllPagesOperation = Core.GetAllPagesOperation<PageTypes>
export type GetPageOperation = Core.GetPageOperation<PageTypes>

View File

@@ -0,0 +1 @@
export * from '@commerce/types/product'

View File

@@ -0,0 +1 @@
export * from '@commerce/types/signup'

View File

@@ -0,0 +1 @@
export * from '@commerce/types/site'

View File

@@ -0,0 +1 @@
export * from '@commerce/types/wishlist'

View File

@@ -1,4 +1,4 @@
import { Cart } from '../types'
import type { Cart } from '../types/cart'
import { CommerceError } from '@commerce/utils/errors'
import {
@@ -27,12 +27,6 @@ export type CheckoutPayload =
| CheckoutQuery
const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => {
if (!checkoutPayload) {
throw new CommerceError({
message: 'Missing checkout payload from response',
})
}
const checkout = checkoutPayload?.checkout
throwUserErrors(checkoutPayload?.checkoutUserErrors)

View File

@@ -1,5 +1,8 @@
import {
GetAllProductVendorsQuery,
GetAllProductVendorsQueryVariables,
} from '../schema'
import { ShopifyConfig } from '../api'
import fetchAllProducts from '../api/utils/fetch-all-products'
import getAllProductVendors from './queries/get-all-product-vendors-query'
export type Brand = {
@@ -14,16 +17,17 @@ export type BrandEdge = {
export type Brands = BrandEdge[]
const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => {
const vendors = await fetchAllProducts({
config,
query: getAllProductVendors,
const getBrands = async (config: ShopifyConfig): Promise<BrandEdge[]> => {
const { data } = await config.fetch<
GetAllProductVendorsQuery,
GetAllProductVendorsQueryVariables
>(getAllProductVendors, {
variables: {
first: 250,
},
})
let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor)
let vendorsStrings = data.products.edges.map(({ node: { vendor } }) => vendor)
return [...new Set(vendorsStrings)].map((v) => {
const id = v.replace(/\s+/g, '-').toLowerCase()
@@ -37,4 +41,4 @@ const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => {
})
}
export default getVendors
export default getBrands

View File

@@ -1,23 +1,32 @@
import type { Category } from '../types/site'
import { ShopifyConfig } from '../api'
import { CollectionEdge } from '../schema'
import { normalizeCategory } from './normalize'
import getSiteCollectionsQuery from './queries/get-all-collections-query'
import { Category } from '@commerce/types'
const getCategories = async (config: ShopifyConfig): Promise<Category[]> => {
const { data } = await config.fetch(getSiteCollectionsQuery, {
variables: {
first: 250,
const getCategories = async ({
fetch,
locale,
}: ShopifyConfig): Promise<Category[]> => {
const { data } = await fetch(
getSiteCollectionsQuery,
{
variables: {
first: 250,
},
},
})
{
...(locale && {
headers: {
'Accept-Language': locale,
},
}),
}
)
return (
data.collections?.edges?.map(
({ node: { id, title: name, handle } }: CollectionEdge) => ({
id,
name,
slug: handle,
path: `/${handle}`,
})
data.collections?.edges?.map(({ node }: CollectionEdge) =>
normalizeCategory(node)
) ?? []
)
}

View File

@@ -1,26 +1,30 @@
import getSortVariables from './get-sort-variables'
import type { SearchProductsInput } from '../product/use-search'
import { SearchProductsBody } from '../types/product'
export const getSearchVariables = ({
brandId,
search,
categoryId,
sort,
}: SearchProductsInput) => {
locale,
}: SearchProductsBody) => {
let query = ''
if (search) {
query += `product_type:${search} OR title:${search} OR tag:${search}`
query += `product_type:${search} OR title:${search} OR tag:${search} `
}
if (brandId) {
query += `${search ? ' AND ' : ''}vendor:${brandId}`
query += `${search ? 'AND ' : ''}vendor:${brandId}`
}
return {
categoryId,
query,
...getSortVariables(sort, !!categoryId),
...(locale && {
locale,
}),
}
}

View File

@@ -1,7 +1,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 getVendors } from './get-vendors'
export { default as getBrands } from './get-brands'
export { default as getCategories } from './get-categories'
export { default as getCheckoutId } from './get-checkout-id'
export { default as checkoutCreate } from './checkout-create'

View File

@@ -1,17 +1,19 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutCreateMutation = /* GraphQL */ `
mutation {
checkoutCreate(input: {}) {
mutation checkoutCreate($input: CheckoutCreateInput = {}) {
checkoutCreate(input: $input) {
checkoutUserErrors {
code
field
message
}
checkout {
${checkoutDetailsFragment}
...checkoutDetails
}
}
}
${checkoutDetailsFragment}
`
export default checkoutCreateMutation

View File

@@ -1,7 +1,10 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemAddMutation = /* GraphQL */ `
mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemInput!]!) {
mutation checkoutLineItemAdd(
$checkoutId: ID!
$lineItems: [CheckoutLineItemInput!]!
) {
checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) {
checkoutUserErrors {
code
@@ -9,9 +12,11 @@ const checkoutLineItemAddMutation = /* GraphQL */ `
message
}
checkout {
${checkoutDetailsFragment}
...checkoutDetails
}
}
}
${checkoutDetailsFragment}
`
export default checkoutLineItemAddMutation

View File

@@ -1,7 +1,7 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemRemoveMutation = /* GraphQL */ `
mutation($checkoutId: ID!, $lineItemIds: [ID!]!) {
mutation checkoutLineItemRemove($checkoutId: ID!, $lineItemIds: [ID!]!) {
checkoutLineItemsRemove(
checkoutId: $checkoutId
lineItemIds: $lineItemIds
@@ -12,9 +12,10 @@ const checkoutLineItemRemoveMutation = /* GraphQL */ `
message
}
checkout {
${checkoutDetailsFragment}
...checkoutDetails
}
}
}
${checkoutDetailsFragment}
`
export default checkoutLineItemRemoveMutation

View File

@@ -1,7 +1,10 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemUpdateMutation = /* GraphQL */ `
mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemUpdateInput!]!) {
mutation checkoutLineItemUpdate(
$checkoutId: ID!
$lineItems: [CheckoutLineItemUpdateInput!]!
) {
checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) {
checkoutUserErrors {
code
@@ -9,9 +12,11 @@ const checkoutLineItemUpdateMutation = /* GraphQL */ `
message
}
checkout {
${checkoutDetailsFragment}
...checkoutDetails
}
}
}
${checkoutDetailsFragment}
`
export default checkoutLineItemUpdateMutation

View File

@@ -1,4 +1,7 @@
import { Product } from '@commerce/types'
import type { Page } from '../types/page'
import type { Product } from '../types/product'
import type { Cart, LineItem } from '../types/cart'
import type { Category } from '../types/site'
import {
Product as ShopifyProduct,
@@ -9,9 +12,11 @@ import {
ProductVariantConnection,
MoneyV2,
ProductOption,
Page as ShopifyPage,
PageEdge,
Collection,
} from '../schema'
import type { Cart, LineItem } from '../types'
import { colorMap } from '@lib/colors'
const money = ({ amount, currencyCode }: MoneyV2) => {
return {
@@ -28,15 +33,18 @@ const normalizeProductOption = ({
return {
__typename: 'MultipleChoiceOption',
id,
displayName,
displayName: displayName.toLowerCase(),
values: values.map((value) => {
let output: any = {
label: value,
}
if (displayName.match(/colou?r/gi)) {
output = {
...output,
hexColors: [value],
const mapedColor = colorMap[value.toLowerCase().replace(/ /g, '')]
if (mapedColor) {
output = {
...output,
hexColors: [mapedColor],
}
}
}
return output
@@ -53,7 +61,16 @@ const normalizeProductImages = ({ edges }: ImageConnection) =>
const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
return edges?.map(
({
node: { id, selectedOptions, sku, title, priceV2, compareAtPriceV2 },
node: {
id,
selectedOptions,
sku,
title,
priceV2,
compareAtPriceV2,
requiresShipping,
availableForSale,
},
}) => {
return {
id,
@@ -61,7 +78,8 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
sku: sku ?? id,
price: +priceV2.amount,
listPrice: +compareAtPriceV2?.amount,
requiresShipping: true,
requiresShipping,
availableForSale,
options: selectedOptions.map(({ name, value }: SelectedOption) => {
const options = normalizeProductOption({
id,
@@ -75,22 +93,21 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
)
}
export function normalizeProduct(productNode: ShopifyProduct): Product {
const {
id,
title: name,
vendor,
images,
variants,
description,
descriptionHtml,
handle,
priceRange,
options,
...rest
} = productNode
const product = {
export function normalizeProduct({
id,
title: name,
vendor,
images,
variants,
description,
descriptionHtml,
handle,
priceRange,
options,
metafields,
...rest
}: ShopifyProduct): Product {
return {
id,
name,
vendor,
@@ -108,13 +125,12 @@ export function normalizeProduct(productNode: ShopifyProduct): Product {
...(descriptionHtml && { descriptionHtml }),
...rest,
}
return product
}
export function normalizeCart(checkout: Checkout): Cart {
return {
id: checkout.id,
url: checkout.webUrl,
customerId: '',
email: '',
createdAt: checkout.createdAt,
@@ -131,7 +147,7 @@ export function normalizeCart(checkout: Checkout): Cart {
}
function normalizeLineItem({
node: { id, title, variant, quantity, ...rest },
node: { id, title, variant, quantity },
}: CheckoutLineItemEdge): LineItem {
return {
id,
@@ -144,7 +160,7 @@ function normalizeLineItem({
sku: variant?.sku ?? '',
name: variant?.title!,
image: {
url: variant?.image?.originalSrc ?? '/product-img-placeholder.svg',
url: variant?.image?.originalSrc || '/product-img-placeholder.svg',
},
requiresShipping: variant?.requiresShipping ?? false,
price: variant?.priceV2?.amount,
@@ -152,14 +168,29 @@ function normalizeLineItem({
},
path: String(variant?.product?.handle),
discounts: [],
options:
// By default Shopify adds a default variant with default names, we're removing it. https://community.shopify.com/c/Shopify-APIs-SDKs/Adding-new-product-variant-is-automatically-adding-quot-Default/td-p/358095
variant?.title == 'Default Title'
? []
: [
{
value: variant?.title,
},
],
options: variant?.title == 'Default Title' ? [] : variant?.selectedOptions,
}
}
export const normalizePage = (
{ title: name, handle, ...page }: ShopifyPage,
locale: string
): Page => ({
...page,
url: `/${locale}/${handle}`,
name,
})
export const normalizePages = (edges: PageEdge[], locale: string): Page[] =>
edges?.map((edge) => normalizePage(edge.node, locale))
export const normalizeCategory = ({
title: name,
handle,
id,
}: Collection): Category => ({
id,
name,
slug: handle,
path: `/${handle}`,
})

View File

@@ -1,46 +1,38 @@
export const productConnection = `
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
id
title
vendor
handle
priceRange {
minVariantPrice {
amount
currencyCode
}
export const productConnectionFragment = /* GraphQL */ `
fragment productConnection on ProductConnection {
pageInfo {
hasNextPage
hasPreviousPage
}
images(first: 1) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
originalSrc
altText
width
height
edges {
node {
id
title
vendor
handle
priceRange {
minVariantPrice {
amount
currencyCode
}
}
images(first: 1) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
originalSrc
altText
width
height
}
}
}
}
}
}
}`
export const productsFragment = `
products(
first: $first
sortKey: $sortKey
reverse: $reverse
query: $query
) {
${productConnection}
}
`
const getAllProductsQuery = /* GraphQL */ `
@@ -50,7 +42,16 @@ const getAllProductsQuery = /* GraphQL */ `
$sortKey: ProductSortKeys = RELEVANCE
$reverse: Boolean = false
) {
${productsFragment}
products(
first: $first
sortKey: $sortKey
reverse: $reverse
query: $query
) {
...productConnection
}
}
${productConnectionFragment}
`
export default getAllProductsQuery

View File

@@ -1,65 +1,70 @@
export const checkoutDetailsFragment = `
id
webUrl
subtotalPriceV2{
amount
currencyCode
}
totalTaxV2 {
amount
currencyCode
}
totalPriceV2 {
amount
currencyCode
}
completedAt
createdAt
taxesIncluded
lineItems(first: 250) {
pageInfo {
hasNextPage
hasPreviousPage
export const checkoutDetailsFragment = /* GraphQL */ `
fragment checkoutDetails on Checkout {
id
webUrl
subtotalPriceV2 {
amount
currencyCode
}
edges {
node {
id
title
variant {
totalTaxV2 {
amount
currencyCode
}
totalPriceV2 {
amount
currencyCode
}
completedAt
createdAt
taxesIncluded
lineItems(first: 250) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
id
sku
title
image {
originalSrc
altText
width
height
}
priceV2{
amount
currencyCode
}
compareAtPriceV2{
amount
currencyCode
}
product {
handle
variant {
id
sku
title
selectedOptions {
name
value
}
image {
originalSrc
altText
width
height
}
priceV2 {
amount
currencyCode
}
compareAtPriceV2 {
amount
currencyCode
}
product {
handle
}
}
quantity
}
quantity
}
}
}
`
const getCheckoutQuery = /* GraphQL */ `
query($checkoutId: ID!) {
query getCheckout($checkoutId: ID!) {
node(id: $checkoutId) {
... on Checkout {
${checkoutDetailsFragment}
}
...checkoutDetails
}
}
${checkoutDetailsFragment}
`
export default getCheckoutQuery

View File

@@ -1,4 +1,4 @@
import { productConnection } from './get-all-products-query'
import { productConnectionFragment } from './get-all-products-query'
const getCollectionProductsQuery = /* GraphQL */ `
query getProductsFromCollection(
@@ -10,15 +10,12 @@ const getCollectionProductsQuery = /* GraphQL */ `
node(id: $categoryId) {
id
... on Collection {
products(
first: $first
sortKey: $sortKey
reverse: $reverse
) {
${productConnection}
products(first: $first, sortKey: $sortKey, reverse: $reverse) {
...productConnection
}
}
}
}
${productConnectionFragment}
`
export default getCollectionProductsQuery

View File

@@ -1,5 +1,5 @@
export const getPageQuery = /* GraphQL */ `
query($id: ID!) {
query getPage($id: ID!) {
node(id: $id) {
id
... on Page {

View File

@@ -3,6 +3,7 @@ const getProductQuery = /* GraphQL */ `
productByHandle(handle: $slug) {
id
handle
availableForSale
title
productType
vendor
@@ -33,6 +34,8 @@ const getProductQuery = /* GraphQL */ `
id
title
sku
availableForSale
requiresShipping
selectedOptions {
name
value

View File

@@ -0,0 +1,8 @@
const getSiteInfoQuery = /* GraphQL */ `
query getSiteInfo {
shop {
name
}
}
`
export default getSiteInfoQuery

View File

@@ -8,3 +8,4 @@ export { default as getCheckoutQuery } from './get-checkout-query'
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'