Skip to content

Commit

Permalink
updates to configuration, update sanity hook to use a diff match patt…
Browse files Browse the repository at this point in the history
…ern against shopify metafields
  • Loading branch information
iamkevingreen committed Nov 6, 2020
1 parent e8f5364 commit 44e96e4
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 67 deletions.
3 changes: 3 additions & 0 deletions web/env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ SENTRY_AUTH_TOKEN=
GATSBY_SENTRY_DSN=
# SHOPIFY_SECRET is the token that appears after you create a webhook in Shopify
SHOPIFY_SECRET=
SHOPIFY_URL=site.myshopify.com
SHOPIFY_API_KEY=
SHOPIFY_API_PASSWORD=
4 changes: 3 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"highlight.js": "^9.18.1",
"http-proxy-middleware": "^0.21.0",
"js-cookie": "^2.2.1",
"jsondiffpatch": "^0.4.1",
"klaviyo-subscribe": "^1.0.0",
"markdown-it": "^10.0.0",
"netlify-lambda": "^1.6.3",
Expand Down Expand Up @@ -117,7 +118,8 @@
"sentry:release": "sentry-cli releases --org ctrl-alt-del-world new -p midway $COMMIT_REF",
"sentry:commits": "sentry-cli releases --org ctrl-alt-del-world set-commits $COMMIT_REF --commit \"ctrl-alt-del-world/midway@$COMMIT_REF\"",
"start": "npm run develop",
"ngrok": "ngrok http 8000 --subdomain midway-shop",
"ngrok": "ngrok http 8000",
"ngrok:reserved": "ngrok http 8000 --subdomain midway-shop",
"serve": "gatsby serve",
"clean": "gatsby clean",
"test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1",
Expand Down
65 changes: 64 additions & 1 deletion web/src/lambda/requestConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,67 @@ const CUSTOMER_ACTIVATE_QUERY = `mutation customerActivate($id: ID!, $input: Cus
}
}`


//
// == PRODUCTS ===
//

const PRODUCT_QUERY = `query getProduct($id: ID!) {
node(id: $id) {
... on Product {
id
handle
title
totalVariants
images(first: 100) {
edges {
node {
id
originalSrc
}
}
}
variants(first: 100) {
edges {
node {
id
title
price
displayName
}
}
}
metafield(namespace: "sync", key: "productData") {
value
id
}
}
}
}
`

const PRODUCT_UPDATE = `mutation productMetaUpdate($input: ProductInput!) {
productUpdate(input: $input) {
product {
metafields(first: 100) {
edges {
node {
id
namespace
key
value
}
}
}
}
userErrors {
field
message
}
}
}`


const statusReturn = (code: number, body: {}) => {
return {
statusCode: code,
Expand Down Expand Up @@ -171,5 +232,7 @@ export {
CUSTOMER_LOGOUT_QUERY,
CUSTOMER_CREATE_QUERY,
CUSTOMER_RESET_QUERY,
CUSTOMER_ACTIVATE_QUERY
CUSTOMER_ACTIVATE_QUERY,
PRODUCT_QUERY,
PRODUCT_UPDATE
}
153 changes: 88 additions & 65 deletions web/src/lambda/shopify-sync.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
require("dotenv").config();
import { APIGatewayEvent } from 'aws-lambda'
import axios from 'axios'
import sanityClient from '@sanity/client'
import { statusReturn } from "./requestConfig";
import crypto from 'crypto'
import _ from 'lodash'

const jsondiffpatch = require('jsondiffpatch')

import fetch from 'node-fetch'

const {
SANITY_API_TOKEN,
SANITY_PROJECT_ID,
SANITY_DATASET,
SHOPIFY_API_PASSWORD,
SHOPIFY_API_KEY,
SHOPIFY_URL,
SHOPIFY_SECRET
} = process.env;

Expand All @@ -21,12 +26,20 @@ const client = sanityClient({
useCdn: false
});

import {
statusReturn,
preparePayload,
shopifyConfig,
PRODUCT_QUERY,
PRODUCT_UPDATE
} from './requestConfig'

const updateEverything = async (data: {
id: number
title: string
variants: any[]
handle: string
}) => {
}, inputObject) => {
const product = {
_type: 'product',
_id: data.id.toString()
Expand All @@ -53,6 +66,15 @@ const updateEverything = async (data: {
"content.main.slug.current": data.handle
}

const metaPayLoad = preparePayload(PRODUCT_UPDATE, inputObject)

const updateMetaField = await axios({
url: `https://${SHOPIFY_API_KEY}:${SHOPIFY_API_PASSWORD}@${SHOPIFY_URL}/admin/api/2020-10/graphql.json`,
method: 'POST',
headers: shopifyConfig,
data: JSON.stringify(metaPayLoad)
})

try {

let tx = client.transaction()
Expand Down Expand Up @@ -165,9 +187,9 @@ export const handler = async (event: APIGatewayEvent): Promise<any> => {
try {
data = JSON.parse(event.body);
const generatedHash = crypto
.createHmac('sha256', SHOPIFY_SECRET)
.update(event.body)
.digest('base64')
.createHmac('sha256', SHOPIFY_SECRET)
.update(event.body)
.digest('base64')
if (generatedHash !== hmac) {
return statusReturn(400, { error: 'Invalid Webhook' })
}
Expand All @@ -182,71 +204,72 @@ export const handler = async (event: APIGatewayEvent): Promise<any> => {
if (data.hasOwnProperty('title') && data.hasOwnProperty('handle')) {
// Build our initial product


const payload = preparePayload(PRODUCT_QUERY, {
id: data.admin_graphql_api_id
})

try {
return client.fetch(`*[_type == "product" && _id == "${data.id.toString()}"][0] {
...,
content {
...,
shopify {
...,
variants[]->
}
const shopifyProduct = await axios({
url: `https://${SHOPIFY_API_KEY}:${SHOPIFY_API_PASSWORD}@${SHOPIFY_URL}/admin/api/2020-10/graphql.json`,
method: 'POST',
headers: shopifyConfig,
data: JSON.stringify(payload)
})

const {
metafield,
title,
id,
handle,
totalVariants,
images,
variants
} = shopifyProduct.data.data.node

const metaCompare = {
id,
title,
handle,
totalVariants,
images,
variants
}
const inputObject = {
input: {
"id": data.admin_graphql_api_id,
"metafields": [
{
id: metafield ? metafield.id : null,
"namespace": "sync",
"key": "productData",
"value": JSON.stringify(metaCompare),
"valueType": "STRING"
}
]
}
}`)
.then(res => {


if (!res.content) {
return updateEverything(data)
}

// Check if product updates have happened that matter
const {
title,
slug: {
current
},
} = res.content.main
const {
defaultVariant,
variants,
image
} = res.content.shopify

// Check Top Level Changes occured and rebuild
if (!image || title !== data.title || current !== data.handle || defaultVariant.price !== data.variants[0].price) {
return updateEverything(data)
} else {
// Check if more variants then currently stored and rebuild
if (variants.length === data.variants.length) {
// Check if nested variant information has changed
let triggerRebuild = false
variants.forEach((v, i) => {
const { productId, variantTitle, variantId } = v.content.shopify
const { id, title, product_id } = data.variants[i]
if (productId !== product_id || variantId !== id || variantTitle !== title) {
console.log(`variant ${variantId} of ${variantTitle} has changed`)
triggerRebuild = true
}
})
if (triggerRebuild) {
return updateEverything(data)
} else {
return statusReturn(200, { body: 'nothing important changed' })
}
} else {
return updateEverything(data)
}
if (metafield) {
if (jsondiffpatch.diff(JSON.parse(metafield.value), metaCompare)) {
try {
return updateEverything(data, inputObject)
} catch (err) {
return statusReturn(200, { error: 'Problem with mutation' })
}
}
}).catch(error => {
console.error(`Sanity error:`, error)
return statusReturn(500, { error: error[0].message })
})
} else {
return statusReturn(200, { body: 'nothing important changed' })
}
} else {
try {

return updateEverything(data, inputObject)
} catch (err) {
return statusReturn(200, { error: 'Problem with mutation' })
}
}
} catch (err) {
return statusReturn(400, { error: 'Bad request body' })
console.log(err)
return statusReturn(200, { error: 'Problem looking up Product' })
}

// move all of this inside of the fetch request to block builds when not necessary

} else if (data.hasOwnProperty('id') && (!data.hasOwnProperty('title') && !data.hasOwnProperty('handle'))) {
Expand Down

0 comments on commit 44e96e4

Please sign in to comment.