Initial api integration

Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
Chloe 2022-04-13 16:42:09 +07:00
parent bba842950c
commit e3ad683cf2
15 changed files with 291 additions and 0 deletions

View File

@ -0,0 +1,3 @@
COMMERCE_PROVIDER=shopify
OPENCOMMERCE_STOREFRONT_API_URL=

View File

@ -0,0 +1,2 @@
node_modules
dist

View File

@ -0,0 +1,6 @@
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"useTabs": false
}

View File

@ -0,0 +1,79 @@
{
"name": "@vercel/commerce-opencommerce",
"version": "0.0.1",
"license": "MIT",
"scripts": {
"release": "taskr release",
"build": "taskr build",
"dev": "taskr",
"types": "tsc --emitDeclarationOnly",
"prettier-fix": "prettier --write ."
},
"sideEffects": false,
"type": "module",
"exports": {
".": "./dist/index.js",
"./*": [
"./dist/*.js",
"./dist/*/index.js"
],
"./next.config": "./dist/next.config.cjs"
},
"typesVersions": {
"*": {
"*": [
"src/*",
"src/*/index"
],
"next.config": [
"dist/next.config.d.cts"
]
}
},
"files": [
"dist"
],
"publishConfig": {
"typesVersions": {
"*": {
"*": [
"dist/*.d.ts",
"dist/*/index.d.ts"
],
"next.config": [
"dist/next.config.d.cts"
]
}
}
},
"dependencies": {
"@vercel/commerce": "^0.0.1",
"@vercel/fetch": "^6.1.1"
},
"peerDependencies": {
"next": "^12",
"react": "^17",
"react-dom": "^17"
},
"devDependencies": {
"@taskr/clear": "^1.1.0",
"@taskr/esnext": "^1.1.0",
"@taskr/watch": "^1.1.0",
"@types/node": "^17.0.8",
"@types/react": "^17.0.38",
"lint-staged": "^12.1.7",
"next": "^12.0.8",
"prettier": "^2.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"taskr": "^1.1.0",
"taskr-swc": "^0.0.1",
"typescript": "^4.5.4"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx,json}": [
"prettier --write",
"git add"
]
}
}

View File

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

View File

@ -0,0 +1,40 @@
import {
CommerceAPI,
CommerceAPIConfig,
getCommerceApi as commerceApi,
} from '@vercel/commerce/api'
import createFetchGraphqlApi from './utils/fetch-grapql-api'
const API_URL = process.env.OPENCOMMERCE_STOREFRONT_API_URL
if (!API_URL) {
throw new Error(
`The environment variable OPENCOMMERCE_STOREFRONT_API_URL is missing and it's required to access your store`
)
}
export interface OpenCommerceConfig extends CommerceAPIConfig {}
const ONE_DAY = 60 * 60 * 24
const config: OpenCommerceConfig = {
commerceUrl: `${API_URL}/graphql`,
apiToken: '',
customerCookie: 'opencommerce_customerToken',
cartCookie: 'opencommerce_cartId',
cartCookieMaxAge: ONE_DAY * 30,
fetch: createFetchGraphqlApi(() => getCommerceApi().getConfig()),
}
const operations = {}
export const provider = { config, operations }
export type Provider = typeof provider
export type OpenCommerceAPI<P extends Provider = Provider> = CommerceAPI<P>
export function getCommerceApi<P extends Provider>(
customProvider: P = provider as any
): OpenCommerceAPI<P> {
return commerceApi(customProvider)
}

View File

@ -0,0 +1,59 @@
import type {
OperationContext,
OperationOptions,
} from '@vercel/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>({
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,39 @@
import { FetcherError } from '@vercel/commerce/utils/errors'
import type { GraphQLFetcher } from '@vercel/commerce/api'
import type { OpenCommerceConfig } from '../index'
import fetch from './fetch'
const fetchGraphqlApi: (
getConfig: () => OpenCommerceConfig
) => GraphQLFetcher =
(getConfig) =>
async (query: string, { variables, preview } = {}, fetchOptions) => {
// log.warn(query)
const config = getConfig()
const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
...fetchOptions,
method: 'POST',
headers: {
...fetchOptions?.headers,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
variables,
}),
})
const json = await res.json()
if (json.errors) {
throw new FetcherError({
errors: json.errors ?? [
{ message: 'Failed to fetch OpenCommerce API' },
],
status: res.status,
})
}
return { data: json.data, res }
}
export default fetchGraphqlApi

View File

@ -0,0 +1,3 @@
import vercelFetch from '@vercel/fetch'
export default vercelFetch()

View File

@ -0,0 +1,10 @@
{
"provider": "opencommerce",
"features": {
"wishlist": false,
"cart": true,
"search": true,
"customerAuth": false,
"customCheckout": true
}
}

View File

View File

@ -0,0 +1,8 @@
const commerce = require('./commerce.config.json')
module.exports = {
commerce,
images: {
domains: ['localhost'],
},
}

View File

View File

@ -0,0 +1,20 @@
export async function build(task, opts) {
await task
.source('src/**/*.+(ts|tsx|js)')
.swc({ dev: opts.dev, outDir: 'dist', baseUrl: 'src' })
.target('dist')
.source('src/**/*.+(cjs|json)')
.target('dist')
task.$.log('Compiled src files')
}
export async function release(task) {
await task.clear('dist').start('build')
}
export default async function dev(task) {
const opts = { dev: true }
await task.clear('dist')
await task.start('build', opts)
await task.watch('src/**/*.+(ts|tsx|js|cjs|json)', 'build', opts)
}

View File

@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"outDir": "dist",
"baseUrl": "src",
"lib": ["dom", "dom.iterable", "esnext"],
"declaration": true,
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"incremental": true,
"jsx": "react-jsx"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}