able to pull Products using Shopify GraphQL API
This commit is contained in:
@ -5,6 +5,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"prettier.semi": false
|
"prettier.semi": false,
|
||||||
|
"cloudcode.duetAI.project": "beepmill-code",
|
||||||
|
"cloudcode.project": "beepmill-code"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,10 +1,43 @@
|
|||||||
class Config {
|
export class Config {
|
||||||
productPhotosFolderId: string
|
productPhotosFolderId: string
|
||||||
|
shopifyApiKey: string
|
||||||
|
shopifyApiSecretKey: string
|
||||||
|
shopifyAdminApiAccessToken: string
|
||||||
|
shopifyApiURI: string
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
let ss = SpreadsheetApp.getActive()
|
let ss = SpreadsheetApp.getActive()
|
||||||
let s = ss.getSheetByName("vars")
|
let s = ss.getSheetByName("vars")
|
||||||
|
|
||||||
this.productPhotosFolderId = vlookupByColumns("vars", "key", "productPhotosFolderId", "value")
|
this.productPhotosFolderId = vlookupByColumns(
|
||||||
|
"vars",
|
||||||
|
"key",
|
||||||
|
"productPhotosFolderId",
|
||||||
|
"value"
|
||||||
|
)
|
||||||
|
this.shopifyApiKey = vlookupByColumns(
|
||||||
|
"vars",
|
||||||
|
"key",
|
||||||
|
"shopifyApiKey",
|
||||||
|
"value"
|
||||||
|
)
|
||||||
|
this.shopifyApiSecretKey = vlookupByColumns(
|
||||||
|
"vars",
|
||||||
|
"key",
|
||||||
|
"shopifyApiSecretKey",
|
||||||
|
"value"
|
||||||
|
)
|
||||||
|
this.shopifyAdminApiAccessToken = vlookupByColumns(
|
||||||
|
"vars",
|
||||||
|
"key",
|
||||||
|
"shopifyAdminApiAccessToken",
|
||||||
|
"value"
|
||||||
|
)
|
||||||
|
this.shopifyApiURI = vlookupByColumns(
|
||||||
|
"vars",
|
||||||
|
"key",
|
||||||
|
"shopifyApiURI",
|
||||||
|
"value"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,22 @@
|
|||||||
|
import { Shop } from "./shopifyApi"
|
||||||
|
|
||||||
function initMenu() {
|
function initMenu() {
|
||||||
let ui = SpreadsheetApp.getUi()
|
let ui = SpreadsheetApp.getUi()
|
||||||
ui.createMenu("BLM")
|
ui.createMenu("BLM")
|
||||||
.addItem("Fill out product from template", "fillProductFromTemplate")
|
.addItem("Fill out product from template", "fillProductFromTemplate")
|
||||||
.addItem("Create missing photo folders", "createMissingPhotoFolders")
|
.addItem("Create missing photo folders", "createMissingPhotoFolders")
|
||||||
|
.addSeparator()
|
||||||
|
.addItem("Run Shopify Orders", "runShopifyOrders")
|
||||||
|
.addItem("Get Shopify Products", "getShopifyProducts")
|
||||||
.addToUi()
|
.addToUi()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function runShopifyOrders() {
|
||||||
|
let shop = new Shop()
|
||||||
|
shop.RunOrders()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getShopifyProducts() {
|
||||||
|
let shop = new Shop()
|
||||||
|
shop.GetProducts()
|
||||||
|
}
|
||||||
|
|||||||
732
src/shopifyApi.ts
Normal file
732
src/shopifyApi.ts
Normal file
@ -0,0 +1,732 @@
|
|||||||
|
// EXTREMELY inspired by https://github.com/webdjoe/shopify_apps_script/blob/master/shopify_api.gs
|
||||||
|
|
||||||
|
// Create a private App in Shopify admin and insert app password below
|
||||||
|
// Sheets need to be created before running with columns of desired output set
|
||||||
|
// Use keys found in Shopify Rest API Order Object as row headers
|
||||||
|
// https://shopify.dev/api/admin-rest/2022-04/resources/order#resource-object
|
||||||
|
|
||||||
|
// Supports the following sheets/order objects with default columns -
|
||||||
|
// orders - id, number, order_name, name, current_total_discounts, current_total_price, current_subtotal_price, current_total_tax, financial_status, landing_site, created_at, processed_at, processing_method, subtotal_price, source_url, tags, token, total_discounts, total_line_items_price, total_price, total_tax, updated_at
|
||||||
|
// shipping_lines - order_id, ordered_at, code, price, title, source, discounted_price, carrier_identifier
|
||||||
|
// discount_applications - order_id, created_at, type, title, value, value_type, code, description, target_type, target_selection, allocation_method, ordered_at
|
||||||
|
// refunds = id, order_id, ordered_at, created_at, note, transaction_id, kind, amount
|
||||||
|
// line_items = id, order_id, sku, name, price, title, vendor, quantity, product_id, variant_id, variant_title, total_discount, fulfillment_status, ordered_at
|
||||||
|
// customer - order_id, ordered_at, id, email, phone, last_name, created_at, first_name, admin_graphql_api_id, updated_at, orders_count, last_order_id
|
||||||
|
|
||||||
|
/// <reference types="@types/google-apps-script" />
|
||||||
|
|
||||||
|
import { Config } from "./config"
|
||||||
|
|
||||||
|
const ss = SpreadsheetApp.getActive()
|
||||||
|
|
||||||
|
const today = new Date()
|
||||||
|
const thirty_days = new Date(
|
||||||
|
today.getFullYear(),
|
||||||
|
today.getMonth() - 1,
|
||||||
|
today.getDate()
|
||||||
|
)
|
||||||
|
const start_str = Utilities.formatDate(
|
||||||
|
thirty_days,
|
||||||
|
SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone(),
|
||||||
|
"yyyy-MM-dd"
|
||||||
|
)
|
||||||
|
const end_str = Utilities.formatDate(
|
||||||
|
today,
|
||||||
|
SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone(),
|
||||||
|
"yyyy-MM-dd"
|
||||||
|
)
|
||||||
|
|
||||||
|
const orders = "_orders"
|
||||||
|
const line_items = "_line_items"
|
||||||
|
const customer = "_customer"
|
||||||
|
const refunds = "_refunds"
|
||||||
|
const shipping_lines = "_shipping_lines"
|
||||||
|
const discount_applications = "_discount_applications"
|
||||||
|
|
||||||
|
function makeArray(headers, obj_list) {
|
||||||
|
var rng_arr = []
|
||||||
|
for (let obj of obj_list) {
|
||||||
|
var line_arr = []
|
||||||
|
headers.forEach((item) => {
|
||||||
|
var line_item = item in obj ? obj[item] : ""
|
||||||
|
if (Array.isArray(line_item)) {
|
||||||
|
line_item = line_item.toString()
|
||||||
|
}
|
||||||
|
line_arr.push(line_item)
|
||||||
|
})
|
||||||
|
rng_arr.push(line_arr)
|
||||||
|
}
|
||||||
|
return rng_arr
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeDiscountsArray(orders_arr) {
|
||||||
|
var disc_arr = []
|
||||||
|
var headers = getDiscountHeaders()
|
||||||
|
for (let ord of orders_arr) {
|
||||||
|
var disc_list = ord.discount_applications
|
||||||
|
if (disc_list.length < 1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
disc_list.forEach((disc) => {
|
||||||
|
var disc_line = []
|
||||||
|
disc["order_id"] = ord["id"]
|
||||||
|
disc["ordered_at"] = ord["created_at"]
|
||||||
|
headers.forEach((head) => {
|
||||||
|
var itm = head in disc ? disc[head] : ""
|
||||||
|
disc_line.push(itm)
|
||||||
|
})
|
||||||
|
disc_arr.push(disc_line)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return disc_arr
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeCustomerArray(orders_arr) {
|
||||||
|
var cust_arr = []
|
||||||
|
var headers = getCustomerHeaders()
|
||||||
|
for (let ord of orders_arr) {
|
||||||
|
var cust_obj = ord.customer
|
||||||
|
if (typeof cust_obj == "undefined") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var customer_line = []
|
||||||
|
cust_obj["order_id"] = ord["id"]
|
||||||
|
cust_obj["ordered_at"] = ord["created_at"]
|
||||||
|
headers.forEach((head) => {
|
||||||
|
var itm = head in cust_obj ? cust_obj[head] : ""
|
||||||
|
customer_line.push(itm)
|
||||||
|
})
|
||||||
|
cust_arr.push(customer_line)
|
||||||
|
}
|
||||||
|
return cust_arr
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeLIArray(orders_arr) {
|
||||||
|
var line_arr = []
|
||||||
|
var headers = getLineItemHeaders()
|
||||||
|
for (let ord of orders_arr) {
|
||||||
|
var line_items_list = ord.line_items
|
||||||
|
if (line_items_list.length < 1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line_items_list.forEach((li_obj) => {
|
||||||
|
var li_line = []
|
||||||
|
li_obj["ordered_at"] = ord["created_at"]
|
||||||
|
li_obj["order_id"] = ord["id"]
|
||||||
|
headers.forEach((head) => {
|
||||||
|
var itm = head in li_obj ? li_obj[head] : ""
|
||||||
|
li_line.push(itm)
|
||||||
|
})
|
||||||
|
line_arr.push(li_line)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return line_arr
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeShippingArray(orders_arr) {
|
||||||
|
var ship_arr = []
|
||||||
|
var header = getShippingHeaders()
|
||||||
|
for (let ord of orders_arr) {
|
||||||
|
var ship_list = ord.shipping_lines
|
||||||
|
if (ship_list.length < 1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ship_list.forEach((ship_obj) => {
|
||||||
|
var line = []
|
||||||
|
|
||||||
|
ship_obj["ordered_at"] = ord["created_at"]
|
||||||
|
ship_obj["order_id"] = ord["id"]
|
||||||
|
|
||||||
|
header.forEach((a) => {
|
||||||
|
var line_itm = a in ship_obj ? ship_obj[a] : ""
|
||||||
|
line.push(line_itm)
|
||||||
|
})
|
||||||
|
ship_arr.push(line)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ship_arr
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeRefundsArray(orders_arr) {
|
||||||
|
var refunds_arr = []
|
||||||
|
var header = getRefundHeaders()
|
||||||
|
for (let ord of orders_arr) {
|
||||||
|
var ref_list = ord.refunds
|
||||||
|
if (ref_list.length < 1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ref_list.forEach((ref_obj) => {
|
||||||
|
var line = []
|
||||||
|
|
||||||
|
ref_obj["ordered_at"] = ord["created_at"]
|
||||||
|
if (ref_obj["transactions"].length == 1) {
|
||||||
|
var trans = ref_obj["transactions"][0]
|
||||||
|
ref_obj["transaction_id"] = trans["id"]
|
||||||
|
ref_obj["amount"] = trans["amount"]
|
||||||
|
ref_obj["kind"] = trans["kind"]
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header.forEach((a) => {
|
||||||
|
var line_itm = a in ref_obj ? ref_obj[a] : ""
|
||||||
|
line.push(line_itm)
|
||||||
|
})
|
||||||
|
refunds_arr.push(line)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return refunds_arr
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendRows(sht_name, arr) {
|
||||||
|
var sht = ss.getSheetByName(sht_name)
|
||||||
|
let last_row = sht.getLastRow()
|
||||||
|
sht.getRange(last_row + 1, 1, arr.length, arr[0].length).setValues(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
function processDataRange(sht_name) {
|
||||||
|
var sh = ss.getSheetByName(sht_name)
|
||||||
|
var data_rng = sh.getDataRange().offset(1, 0, sh.getLastRow() - 1)
|
||||||
|
var data = data_rng.getValues()
|
||||||
|
|
||||||
|
var targetData = new Array()
|
||||||
|
for (let n = 0; n < data.length; ++n) {
|
||||||
|
if (data[n].join().replace(/,/g, "") != "") {
|
||||||
|
targetData.push(data[n])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data_rng.clear()
|
||||||
|
targetData.sort((a, b) => a[1] - b[1])
|
||||||
|
sh.getRange(2, 1, targetData.length, targetData[0].length).setValues(
|
||||||
|
targetData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeDuplicates(obj_list, sht_name, id = "id") {
|
||||||
|
var sht = ss.getSheetByName(sht_name)
|
||||||
|
var sht_arr = sht.getDataRange().getValues()
|
||||||
|
for (let a = 0; a < sht_arr[0].length; a++) {
|
||||||
|
if (sht_arr[0][a] == id) {
|
||||||
|
var id_col = a
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let x = 0; x < sht_arr.length; x++) {
|
||||||
|
var row_id = sht_arr[x][id_col]
|
||||||
|
for (let y = 0; y < obj_list.length; y++) {
|
||||||
|
if (obj_list[y][id_col] == row_id) {
|
||||||
|
sht.getRange(x + 1, 1, 1, sht_arr[0].length).clearContent()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAllDuplicates(obj_list, sht_name, id = "id") {
|
||||||
|
var sht = ss.getSheetByName(sht_name)
|
||||||
|
var sht_arr = sht.getDataRange().getValues()
|
||||||
|
for (let a = 0; a < sht_arr[0].length; a++) {
|
||||||
|
if (sht_arr[0][a] == id) {
|
||||||
|
var id_col = a
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let x = 0; x < sht_arr.length; x++) {
|
||||||
|
var row_id = sht_arr[x][id_col]
|
||||||
|
for (let y = 0; y < obj_list.length; y++) {
|
||||||
|
if (obj_list[y][id_col] == row_id) {
|
||||||
|
sht.getRange(x + 1, 1, 1, sht_arr[0].length).clearContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColumnList() {
|
||||||
|
var order_list = getOrderHeaders()
|
||||||
|
order_list.push(
|
||||||
|
line_items,
|
||||||
|
customer,
|
||||||
|
refunds,
|
||||||
|
discount_applications,
|
||||||
|
shipping_lines
|
||||||
|
)
|
||||||
|
return order_list
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColumns(data: string) {
|
||||||
|
var sht = ss.getSheetByName(data)
|
||||||
|
if (sht == null) {
|
||||||
|
ss.insertSheet(data)
|
||||||
|
sht = ss.getSheetByName(data)
|
||||||
|
}
|
||||||
|
if (sht == null) {
|
||||||
|
throw new Error("unable to get/create sheet " + data)
|
||||||
|
}
|
||||||
|
var last_col = sht.getLastColumn()
|
||||||
|
var header_list = sht.getRange(1, 1, 1, last_col).getValues()
|
||||||
|
return header_list[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getShippingHeaders() {
|
||||||
|
return getColumns(shipping_lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCustomerHeaders() {
|
||||||
|
return getColumns(customer)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLineItemHeaders() {
|
||||||
|
return getColumns(line_items)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOrderHeaders() {
|
||||||
|
return Order.columns
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDiscountHeaders() {
|
||||||
|
return getColumns(discount_applications)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRefundHeaders() {
|
||||||
|
return getColumns(refunds)
|
||||||
|
}
|
||||||
|
function customerTable(orders_arr) {
|
||||||
|
var rng_arr = makeCustomerArray(orders_arr)
|
||||||
|
removeDuplicates(rng_arr, customer, "order_id")
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
appendRows(customer, rng_arr)
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
processDataRange(customer)
|
||||||
|
}
|
||||||
|
|
||||||
|
function discountsTable(orders_arr) {
|
||||||
|
var rng_arr = makeDiscountsArray(orders_arr)
|
||||||
|
if (rng_arr.length > 0) {
|
||||||
|
removeAllDuplicates(rng_arr, discount_applications, "order_id")
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
appendRows(discount_applications, rng_arr)
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
processDataRange(discount_applications)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ordersTable(orders_arr) {
|
||||||
|
var rng_arr = makeArray(getOrderHeaders(), orders_arr)
|
||||||
|
removeDuplicates(rng_arr, orders, "id")
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
appendRows(orders, rng_arr)
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
processDataRange(orders)
|
||||||
|
}
|
||||||
|
|
||||||
|
function refundsTable(orders_arr) {
|
||||||
|
var rng_arr = makeRefundsArray(orders_arr)
|
||||||
|
if (rng_arr.length > 0) {
|
||||||
|
removeDuplicates(rng_arr, refunds, "id")
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
appendRows(refunds, rng_arr)
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
processDataRange(refunds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function lineItemsTable(orders_arr) {
|
||||||
|
var rng_arr = makeLIArray(orders_arr)
|
||||||
|
removeDuplicates(rng_arr, line_items, "id")
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
appendRows(line_items, rng_arr)
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
processDataRange(line_items)
|
||||||
|
}
|
||||||
|
|
||||||
|
function shippingTable(orders_arr) {
|
||||||
|
var rng_arr = makeShippingArray(orders_arr)
|
||||||
|
if (rng_arr.length > 0) {
|
||||||
|
removeDuplicates(rng_arr, shipping_lines, "order_id")
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
appendRows(shipping_lines, rng_arr)
|
||||||
|
SpreadsheetApp.flush()
|
||||||
|
processDataRange(shipping_lines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unquote(value) {
|
||||||
|
if (value.charAt(0) == '"' && value.charAt(value.length - 1) == '"')
|
||||||
|
return value.substring(1, value.length - 1)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLinkHeader(header) {
|
||||||
|
var linkexp =
|
||||||
|
/<[^>]*>\s*(\s*;\s*[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*")))*(,|$)/g
|
||||||
|
var paramexp =
|
||||||
|
/[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*"))/g
|
||||||
|
|
||||||
|
var matches = header.match(linkexp)
|
||||||
|
var rels = {}
|
||||||
|
for (let i = 0; i < matches.length; i++) {
|
||||||
|
var split = matches[i].split(">")
|
||||||
|
var href = split[0].substring(1)
|
||||||
|
var ps = split[1]
|
||||||
|
var link = {
|
||||||
|
href: "",
|
||||||
|
rel: "",
|
||||||
|
}
|
||||||
|
link.href = href
|
||||||
|
var s = ps.match(paramexp)
|
||||||
|
for (let j = 0; j < s.length; j++) {
|
||||||
|
var p = s[j]
|
||||||
|
var paramsplit = p.split("=")
|
||||||
|
var name = paramsplit[0]
|
||||||
|
link[name] = unquote(paramsplit[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link.rel !== undefined) {
|
||||||
|
rels[link.rel] = link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rels
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Shop {
|
||||||
|
private shopifyApiKey: string
|
||||||
|
private shopifyApiSecretKey: string
|
||||||
|
private shopifyAdminApiAccessToken: string
|
||||||
|
private shopifyApiURI: string
|
||||||
|
static endpoints = {
|
||||||
|
orders: "/admin/api/2022-04/orders.json",
|
||||||
|
graphql: "/admin/api/2024-10/graphql.json",
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
let config = new Config()
|
||||||
|
this.shopifyApiKey = config.shopifyApiKey
|
||||||
|
this.shopifyApiSecretKey = config.shopifyApiSecretKey
|
||||||
|
this.shopifyAdminApiAccessToken = config.shopifyAdminApiAccessToken
|
||||||
|
this.shopifyApiURI = config.shopifyApiURI
|
||||||
|
}
|
||||||
|
|
||||||
|
RunOrders(start = "", end = "") {
|
||||||
|
if (start === "" || end === "") {
|
||||||
|
start = start_str
|
||||||
|
end = end_str
|
||||||
|
}
|
||||||
|
const date_exp = /^\d{4}[\-](0?[1-9]|1[012])[\-](0?[1-9]|[12][0-9]|3[01])$/
|
||||||
|
if (!date_exp.test(start) || !date_exp.test(end)) {
|
||||||
|
Logger.log("Dates must be in yyyy-MM-dd format")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.GetOrders(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
GetOrders(start: string, end: string) {
|
||||||
|
let endpoint = Shop.endpoints.orders
|
||||||
|
var params = {
|
||||||
|
created_at_min: start,
|
||||||
|
created_at_max: end,
|
||||||
|
fields: getColumnList(),
|
||||||
|
}
|
||||||
|
|
||||||
|
var done = false
|
||||||
|
var next_link = ""
|
||||||
|
do {
|
||||||
|
if (next_link === "") {
|
||||||
|
var response = this.shopifyAPI(endpoint, params)
|
||||||
|
console.log(response)
|
||||||
|
} else {
|
||||||
|
var response = this.shopifyAPI(endpoint, params, next_link)
|
||||||
|
console.log(response)
|
||||||
|
}
|
||||||
|
let resp = response.content
|
||||||
|
let headers = response.headers
|
||||||
|
var orders_arr: Order[] = resp["orders"]
|
||||||
|
if (orders_arr.length < 1) {
|
||||||
|
console.log("No orders found")
|
||||||
|
done = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var cols = getColumnList()
|
||||||
|
var numColumns = cols.length
|
||||||
|
var respKeys = Object.keys(orders_arr[0])
|
||||||
|
var respCols = respKeys.length
|
||||||
|
if (cols.filter((x) => respKeys.indexOf(x) === -1).length !== 0) {
|
||||||
|
Logger.log(
|
||||||
|
"Keys missing from return - " +
|
||||||
|
cols.filter((x) => respKeys.indexOf(x) === -1)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ordersTable(orders_arr)
|
||||||
|
refundsTable(orders_arr)
|
||||||
|
lineItemsTable(orders_arr)
|
||||||
|
customerTable(orders_arr)
|
||||||
|
discountsTable(orders_arr)
|
||||||
|
shippingTable(orders_arr)
|
||||||
|
|
||||||
|
if (headers["Link"] !== undefined) {
|
||||||
|
var links = parseLinkHeader(headers["Link"])
|
||||||
|
Logger.log(headers["Link"])
|
||||||
|
if (links["next"] === undefined) {
|
||||||
|
done = true
|
||||||
|
} else {
|
||||||
|
next_link = links["next"]["href"]
|
||||||
|
Logger.log(next_link)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
} while (!done)
|
||||||
|
}
|
||||||
|
|
||||||
|
GetProducts() {
|
||||||
|
let done = false
|
||||||
|
let cursor = ""
|
||||||
|
let fields = ["id", "title"]
|
||||||
|
var response = {
|
||||||
|
content: {},
|
||||||
|
headers: {},
|
||||||
|
}
|
||||||
|
let products: Product[] = []
|
||||||
|
do {
|
||||||
|
let query = new ProductsQuery(fields, cursor)
|
||||||
|
response = this.shopifyGraphQLAPI(query.JSON)
|
||||||
|
console.log(response)
|
||||||
|
let productsResponse = new ProductsResponse(response.content)
|
||||||
|
if (productsResponse.products.edges.length <= 0) {
|
||||||
|
console.log("no products returned")
|
||||||
|
done = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (let i = 0; i < productsResponse.products.edges.length; i++) {
|
||||||
|
let edge = productsResponse.products.edges[i]
|
||||||
|
console.log(JSON.stringify(edge))
|
||||||
|
let p = new Product()
|
||||||
|
Object.assign(edge.node, p)
|
||||||
|
products.push(p)
|
||||||
|
}
|
||||||
|
if (productsResponse.products.pageInfo.hasNextPage) {
|
||||||
|
cursor = productsResponse.products.pageInfo.endCursor
|
||||||
|
} else {
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
} while (!done)
|
||||||
|
}
|
||||||
|
|
||||||
|
shopifyAPI(endpoint: string, query: {}, next = "") {
|
||||||
|
var options: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions = {
|
||||||
|
method: "get",
|
||||||
|
headers: {
|
||||||
|
"X-Shopify-Access-Token": this.shopifyAdminApiAccessToken,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if (next == "") {
|
||||||
|
var url = this.buildURL(endpoint, query)
|
||||||
|
var resp = UrlFetchApp.fetch(url, options)
|
||||||
|
console.log(resp.getContentText())
|
||||||
|
} else {
|
||||||
|
var url = next
|
||||||
|
var resp = UrlFetchApp.fetch(url, options)
|
||||||
|
console.log(resp.getContentText())
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: JSON.parse(resp.getContentText()),
|
||||||
|
headers: resp.getHeaders(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shopifyGraphQLAPI(query: {}, next = "") {
|
||||||
|
let endpoint = Shop.endpoints.graphql
|
||||||
|
let options: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions = {
|
||||||
|
method: "post",
|
||||||
|
payload: JSON.stringify(query),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Shopify-Access-Token": this.shopifyAdminApiAccessToken,
|
||||||
|
},
|
||||||
|
muteHttpExceptions: true,
|
||||||
|
}
|
||||||
|
var url = this.buildURL(endpoint)
|
||||||
|
console.log(UrlFetchApp.getRequest(url, options))
|
||||||
|
var resp = UrlFetchApp.fetch(url, options)
|
||||||
|
console.log(resp.getContentText())
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: JSON.parse(resp.getContentText()),
|
||||||
|
headers: resp.getHeaders(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildURL(endpoint: string, params = {}) {
|
||||||
|
var url = this.shopifyApiURI + endpoint
|
||||||
|
if (params !== undefined && Object.keys(params).length > 0) {
|
||||||
|
let i = 0
|
||||||
|
for (let p in params) {
|
||||||
|
if (i === 0) {
|
||||||
|
url += "?" + p + "=" + encodeURIComponent(params[p])
|
||||||
|
} else {
|
||||||
|
url += "&" + p + "=" + encodeURIComponent(params[p])
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Order {
|
||||||
|
static columns = [
|
||||||
|
"id",
|
||||||
|
"number",
|
||||||
|
"order_name",
|
||||||
|
"name",
|
||||||
|
"current_total_discounts",
|
||||||
|
"current_total_price",
|
||||||
|
"current_subtotal_price",
|
||||||
|
"current_total_tax",
|
||||||
|
"financial_status",
|
||||||
|
"landing_site",
|
||||||
|
"created_at",
|
||||||
|
"processed_at",
|
||||||
|
"processing_method",
|
||||||
|
"subtotal_price",
|
||||||
|
"source_url",
|
||||||
|
"tags",
|
||||||
|
"token",
|
||||||
|
"total_discounts",
|
||||||
|
"total_line_items_price",
|
||||||
|
"total_price",
|
||||||
|
"total_tax",
|
||||||
|
"updated_at",
|
||||||
|
]
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
for (let i = 0; i < Order.columns.length; i++) {
|
||||||
|
this[Order.columns[i]] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Product {
|
||||||
|
body_html: string
|
||||||
|
created_at: Date
|
||||||
|
handle: string
|
||||||
|
id: number
|
||||||
|
images: ProductImage[]
|
||||||
|
options: ProductOption[]
|
||||||
|
product_type: string
|
||||||
|
published_at: Date
|
||||||
|
published_scope: string
|
||||||
|
status: string
|
||||||
|
tags: string[]
|
||||||
|
template_suffix: string
|
||||||
|
title: string
|
||||||
|
updated_at: Date
|
||||||
|
variants: ProductVariant[]
|
||||||
|
vendor: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProductImage {
|
||||||
|
id: number
|
||||||
|
product_id: number
|
||||||
|
position: number
|
||||||
|
created_at: Date
|
||||||
|
updated_at: Date
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
src: string
|
||||||
|
variant_ids: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProductOption {
|
||||||
|
id: number
|
||||||
|
product_id: number
|
||||||
|
name: string
|
||||||
|
position: number
|
||||||
|
values: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProductVariant {
|
||||||
|
barcode: string
|
||||||
|
compare_at_price: number
|
||||||
|
created_at: Date
|
||||||
|
fulfillment_service: string
|
||||||
|
grams: number
|
||||||
|
weight: number
|
||||||
|
weight_unit: string
|
||||||
|
id: number
|
||||||
|
inventory_item_id: number
|
||||||
|
inventory_management: string
|
||||||
|
inventory_policy: string
|
||||||
|
inventory_quantity: number
|
||||||
|
option1: string
|
||||||
|
position: number
|
||||||
|
price: number
|
||||||
|
product_id: number
|
||||||
|
requires_shipping: boolean
|
||||||
|
sku: string
|
||||||
|
taxable: boolean
|
||||||
|
title: string
|
||||||
|
updated_at: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
class Products {
|
||||||
|
edges: ProductEdge[]
|
||||||
|
pageInfo: PageInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProductEdge {
|
||||||
|
node: Product
|
||||||
|
cursor: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageInfo {
|
||||||
|
hasNextPage: boolean
|
||||||
|
hasPreviousPage: boolean
|
||||||
|
startCursor: string
|
||||||
|
endCursor: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProductsQuery {
|
||||||
|
GQL: string
|
||||||
|
JSON: JSON
|
||||||
|
constructor(
|
||||||
|
fields: string[] = ["id", "title", "handle"],
|
||||||
|
cursor: string = ""
|
||||||
|
) {
|
||||||
|
let cursorText: string
|
||||||
|
if (cursor == "") {
|
||||||
|
cursorText = ""
|
||||||
|
} else {
|
||||||
|
cursorText = `, after: "${cursor}"`
|
||||||
|
}
|
||||||
|
this.GQL = `{
|
||||||
|
products(first: 10${cursorText}) {
|
||||||
|
edges {
|
||||||
|
node { ${fields.join(" ")} }
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
let j = `{"query": ${formatGqlForJSON(this.GQL)}}`
|
||||||
|
console.log(j)
|
||||||
|
this.JSON = JSON.parse(j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProductsResponse {
|
||||||
|
products: Products
|
||||||
|
constructor(response: {}) {
|
||||||
|
this.products = response["data"]["products"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatGqlForJSON(gql: string) {
|
||||||
|
let singleLine = gql.split("\n").join(" ").replace(/\s+/g, " ")
|
||||||
|
return JSON.stringify(singleLine)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user