Skip to content

Commit

Permalink
feat(provider): Implement Kibo Commerce provider (vercel#575)
Browse files Browse the repository at this point in the history
* Icky 161 folder and env setup (vercel#1)

* folder and env setup

* codegen.json headers removed

Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com>

* 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 <43542673+Chandradeepta@users.noreply.github.com>

* 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 <43542673+Chandradeepta@users.noreply.github.com>

* 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 <Sushant.Jadhav@kibocommerce.com>

* 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 <amol.nadagonde@kibocommerce.com>
Co-authored-by: kibo-sushant <sushant.jadhav@blueconchtech.com>

* 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 <amol.nadagonde@kibocommerce.com>
Co-authored-by: kibo-sushant <sushant.jadhav@blueconchtech.com>

* 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 <sushant.jadhav@blueconchtech.com>
Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com>
Co-authored-by: kibo-sushant <89385472+kibo-sushant@users.noreply.github.com>
Co-authored-by: kibo-kevinwatts <85258296+kibo-kevinwatts@users.noreply.github.com>
Co-authored-by: amolnadagonde <amol.nadagonde@kibocommerce.com>

* 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 <43542673+Chandradeepta@users.noreply.github.com>

* Icky 178 - Update Cart Quantity implemented (vercel#14)

* update cart quantity implemented

* add item to cart bug fixed

Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com>

* 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 <sushant.jadhav@blueconchtech.com>

* token encoding and decoding fixed (vercel#19)

Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com>

* 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 <43542673+Chandradeepta@users.noreply.github.com>

* 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 <89371824+kibo-chandradeeptalaha@users.noreply.github.com>
Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com>
Co-authored-by: kibo-geetanshuchhabra <89399259+kibo-geetanshuchhabra@users.noreply.github.com>
Co-authored-by: kibo-sushant <89385472+kibo-sushant@users.noreply.github.com>
Co-authored-by: SushantJadhav <Sushant.Jadhav@kibocommerce.com>
Co-authored-by: amolnadagonde <amol.nadagonde@kibocommerce.com>
Co-authored-by: kibo-sushant <sushant.jadhav@blueconchtech.com>
Co-authored-by: kibo-amolnadagonde <75060520+kibo-amolnadagonde@users.noreply.github.com>
  • Loading branch information
9 people committed Dec 15, 2021
1 parent 53fd28d commit c449557
Show file tree
Hide file tree
Showing 108 changed files with 23,610 additions and 3 deletions.
7 changes: 7 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,10 @@ NEXT_PUBLIC_VENDURE_LOCAL_URL=
ORDERCLOUD_CLIENT_ID=
ORDERCLOUD_CLIENT_SECRET=
STRIPE_SECRET=

KIBO_API_URL=
KIBO_CLIENT_ID=
KIBO_SHARED_SECRET=
KIBO_CART_COOKIE=
KIBO_CUSTOMER_COOKIE=
KIBO_API_HOST=
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,4 @@ After Email confirmation, Checkout should be manually enabled through BigCommerc
<br>
<br>
BigCommerce team has been notified and they plan to add more details about this subject.
</details>
</details>
3 changes: 2 additions & 1 deletion framework/commerce/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ const PROVIDERS = [
'swell',
'vendure',
'ordercloud',
'spree',
'kibocommerce',
'spree'
]

function getProviderName() {
Expand Down
7 changes: 7 additions & 0 deletions framework/kibocommerce/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
COMMERCE_PROVIDER=kibocommerce
KIBO_API_URL=
KIBO_CART_COOKIE=
KIBO_CUSTOMER_COOKIE=
KIBO_CLIENT_ID=
KIBO_SHARED_SECRET=
KIBO_AUTH_URL=
37 changes: 37 additions & 0 deletions framework/kibocommerce/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Kibo Commerce Provider

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):

```bash
cp framework/kibocommerce/.env.template .env.local
```

Then, set the environment variables in `.env.local` to match the ones from your store.

```
COMMERCE_PROVIDER='kibocommerce'
KIBO_API_URL= 'https://t1234-s1234.sandbox.mozu.com/graphql'
KIBO_CART_COOKIE='kibo_cart'
KIBO_CUSTOMER_COOKIE='kibo_customer'
KIBO_CLIENT_ID='KIBO.APP.1.0.0.Release'
KIBO_SHARED_SECRET='12345secret'
KIBO_AUTH_URL='https://home.mozu.com'
```

- `KIBO_API_URL` - link to your Kibo Commerce GraphQL API instance.
- `KIBO_CART_COOKIE` - configurable cookie name for cart.
- `KIBO_CUSTOMER_COOKIE` - configurable cookie name for shopper identifier/authentication cookie
- `KIBO_CLIENT_ID` - Unique Application (Client) ID of your Application
- `KIBO_SHARED_SECRET` - Secret API key used to authenticate application/client id.


Your Kibo Client ID and Shared Secret can be found from your [Kibo eCommerce Dev Center](https://mozu.com/login)

Visit [Kibo documentation](https://apidocs.kibong-perf.com/?spec=graphql#auth) for more details on API authentication

Based on the config, this integration will handle Authenticating your application against the Kibo API using the Kibo Client ID and Kibo Shared Secret.
## Contribute

Our commitment to Open Source can be found [here](https://vercel.com/oss).

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).
102 changes: 102 additions & 0 deletions framework/kibocommerce/api/endpoints/cart/add-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Product } from './../../../schema.d'
import { normalizeCart } from '../../../lib/normalize'
import type { CartEndpoint } from '.'
import addToCurrentCartMutation from '../../../api/mutations/addToCart-mutation'

import { getProductQuery } from '../../../api/queries/get-product-query'
import { getCartQuery } from '../../../api/queries/get-cart-query'
import CookieHandler from '../../../api/utils/cookie-handler'

const buildAddToCartVariables = ({
productId,
variantId,
quantity = 1,
productResponse,
}: {
productId: string
variantId: string
quantity: number
productResponse: any
}) => {
const { product } = productResponse.data

const selectedOptions = product.variations?.find(
(v: any) => v.productCode === variantId
).options

let options: any[] = []
selectedOptions?.forEach((each: any) => {
product?.options
.filter((option: any) => {
return option.attributeFQN == each.attributeFQN
})
.forEach((po: any) => {
options.push({
attributeFQN: po.attributeFQN,
name: po.attributeDetail.name,
value: po.values?.find((v: any) => v.value == each.value).value,
})
})
})

return {
productToAdd: {
product: {
productCode: productId,
variationProductCode: variantId ? variantId : null,
options,
},
quantity,
fulfillmentMethod: 'Ship',
},
}
}

const addItem: CartEndpoint['handlers']['addItem'] = async ({
req,
res,
body: { cartId, item },
config,
}) => {
if (!item) {
return res.status(400).json({
data: null,
errors: [{ message: 'Missing item' }],
})
}
if (!item.quantity) item.quantity = 1

const productResponse = await config.fetch(getProductQuery, {
variables: { productCode: item?.productId },
})

const cookieHandler = new CookieHandler(config, req, res)
let accessToken = null

if (!cookieHandler.getAccessToken()) {
let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken()
accessToken = anonymousShopperTokenResponse.accessToken;
} else {
accessToken = cookieHandler.getAccessToken()
}

const addToCartResponse = await config.fetch(
addToCurrentCartMutation,
{
variables: buildAddToCartVariables({ ...item, productResponse }),
},
{ headers: { 'x-vol-user-claims': accessToken } }
)
let currentCart = null
if (addToCartResponse.data.addItemToCurrentCart) {
let result = await config.fetch(
getCartQuery,
{},
{ headers: { 'x-vol-user-claims': accessToken } }
)
currentCart = result?.data?.currentCart
}
res.status(200).json({ data: normalizeCart(currentCart) })
}

export default addItem
41 changes: 41 additions & 0 deletions framework/kibocommerce/api/endpoints/cart/get-cart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import CookieHandler from '../../../api/utils/cookie-handler'
import { normalizeCart } from '../../../lib/normalize'
import { Cart } from '../../../schema'
import type { CartEndpoint } from '.'
import { getCartQuery } from '../../queries/get-cart-query'

const getCart: CartEndpoint['handlers']['getCart'] = async ({
req,
res,
body: { cartId },
config,
}) => {
let currentCart: Cart = {}
try {
const cookieHandler = new CookieHandler(config, req, res)
let accessToken = null

if (!cookieHandler.getAccessToken()) {
let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken()
const response = anonymousShopperTokenResponse.response
accessToken = anonymousShopperTokenResponse.accessToken
cookieHandler.setAnonymousShopperCookie(response)
} else {
accessToken = cookieHandler.getAccessToken()
}

let result = await config.fetch(
getCartQuery,
{},
{ headers: { 'x-vol-user-claims': accessToken } }
)
currentCart = result?.data?.currentCart
} catch (error) {
throw error
}
res.status(200).json({
data: currentCart ? normalizeCart(currentCart) : null,
})
}

export default getCart
25 changes: 25 additions & 0 deletions framework/kibocommerce/api/endpoints/cart/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { GetAPISchema, createEndpoint } from '@commerce/api'
import cartEndpoint from '@commerce/api/endpoints/cart'
import type { KiboCommerceAPI } from '../..'
import getCart from './get-cart';
import addItem from './add-item';
import updateItem from './update-item'
import removeItem from './remove-item'

export type CartAPI = GetAPISchema<KiboCommerceAPI, any>

export type CartEndpoint = CartAPI['endpoint']

export const handlers: CartEndpoint['handlers'] = {
getCart,
addItem,
updateItem,
removeItem,
}

const cartApi = createEndpoint<CartAPI>({
handler: cartEndpoint,
handlers,
})

export default cartApi
45 changes: 45 additions & 0 deletions framework/kibocommerce/api/endpoints/cart/remove-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { normalizeCart } from '../../../lib/normalize'
import type { CartEndpoint } from '.'
import removeItemFromCartMutation from '../../../api/mutations/removeItemFromCart-mutation'
import { getCartQuery } from '../../../api/queries/get-cart-query'

const removeItem: CartEndpoint['handlers']['removeItem'] = async ({
req,
res,
body: { cartId, itemId },
config,
}) => {
if (!itemId) {
return res.status(400).json({
data: null,
errors: [{ message: 'Invalid request' }],
})
}
const encodedToken = req.cookies[config.customerCookie]
const token = encodedToken
? Buffer.from(encodedToken, 'base64').toString('ascii')
: null

const accessToken = token ? JSON.parse(token).accessToken : null

const removeItemResponse = await config.fetch(
removeItemFromCartMutation,
{
variables: { id: itemId },
},
{ headers: { 'x-vol-user-claims': accessToken } }
)

let currentCart = null
if (removeItemResponse.data.deleteCurrentCartItem) {
let result = await config.fetch(
getCartQuery,
{},
{ headers: { 'x-vol-user-claims': accessToken } }
)
currentCart = result?.data?.currentCart
}
res.status(200).json({ data: normalizeCart(currentCart) })
}

export default removeItem
45 changes: 45 additions & 0 deletions framework/kibocommerce/api/endpoints/cart/update-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { normalizeCart } from '../../../lib/normalize'
import type { CartEndpoint } from '.'
import { getCartQuery } from '../../../api/queries/get-cart-query'
import updateCartItemQuantityMutation from '../../../api/mutations/updateCartItemQuantity-mutation'

const updateItem: CartEndpoint['handlers']['updateItem'] = async ({
req,
res,
body: { cartId, itemId, item },
config,
}) => {
if (!itemId || !item) {
return res.status(400).json({
data: null,
errors: [{ message: 'Invalid request' }],
})
}
const encodedToken = req.cookies[config.customerCookie]
const token = encodedToken
? Buffer.from(encodedToken, 'base64').toString('ascii')
: null

const accessToken = token ? JSON.parse(token).accessToken : null

const updateItemResponse = await config.fetch(
updateCartItemQuantityMutation,
{
variables: { itemId: itemId, quantity: item.quantity },
},
{ headers: { 'x-vol-user-claims': accessToken } }
)

let currentCart = null
if (updateItemResponse.data) {
let result = await config.fetch(
getCartQuery,
{},
{ headers: { 'x-vol-user-claims': accessToken } }
)
currentCart = result?.data?.currentCart
}
res.status(200).json({ data: normalizeCart(currentCart) })
}

export default updateItem
17 changes: 17 additions & 0 deletions framework/kibocommerce/api/endpoints/catalog/products/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { GetAPISchema, createEndpoint } from '@commerce/api'
import productsEndpoint from '@commerce/api/endpoints/catalog/products'
import type { KiboCommerceAPI } from '../../..'
import getProducts from '../products/products'

export type ProductsAPI = GetAPISchema<KiboCommerceAPI, any>

export type ProductsEndpoint = ProductsAPI['endpoint']

export const handlers: ProductsEndpoint['handlers'] = { getProducts }

const productsApi = createEndpoint<ProductsAPI>({
handler: productsEndpoint,
handlers,
})

export default productsApi
31 changes: 31 additions & 0 deletions framework/kibocommerce/api/endpoints/catalog/products/products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Product } from '@commerce/types/product'
import { ProductsEndpoint } from '.'
import productSearchQuery from '../../../queries/product-search-query'
import { buildProductSearchVars } from '../../../../lib/product-search-vars'
import {normalizeProduct} from '../../../../lib/normalize'

const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
res,
body: { search, categoryId, brandId, sort },
config,
}) => {
const pageSize = 100;
const filters = {};
const startIndex = 0;
const variables = buildProductSearchVars({
categoryCode: categoryId,
pageSize,
search,
sort,
filters,
startIndex,
})
const {data} = await config.fetch(productSearchQuery, { variables });
const found = data?.products?.items?.length > 0 ? true : false;
let productsResponse= data?.products?.items.map((item: any) =>normalizeProduct(item,config));
const products: Product[] = found ? productsResponse : [];

res.status(200).json({ data: { products, found } });
}

export default getProducts
1 change: 1 addition & 0 deletions framework/kibocommerce/api/endpoints/checkout/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}
1 change: 1 addition & 0 deletions framework/kibocommerce/api/endpoints/customer/address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}
1 change: 1 addition & 0 deletions framework/kibocommerce/api/endpoints/customer/card.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}

0 comments on commit c449557

Please sign in to comment.