Skip to content

Commit

Permalink
Merge pull request #46 from bigcommerce/develop
Browse files Browse the repository at this point in the history
v1.2.0
  • Loading branch information
jorgemasta authored Mar 19, 2021
2 parents 9333763 + 67d372f commit e6196b0
Show file tree
Hide file tree
Showing 18 changed files with 654 additions and 2,919 deletions.
34 changes: 31 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
# 1.0.1
# 1.2.0
### Embedded checkout

- Now the `SHOP_TOKEN` is set at TLD (Top Level Domain) so the subdomain (where the embedded checkout iframe lives) can access the token
- Added a new section in the readme with some notes about how to make the checkout work
- Modified the `logout` endpoint to be `POST` to prevent unwanted caching

*Resolves [#36](https://github.com/bigcommerce/storefront-data-hooks/issues/36)*
*Resolves [#6](https://github.com/bigcommerce/storefront-data-hooks/issues/6)*

### Create `useOrders` hooks

Allow to fetch the orders using a new `useOrders` hook.

- If the user is not logged in, nothing is returned.
- If the user is logged in, it returns an array with their orders.
- Get the customer ID on the server (through the token) to avoid security issues.

*Resolves [#25](https://github.com/bigcommerce/storefront-data-hooks/issues/25)*

- Initial public release in support of https://nextjs.org/commerce
### Add locale to cart

When creating the carts, you can specify the locale. By default it's **en**, now the package checks the locale of the app to add it to the request.

⚠️ This adds as a Peer Dependency `next`.

*Resolves [#30](https://github.com/bigcommerce/storefront-data-hooks/issues/30)*

# 1.1.0

Expand Down Expand Up @@ -30,4 +54,8 @@
### Fix `useAddItem` types
- Set `variantId` as optional

*Resolves [#21](https://github.com/bigcommerce/storefront-data-hooks/issues/21)*
*Resolves [#21](https://github.com/bigcommerce/storefront-data-hooks/issues/21)*

# 1.0.1

- Initial public release in support of https://nextjs.org/commerce
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Table of Contents
* [useSearch](#usesearch)
* [getAllProducts](#getallproducts)
* [getProduct](#getproduct)
* [Checkout](#checkout)
* [Troubleshooting](#troubleshooting)
* [More](#more)

Expand Down Expand Up @@ -391,6 +392,19 @@ const { product } = await getProduct({
})
```
## Checkout
> The checkout only works on **production** and with a custom domain
The recommended method is the [Embedded Checkout](https://developer.bigcommerce.com/api-docs/storefronts/embedded-checkout/embedded-checkout-tutorial), follow the tutorial to create a channel and a site. Notes:
- The channel should be of type `storefront`
- The site url must be your production url (e.g: https://mystorefront.com)
- This package takes care of the cart and redirect links creation
- Your bigcommerce storefront must be a subdomain of your headless storefront (eg: https://bc.mystorefront.com)
![example image](https://cdn-std.droplr.net/files/acc_896732/CSo83V)
## Troubleshooting
<details>
<summary>When I try to create a customer with <code>useSignup</code>, I receive an error but the user is created</summary>
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bigcommerce/storefront-data-hooks",
"version": "1.1.0",
"version": "1.2.0",
"main": "index.js",
"repository": "[email protected]:bigcommerce/storefront-data-hooks.git",
"license": "MIT",
Expand Down Expand Up @@ -43,12 +43,13 @@
"@types/node": "^14.14.3",
"@types/react": "^16.9.53",
"graphql": "^15.4.0",
"next": "^9.5.5",
"next": "^10.0.8",
"prettier": "^2.1.2",
"rimraf": "^3.0.2",
"typescript": "^4.0.3"
},
"peerDependencies": {
"next": ">=10",
"react": ">=17"
}
}
5 changes: 4 additions & 1 deletion src/api/cart/handlers/add-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { CartHandlers } from '..'
// Return current cart info
const addItem: CartHandlers['addItem'] = async ({
res,
body: { cartId, item },
body: { cartId, locale, item },
config,
}) => {
if (!item) {
Expand All @@ -23,6 +23,9 @@ const addItem: CartHandlers['addItem'] = async ({
...(!cartId && config.storeChannelId
? { channel_id: config.storeChannelId }
: {}),
...(!cartId
? { locale }
: {}),
}),
}
const { data } = cartId
Expand Down
2 changes: 1 addition & 1 deletion src/api/cart/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export type ItemBody = {
optionSelections?: OptionSelections
}

export type AddItemBody = { item: ItemBody }
export type AddItemBody = { item: ItemBody, locale?: string }

export type UpdateItemBody = { itemId: string; item: ItemBody }

Expand Down
3 changes: 1 addition & 2 deletions src/api/checkout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import createApiHandler, {
import { BigcommerceApiError } from './utils/errors'

const METHODS = ['GET']
const fullCheckout = true
const fullCheckout = false

// TODO: a complete implementation should have schema validation for `req.body`
const checkoutApi: BigcommerceApiHandler<any> = async (req, res, config) => {
Expand All @@ -32,7 +32,6 @@ const checkoutApi: BigcommerceApiHandler<any> = async (req, res, config) => {
return
}

// TODO: make the embedded checkout work too!
const html = `
<!DOCTYPE html>
<html lang="en">
Expand Down
3 changes: 2 additions & 1 deletion src/api/customers/handlers/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { LoginHandlers } from '../login'
const invalidCredentials = /invalid credentials/i

const loginHandler: LoginHandlers['login'] = async ({
req,
res,
body: { email, password },
config,
Expand All @@ -21,7 +22,7 @@ const loginHandler: LoginHandlers['login'] = async ({
// and numeric characters.

try {
await login({ variables: { email, password }, config, res })
await login({ variables: { email, password }, config, req, res })
} catch (error) {
// Check if the email and password didn't match an existing account
if (
Expand Down
4 changes: 3 additions & 1 deletion src/api/customers/handlers/logout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import { serialize } from 'cookie'
import { LogoutHandlers } from '../logout'

const logoutHandler: LogoutHandlers['logout'] = async ({
req: request,
res,
body: { redirectTo },
config,
}) => {
const { host } = request.headers
// Remove the cookie
res.setHeader(
'Set-Cookie',
serialize(config.customerCookie, '', { maxAge: -1, path: '/' })
serialize(config.customerCookie, '', { maxAge: -1, path: '/', domain: host?.includes(':') ? host?.slice(0, host.indexOf(':')) : host })
)

// Only allow redirects to a relative URL
Expand Down
3 changes: 2 additions & 1 deletion src/api/customers/handlers/signup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import login from '../../operations/login'
import { SignupHandlers } from '../signup'

const signup: SignupHandlers['signup'] = async ({
req,
res,
body: { firstName, lastName, email, password },
config,
Expand Down Expand Up @@ -54,7 +55,7 @@ const signup: SignupHandlers['signup'] = async ({
}

// Login the customer right after creating it
await login({ variables: { email, password }, res, config })
await login({ variables: { email, password }, req, res, config })

res.status(200).json({ data: null })
}
Expand Down
7 changes: 5 additions & 2 deletions src/api/customers/logout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,25 @@ import createApiHandler, {
BigcommerceHandler,
} from '../utils/create-api-handler'
import isAllowedMethod from '../utils/is-allowed-method'
import warnDeprecatedMethod from '../utils/warn-deprecated-method'
import { BigcommerceApiError } from '../utils/errors'
import logout from './handlers/logout'

export type LogoutHandlers = {
logout: BigcommerceHandler<null, { redirectTo?: string }>
}

const METHODS = ['GET']
const METHODS = ['POST']
const DEPRECATED_METHODS = ['GET']

const logoutApi: BigcommerceApiHandler<null, LogoutHandlers> = async (
req,
res,
config,
handlers
) => {
if (!isAllowedMethod(req, res, METHODS)) return
if (!isAllowedMethod(req, res, [...METHODS, ...DEPRECATED_METHODS])) return
warnDeprecatedMethod(req, DEPRECATED_METHODS)

try {
const redirectTo = req.query.redirect_to
Expand Down
12 changes: 11 additions & 1 deletion src/api/operations/login.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { ServerResponse } from 'http'
import type { NextApiRequest } from 'next'

import type { LoginMutation, LoginMutationVariables } from '../../schema'
import type { RecursivePartial } from '../utils/types'
import concatHeader from '../utils/concat-cookie'
Expand All @@ -19,24 +21,28 @@ export type LoginVariables = LoginMutationVariables
async function login(opts: {
variables: LoginVariables
config?: BigcommerceConfig
req: NextApiRequest
res: ServerResponse
}): Promise<LoginResult>

async function login<T extends { result?: any }, V = any>(opts: {
query: string
variables: V
req: NextApiRequest
res: ServerResponse
config?: BigcommerceConfig
}): Promise<LoginResult<T>>

async function login({
query = loginMutation,
variables,
req: request,
res: response,
config,
}: {
query?: string
variables: LoginVariables
req: NextApiRequest
res: ServerResponse
config?: BigcommerceConfig
}): Promise<LoginResult> {
Expand All @@ -50,11 +56,15 @@ async function login({
let cookie = res.headers.get('Set-Cookie')

if (cookie && typeof cookie === 'string') {
const { host } = request.headers
// Set the cookie at TLD to make it accessible on subdomains
cookie = cookie + `; Domain=${host?.includes(':') ? host?.slice(0, host.indexOf(':')) : host}`

// In development, don't set a secure cookie or the browser will ignore it
if (process.env.NODE_ENV !== 'production') {
cookie = cookie.replace('; Secure', '')
// SameSite=none can't be set unless the cookie is Secure
// bc seems to sometimes send back SameSite=None rather than none so make
// bc seems to sometimes send back SameSite=None rather than none so make
// this case insensitive
cookie = cookie.replace(/; SameSite=none/gi, '; SameSite=lax')
}
Expand Down
31 changes: 31 additions & 0 deletions src/api/orders/handlers/get-orders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Orders, OrdersHandlers } from '..'
import getCustomerId from '../../operations/get-customer-id'

const getOrders: OrdersHandlers['getOrders'] = async ({
res,
body: { customerToken },
config,
}) => {
let result: Orders = []
if (customerToken) {
const customerId = customerToken && (await getCustomerId({ customerToken, config }))

if (!customerId) {
// If the customerToken is invalid, then this request is too
return res.status(404).json({
data: null,
errors: [{ message: 'Orders not found' }],
})
}

result = await config.storeApiFetch(`/v2/orders?customer_id=${customerId}`, {
headers: {
Accept: "application/json",
}
})
}

res.status(200).json({ data: result ?? null })
}

export default getOrders
Loading

0 comments on commit e6196b0

Please sign in to comment.