Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…into swellstores-swell
  • Loading branch information
okbel committed May 26, 2021
2 parents 12d9a75 + 385dc1b commit 508f383
Show file tree
Hide file tree
Showing 71 changed files with 16,620 additions and 15 deletions.
7 changes: 5 additions & 2 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Available providers: bigcommerce, shopify
COMMERCE_PROVIDER=bigcommerce
# Available providers: bigcommerce, shopify, swell
COMMERCE_PROVIDER=

BIGCOMMERCE_STOREFRONT_API_URL=
BIGCOMMERCE_STOREFRONT_API_TOKEN=
Expand All @@ -10,3 +10,6 @@ BIGCOMMERCE_CHANNEL_ID=

NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=
NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=

NEXT_PUBLIC_SWELL_STORE_ID=
NEXT_PUBLIC_SWELL_PUBLIC_KEY=
2 changes: 1 addition & 1 deletion framework/commerce/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const fs = require('fs')
const merge = require('deepmerge')
const prettier = require('prettier')

const PROVIDERS = ['bigcommerce', 'shopify']
const PROVIDERS = ['bigcommerce', 'shopify', 'swell']

function getProviderName() {
return (
Expand Down
5 changes: 5 additions & 0 deletions framework/swell/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SWELL_STORE_DOMAIN=
SWELL_STOREFRONT_ACCESS_TOKEN=

NEXT_PUBLIC_SWELL_STORE_ID=
NEXT_PUBLIC_SWELL_PUBLIC_KEY=
1 change: 1 addition & 0 deletions framework/swell/api/cart/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function () {}
1 change: 1 addition & 0 deletions framework/swell/api/catalog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function () {}
1 change: 1 addition & 0 deletions framework/swell/api/catalog/products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function () {}
20 changes: 20 additions & 0 deletions framework/swell/api/checkout/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import createApiHandler, { SwellApiHandler } from '../utils/create-api-handler'

import { SWELL_CHECKOUT_URL_COOKIE } from '../../const'

import { getConfig } from '..'

const checkoutApi: SwellApiHandler<any> = async (req, res, config) => {
config = getConfig()

const { cookies } = req
const checkoutUrl = cookies[SWELL_CHECKOUT_URL_COOKIE]

if (checkoutUrl) {
res.redirect(checkoutUrl)
} else {
res.redirect('/cart')
}
}

export default createApiHandler(checkoutApi, {}, {})
1 change: 1 addition & 0 deletions framework/swell/api/customer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function () {}
1 change: 1 addition & 0 deletions framework/swell/api/customers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function () {}
1 change: 1 addition & 0 deletions framework/swell/api/customers/login.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function () {}
1 change: 1 addition & 0 deletions framework/swell/api/customers/logout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function () {}
1 change: 1 addition & 0 deletions framework/swell/api/customers/signup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function () {}
50 changes: 50 additions & 0 deletions framework/swell/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { CommerceAPIConfig } from '@commerce/api'

import {
SWELL_CHECKOUT_ID_COOKIE,
SWELL_CUSTOMER_TOKEN_COOKIE,
SWELL_COOKIE_EXPIRE,
} from '../const'

import fetchApi from './utils/fetch-swell-api'

export interface SwellConfig extends CommerceAPIConfig {
fetch: any
}

export class Config {
private config: SwellConfig

constructor(config: SwellConfig) {
this.config = config
}

getConfig(userConfig: Partial<SwellConfig> = {}) {
return Object.entries(userConfig).reduce<SwellConfig>(
(cfg, [key, value]) => Object.assign(cfg, { [key]: value }),
{ ...this.config }
)
}

setConfig(newConfig: Partial<SwellConfig>) {
Object.assign(this.config, newConfig)
}
}

const config = new Config({
locale: 'en-US',
commerceUrl: '',
apiToken: ''!,
cartCookie: SWELL_CHECKOUT_ID_COOKIE,
cartCookieMaxAge: SWELL_COOKIE_EXPIRE,
fetch: fetchApi,
customerCookie: SWELL_CUSTOMER_TOKEN_COOKIE,
})

export function getConfig(userConfig?: Partial<SwellConfig>) {
return config.getConfig(userConfig)
}

export function setConfig(newConfig: Partial<SwellConfig>) {
return config.setConfig(newConfig)
}
25 changes: 25 additions & 0 deletions framework/swell/api/operations/get-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Page } from '../../schema'
import { SwellConfig, getConfig } from '..'

export type GetPageResult<T extends { page?: any } = { page?: Page }> = T

export type PageVariables = {
id: string
}

async function getPage({
url,
variables,
config,
preview,
}: {
url?: string
variables: PageVariables
config?: SwellConfig
preview?: boolean
}): Promise<GetPageResult> {
config = getConfig(config)
return {}
}

export default getPage
58 changes: 58 additions & 0 deletions framework/swell/api/utils/create-api-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
import { SwellConfig, getConfig } from '..'

export type SwellApiHandler<
T = any,
H extends SwellHandlers = {},
Options extends {} = {}
> = (
req: NextApiRequest,
res: NextApiResponse<SwellApiResponse<T>>,
config: SwellConfig,
handlers: H,
// Custom configs that may be used by a particular handler
options: Options
) => void | Promise<void>

export type SwellHandler<T = any, Body = null> = (options: {
req: NextApiRequest
res: NextApiResponse<SwellApiResponse<T>>
config: SwellConfig
body: Body
}) => void | Promise<void>

export type SwellHandlers<T = any> = {
[k: string]: SwellHandler<T, any>
}

export type SwellApiResponse<T> = {
data: T | null
errors?: { message: string; code?: string }[]
}

export default function createApiHandler<
T = any,
H extends SwellHandlers = {},
Options extends {} = {}
>(
handler: SwellApiHandler<T, H, Options>,
handlers: H,
defaultOptions: Options
) {
return function getApiHandler({
config,
operations,
options,
}: {
config?: SwellConfig
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)
}
}
}
7 changes: 7 additions & 0 deletions framework/swell/api/utils/fetch-swell-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { swellConfig } from '../..'

const fetchApi = async (query: string, method: string, variables: [] = []) => {
const { swell } = swellConfig
return swell[query][method](...variables)
}
export default fetchApi
2 changes: 2 additions & 0 deletions framework/swell/api/utils/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import zeitFetch from '@vercel/fetch'
export default zeitFetch()
28 changes: 28 additions & 0 deletions framework/swell/api/utils/is-allowed-method.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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
}
2 changes: 2 additions & 0 deletions framework/swell/api/wishlist/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type WishlistItem = { product: any; id: number }
export default function () {}
74 changes: 74 additions & 0 deletions framework/swell/auth/use-login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
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 {
CustomerAccessTokenCreateInput,
CustomerUserError,
Mutation,
MutationCheckoutCreateArgs,
} from '../schema'
import useLogin, { UseLogin } from '@commerce/auth/use-login'
import { setCustomerToken } from '../utils'

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> = {
fetchOptions: {
query: 'account',
method: 'login',
},
async fetcher({ input: { email, password }, options, fetch }) {
if (!(email && password)) {
throw new CommerceError({
message:
'A first name, last name, email and password are required to login',
})
}

const { customerAccessTokenCreate } = await fetch<
Mutation,
MutationCheckoutCreateArgs
>({
...options,
variables: [email, password],
})

const errors = customerAccessTokenCreate?.customerUserErrors

if (errors && errors.length) {
throw new ValidationError({
message: getErrorMessage(errors[0]),
})
}
const customerAccessToken = customerAccessTokenCreate?.customerAccessToken
const accessToken = customerAccessToken?.accessToken

if (accessToken) {
setCustomerToken(accessToken)
}

return null
},
useHook: ({ fetch }) => () => {
const { revalidate } = useCustomer()

return useCallback(
async function login(input) {
const data = await fetch({ input })
await revalidate()
return data
},
[fetch, revalidate]
)
},
}
36 changes: 36 additions & 0 deletions framework/swell/auth/use-logout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types'
import useLogout, { UseLogout } from '@commerce/auth/use-logout'
import useCustomer from '../customer/use-customer'
import { getCustomerToken, setCustomerToken } from '../utils/customer-token'

export default useLogout as UseLogout<typeof handler>

export const handler: MutationHook<null> = {
fetchOptions: {
query: 'account',
method: 'logout',
},
async fetcher({ options, fetch }) {
await fetch({
...options,
variables: {
customerAccessToken: getCustomerToken(),
},
})
setCustomerToken(null)
return null
},
useHook: ({ fetch }) => () => {
const { mutate } = useCustomer()

return useCallback(
async function logout() {
const data = await fetch()
await mutate(null, false)
return data
},
[fetch, mutate]
)
},
}
Loading

0 comments on commit 508f383

Please sign in to comment.