Skip to content

Commit 9a4772c

Browse files
kibo-kevinwattskibo-chandradeeptalahaChandradeeptakibo-geetanshuchhabrakibo-sushant
authored
feat(provider): Implement Kibo Commerce provider (vercel#575)
* Icky 161 folder and env setup (vercel#1) * folder and env setup * codegen.json headers removed Co-authored-by: Chandradeepta <[email protected]> * Feature/icky 194 (vercel#5) * folder and env setup * codegen.json headers removed * use-cart code flow updated * use-cart code flow updated * Implemented get-cart functionality * removed unused file * getAnonymousShopperToken function added * normalization mapping updated * PR points resolved * Anonymous shopper token query added * getAnonymousShopperToken function added * Anonymous shopper token query added Co-authored-by: Chandradeepta <[email protected]> * Icky 175 (vercel#3) * folder and env setup * codegen.json headers removed * icky-175-get-site-info * PR comments resolved * update category Id Co-authored-by: Chandradeepta <[email protected]> * Icky-169-LogIn (vercel#4) * Update README.md * Initial Commit * Commited Keys * GraphQL Changes * GraphQL Query * Final Changes * Changed login.ts * Made changes in login.ts * Final Changes * Refactored login.ts * SignUp Initial Checkin * logout Initial * Customer Account Initial Commit * Logout - deleted cookie * Reverted ReadMe and UserNav file * Final Changes * Resolved comments * Resolved comments 1 * Resolved comments 2 * Resolved comments 3 * Resolved comments 4 Co-authored-by: SushantJadhav <[email protected]> * ICKY-166-getProducts-and-getProduct (vercel#6) * GetProduct Initial Commit * Passed productCode as Slug to get-product * Moved currencyCode in Config file * Icky 173 (vercel#9) * Initial commit related to getAllPages * Initial Changes * Making documentListName configurable * fixing dynamic page rendering and adding typescript code Co-authored-by: amolnadagonde <[email protected]> Co-authored-by: kibo-sushant <[email protected]> * Feature/icky 176 (vercel#8) * GetProduct Initial Commit * addItemToCart function implemneted * Add Item to cart functionality implemented * ICKY-166-getProducts-and-getProduct (vercel#6) * GetProduct Initial Commit * Passed productCode as Slug to get-product * Moved currencyCode in Config file * Icky 173 (vercel#9) * Initial commit related to getAllPages * Initial Changes * Making documentListName configurable * fixing dynamic page rendering and adding typescript code Co-authored-by: amolnadagonde <[email protected]> Co-authored-by: kibo-sushant <[email protected]> * addItemToCart function implemneted Conflicts resolved * Add Item to cart functionality implemented * booleans removed from query * cart size option enabled * updated addItem for with and without variants products Co-authored-by: kibo-sushant <[email protected]> Co-authored-by: Chandradeepta <[email protected]> Co-authored-by: kibo-sushant <[email protected]> Co-authored-by: kibo-kevinwatts <[email protected]> Co-authored-by: amolnadagonde <[email protected]> * Removed types from schema.d.ts (vercel#11) * Final Changes (vercel#16) * Icky 177 (vercel#13) * addItemToCart function implemneted Conflicts resolved * Add Item to cart functionality implemented * remove Item from cart implemented * removed unused code Co-authored-by: Chandradeepta <[email protected]> * Icky 178 - Update Cart Quantity implemented (vercel#14) * update cart quantity implemented * add item to cart bug fixed Co-authored-by: Chandradeepta <[email protected]> * Icky 182 (vercel#12) * initial commit * useSearch hook * remove extra spaces * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * useSearch hook * remove extra spaces * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * changes in product-search-vars * remove unwanted boolean * Feature/icky 179 (vercel#17) * initial commit * useSearch hook * remove extra spaces * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * useSearch hook * remove extra spaces * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * changes in product-search-vars * remove unwanted boolean * initial commit * updated Provider * usewishlist/getwishlist * changes in provider.ts * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * usewishlist/getwishlist * updated Provider * changes in provider.ts * normalize wishlistitem * changes in get-customer-account * remove unwanted code * resolve empty wishlist case * resolve pr comments Co-authored-by: kibo-sushant <[email protected]> * token encoding and decoding fixed (vercel#19) Co-authored-by: Chandradeepta <[email protected]> * Feature/icky- 180 & 181 (vercel#20) * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * usewishlist/getwishlist * changes in provider.ts * changes in get-customer-account * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * usewishlist/getwishlist * changes in provider.ts * remove unwanted code * initial commit * resolve pr comments * changes in add-item * remove wishlist fragment * remove item from wishlist * changes in normalize.ts * changes in additemtowishlist mutation * resove pr comments * Feature/icky 291 (vercel#22) * Kibo API authentication helper handling oauth token generation / refresh and making auth ticket available to process via next server runtime config * Update .env template with placeholder for Kibo Auth Url * resolve ICKY-275 (vercel#24) * Fix/icky 276 (vercel#21) * remove Item from cart implemented * update cart quantity implemented * removed unused code * ICKY 176 and 263 implemented * ICKY 263 removed * PR points resolved Co-authored-by: Chandradeepta <[email protected]> * ICKY-263 (vercel#23) * ICKY-263 * resolve pr comments * resolve pr comments for customer typescript * docs: update kibo commerce readme with env details (vercel#26) * resolve icky-264 (vercel#25) * chore: remove extra field from .env.template * chore: remove extra .vscode launch json file * chore: cleanup test message from kibocommerce fork * chore(docs): remove extra field from .env template and .env related docs * chore: remove test data json file * chore: delete yarn.lock * refactor: remove unused fetch from kibo config, remove unused CommerceProvider * refactor: rename queries and util modules for consistency * chore: add checkout related api noop handlers and hooks * chore: revert modified core files * chore(config): add kibo production sandbox cdn to image domains config * fix: page normalizer and query for static pages * chore: remove commented code and unnecessary imports * fix(module paths): switch framework alias for relative path Co-authored-by: kibo-chandradeeptalaha <[email protected]> Co-authored-by: Chandradeepta <[email protected]> Co-authored-by: kibo-geetanshuchhabra <[email protected]> Co-authored-by: kibo-sushant <[email protected]> Co-authored-by: SushantJadhav <[email protected]> Co-authored-by: amolnadagonde <[email protected]> Co-authored-by: kibo-sushant <[email protected]> Co-authored-by: kibo-amolnadagonde <[email protected]>
1 parent d77d000 commit 9a4772c

File tree

108 files changed

+23610
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+23610
-3
lines changed

.env.template

+7
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,10 @@ NEXT_PUBLIC_VENDURE_LOCAL_URL=
2727
ORDERCLOUD_CLIENT_ID=
2828
ORDERCLOUD_CLIENT_SECRET=
2929
STRIPE_SECRET=
30+
31+
KIBO_API_URL=
32+
KIBO_CLIENT_ID=
33+
KIBO_SHARED_SECRET=
34+
KIBO_CART_COOKIE=
35+
KIBO_CUSTOMER_COOKIE=
36+
KIBO_API_HOST=

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,4 @@ After Email confirmation, Checkout should be manually enabled through BigCommerc
155155
<br>
156156
<br>
157157
BigCommerce team has been notified and they plan to add more details about this subject.
158-
</details>
158+
</details>

framework/commerce/config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const PROVIDERS = [
1515
'swell',
1616
'vendure',
1717
'ordercloud',
18-
'spree',
18+
'kibocommerce',
19+
'spree'
1920
]
2021

2122
function getProviderName() {

framework/kibocommerce/.env.template

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
COMMERCE_PROVIDER=kibocommerce
2+
KIBO_API_URL=
3+
KIBO_CART_COOKIE=
4+
KIBO_CUSTOMER_COOKIE=
5+
KIBO_CLIENT_ID=
6+
KIBO_SHARED_SECRET=
7+
KIBO_AUTH_URL=

framework/kibocommerce/README.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Kibo Commerce Provider
2+
3+
If you already have a Kibo Commerce account and want to use your current store, then copy the `.env.template` file in this directory to `.env.local` in the main directory (which will be ignored by Git):
4+
5+
```bash
6+
cp framework/kibocommerce/.env.template .env.local
7+
```
8+
9+
Then, set the environment variables in `.env.local` to match the ones from your store.
10+
11+
```
12+
COMMERCE_PROVIDER='kibocommerce'
13+
KIBO_API_URL= 'https://t1234-s1234.sandbox.mozu.com/graphql'
14+
KIBO_CART_COOKIE='kibo_cart'
15+
KIBO_CUSTOMER_COOKIE='kibo_customer'
16+
KIBO_CLIENT_ID='KIBO.APP.1.0.0.Release'
17+
KIBO_SHARED_SECRET='12345secret'
18+
KIBO_AUTH_URL='https://home.mozu.com'
19+
```
20+
21+
- `KIBO_API_URL` - link to your Kibo Commerce GraphQL API instance.
22+
- `KIBO_CART_COOKIE` - configurable cookie name for cart.
23+
- `KIBO_CUSTOMER_COOKIE` - configurable cookie name for shopper identifier/authentication cookie
24+
- `KIBO_CLIENT_ID` - Unique Application (Client) ID of your Application
25+
- `KIBO_SHARED_SECRET` - Secret API key used to authenticate application/client id.
26+
27+
28+
Your Kibo Client ID and Shared Secret can be found from your [Kibo eCommerce Dev Center](https://mozu.com/login)
29+
30+
Visit [Kibo documentation](https://apidocs.kibong-perf.com/?spec=graphql#auth) for more details on API authentication
31+
32+
Based on the config, this integration will handle Authenticating your application against the Kibo API using the Kibo Client ID and Kibo Shared Secret.
33+
## Contribute
34+
35+
Our commitment to Open Source can be found [here](https://vercel.com/oss).
36+
37+
If you find an issue with the provider or want a new feature, feel free to open a PR or [create a new issue](https://github.com/vercel/commerce/issues).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { Product } from './../../../schema.d'
2+
import { normalizeCart } from '../../../lib/normalize'
3+
import type { CartEndpoint } from '.'
4+
import addToCurrentCartMutation from '../../../api/mutations/addToCart-mutation'
5+
6+
import { getProductQuery } from '../../../api/queries/get-product-query'
7+
import { getCartQuery } from '../../../api/queries/get-cart-query'
8+
import CookieHandler from '../../../api/utils/cookie-handler'
9+
10+
const buildAddToCartVariables = ({
11+
productId,
12+
variantId,
13+
quantity = 1,
14+
productResponse,
15+
}: {
16+
productId: string
17+
variantId: string
18+
quantity: number
19+
productResponse: any
20+
}) => {
21+
const { product } = productResponse.data
22+
23+
const selectedOptions = product.variations?.find(
24+
(v: any) => v.productCode === variantId
25+
).options
26+
27+
let options: any[] = []
28+
selectedOptions?.forEach((each: any) => {
29+
product?.options
30+
.filter((option: any) => {
31+
return option.attributeFQN == each.attributeFQN
32+
})
33+
.forEach((po: any) => {
34+
options.push({
35+
attributeFQN: po.attributeFQN,
36+
name: po.attributeDetail.name,
37+
value: po.values?.find((v: any) => v.value == each.value).value,
38+
})
39+
})
40+
})
41+
42+
return {
43+
productToAdd: {
44+
product: {
45+
productCode: productId,
46+
variationProductCode: variantId ? variantId : null,
47+
options,
48+
},
49+
quantity,
50+
fulfillmentMethod: 'Ship',
51+
},
52+
}
53+
}
54+
55+
const addItem: CartEndpoint['handlers']['addItem'] = async ({
56+
req,
57+
res,
58+
body: { cartId, item },
59+
config,
60+
}) => {
61+
if (!item) {
62+
return res.status(400).json({
63+
data: null,
64+
errors: [{ message: 'Missing item' }],
65+
})
66+
}
67+
if (!item.quantity) item.quantity = 1
68+
69+
const productResponse = await config.fetch(getProductQuery, {
70+
variables: { productCode: item?.productId },
71+
})
72+
73+
const cookieHandler = new CookieHandler(config, req, res)
74+
let accessToken = null
75+
76+
if (!cookieHandler.getAccessToken()) {
77+
let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken()
78+
accessToken = anonymousShopperTokenResponse.accessToken;
79+
} else {
80+
accessToken = cookieHandler.getAccessToken()
81+
}
82+
83+
const addToCartResponse = await config.fetch(
84+
addToCurrentCartMutation,
85+
{
86+
variables: buildAddToCartVariables({ ...item, productResponse }),
87+
},
88+
{ headers: { 'x-vol-user-claims': accessToken } }
89+
)
90+
let currentCart = null
91+
if (addToCartResponse.data.addItemToCurrentCart) {
92+
let result = await config.fetch(
93+
getCartQuery,
94+
{},
95+
{ headers: { 'x-vol-user-claims': accessToken } }
96+
)
97+
currentCart = result?.data?.currentCart
98+
}
99+
res.status(200).json({ data: normalizeCart(currentCart) })
100+
}
101+
102+
export default addItem
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import CookieHandler from '../../../api/utils/cookie-handler'
2+
import { normalizeCart } from '../../../lib/normalize'
3+
import { Cart } from '../../../schema'
4+
import type { CartEndpoint } from '.'
5+
import { getCartQuery } from '../../queries/get-cart-query'
6+
7+
const getCart: CartEndpoint['handlers']['getCart'] = async ({
8+
req,
9+
res,
10+
body: { cartId },
11+
config,
12+
}) => {
13+
let currentCart: Cart = {}
14+
try {
15+
const cookieHandler = new CookieHandler(config, req, res)
16+
let accessToken = null
17+
18+
if (!cookieHandler.getAccessToken()) {
19+
let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken()
20+
const response = anonymousShopperTokenResponse.response
21+
accessToken = anonymousShopperTokenResponse.accessToken
22+
cookieHandler.setAnonymousShopperCookie(response)
23+
} else {
24+
accessToken = cookieHandler.getAccessToken()
25+
}
26+
27+
let result = await config.fetch(
28+
getCartQuery,
29+
{},
30+
{ headers: { 'x-vol-user-claims': accessToken } }
31+
)
32+
currentCart = result?.data?.currentCart
33+
} catch (error) {
34+
throw error
35+
}
36+
res.status(200).json({
37+
data: currentCart ? normalizeCart(currentCart) : null,
38+
})
39+
}
40+
41+
export default getCart
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { GetAPISchema, createEndpoint } from '@commerce/api'
2+
import cartEndpoint from '@commerce/api/endpoints/cart'
3+
import type { KiboCommerceAPI } from '../..'
4+
import getCart from './get-cart';
5+
import addItem from './add-item';
6+
import updateItem from './update-item'
7+
import removeItem from './remove-item'
8+
9+
export type CartAPI = GetAPISchema<KiboCommerceAPI, any>
10+
11+
export type CartEndpoint = CartAPI['endpoint']
12+
13+
export const handlers: CartEndpoint['handlers'] = {
14+
getCart,
15+
addItem,
16+
updateItem,
17+
removeItem,
18+
}
19+
20+
const cartApi = createEndpoint<CartAPI>({
21+
handler: cartEndpoint,
22+
handlers,
23+
})
24+
25+
export default cartApi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { normalizeCart } from '../../../lib/normalize'
2+
import type { CartEndpoint } from '.'
3+
import removeItemFromCartMutation from '../../../api/mutations/removeItemFromCart-mutation'
4+
import { getCartQuery } from '../../../api/queries/get-cart-query'
5+
6+
const removeItem: CartEndpoint['handlers']['removeItem'] = async ({
7+
req,
8+
res,
9+
body: { cartId, itemId },
10+
config,
11+
}) => {
12+
if (!itemId) {
13+
return res.status(400).json({
14+
data: null,
15+
errors: [{ message: 'Invalid request' }],
16+
})
17+
}
18+
const encodedToken = req.cookies[config.customerCookie]
19+
const token = encodedToken
20+
? Buffer.from(encodedToken, 'base64').toString('ascii')
21+
: null
22+
23+
const accessToken = token ? JSON.parse(token).accessToken : null
24+
25+
const removeItemResponse = await config.fetch(
26+
removeItemFromCartMutation,
27+
{
28+
variables: { id: itemId },
29+
},
30+
{ headers: { 'x-vol-user-claims': accessToken } }
31+
)
32+
33+
let currentCart = null
34+
if (removeItemResponse.data.deleteCurrentCartItem) {
35+
let result = await config.fetch(
36+
getCartQuery,
37+
{},
38+
{ headers: { 'x-vol-user-claims': accessToken } }
39+
)
40+
currentCart = result?.data?.currentCart
41+
}
42+
res.status(200).json({ data: normalizeCart(currentCart) })
43+
}
44+
45+
export default removeItem
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { normalizeCart } from '../../../lib/normalize'
2+
import type { CartEndpoint } from '.'
3+
import { getCartQuery } from '../../../api/queries/get-cart-query'
4+
import updateCartItemQuantityMutation from '../../../api/mutations/updateCartItemQuantity-mutation'
5+
6+
const updateItem: CartEndpoint['handlers']['updateItem'] = async ({
7+
req,
8+
res,
9+
body: { cartId, itemId, item },
10+
config,
11+
}) => {
12+
if (!itemId || !item) {
13+
return res.status(400).json({
14+
data: null,
15+
errors: [{ message: 'Invalid request' }],
16+
})
17+
}
18+
const encodedToken = req.cookies[config.customerCookie]
19+
const token = encodedToken
20+
? Buffer.from(encodedToken, 'base64').toString('ascii')
21+
: null
22+
23+
const accessToken = token ? JSON.parse(token).accessToken : null
24+
25+
const updateItemResponse = await config.fetch(
26+
updateCartItemQuantityMutation,
27+
{
28+
variables: { itemId: itemId, quantity: item.quantity },
29+
},
30+
{ headers: { 'x-vol-user-claims': accessToken } }
31+
)
32+
33+
let currentCart = null
34+
if (updateItemResponse.data) {
35+
let result = await config.fetch(
36+
getCartQuery,
37+
{},
38+
{ headers: { 'x-vol-user-claims': accessToken } }
39+
)
40+
currentCart = result?.data?.currentCart
41+
}
42+
res.status(200).json({ data: normalizeCart(currentCart) })
43+
}
44+
45+
export default updateItem
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { GetAPISchema, createEndpoint } from '@commerce/api'
2+
import productsEndpoint from '@commerce/api/endpoints/catalog/products'
3+
import type { KiboCommerceAPI } from '../../..'
4+
import getProducts from '../products/products'
5+
6+
export type ProductsAPI = GetAPISchema<KiboCommerceAPI, any>
7+
8+
export type ProductsEndpoint = ProductsAPI['endpoint']
9+
10+
export const handlers: ProductsEndpoint['handlers'] = { getProducts }
11+
12+
const productsApi = createEndpoint<ProductsAPI>({
13+
handler: productsEndpoint,
14+
handlers,
15+
})
16+
17+
export default productsApi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Product } from '@commerce/types/product'
2+
import { ProductsEndpoint } from '.'
3+
import productSearchQuery from '../../../queries/product-search-query'
4+
import { buildProductSearchVars } from '../../../../lib/product-search-vars'
5+
import {normalizeProduct} from '../../../../lib/normalize'
6+
7+
const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
8+
res,
9+
body: { search, categoryId, brandId, sort },
10+
config,
11+
}) => {
12+
const pageSize = 100;
13+
const filters = {};
14+
const startIndex = 0;
15+
const variables = buildProductSearchVars({
16+
categoryCode: categoryId,
17+
pageSize,
18+
search,
19+
sort,
20+
filters,
21+
startIndex,
22+
})
23+
const {data} = await config.fetch(productSearchQuery, { variables });
24+
const found = data?.products?.items?.length > 0 ? true : false;
25+
let productsResponse= data?.products?.items.map((item: any) =>normalizeProduct(item,config));
26+
const products: Product[] = found ? productsResponse : [];
27+
28+
res.status(200).json({ data: { products, found } });
29+
}
30+
31+
export default getProducts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default function noopApi(...args: any[]): void {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default function noopApi(...args: any[]): void {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default function noopApi(...args: any[]): void {}

0 commit comments

Comments
 (0)