← back to Flockedwallpaper
fetch-flocked-products.js
266 lines
require('dotenv').config();
const axios = require('axios');
const fs = require('fs');
const SHOPIFY_STORE = process.env.SHOPIFY_STORE_NAME;
const ACCESS_TOKEN = process.env.SHOPIFY_ACCESS_TOKEN;
const API_VERSION = process.env.SHOPIFY_API_VERSION || '2024-10';
// Shopify Admin API GraphQL endpoint
const GRAPHQL_ENDPOINT = `https://${SHOPIFY_STORE}.myshopify.com/admin/api/${API_VERSION}/graphql.json`;
async function fetchAllProducts() {
console.log('🔍 Fetching flocked products from Shopify...');
const allProducts = [];
let hasNextPage = true;
let cursor = null;
while (hasNextPage) {
const query = `
query ($cursor: String) {
products(first: 250, after: $cursor, query: "flock status:active") {
edges {
cursor
node {
id
title
handle
description
descriptionHtml
productType
vendor
tags
createdAt
updatedAt
status
variants(first: 10) {
edges {
node {
id
title
sku
price
inventoryQuantity
}
}
}
featuredImage {
url
altText
}
images(first: 5) {
edges {
node {
url
altText
}
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
`;
try {
const response = await axios.post(
GRAPHQL_ENDPOINT,
{
query: query,
variables: { cursor: cursor }
},
{
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': ACCESS_TOKEN
}
}
);
if (response.data.errors) {
console.error('GraphQL Errors:', response.data.errors);
break;
}
const products = response.data.data.products.edges;
allProducts.push(...products);
hasNextPage = response.data.data.products.pageInfo.hasNextPage;
cursor = response.data.data.products.pageInfo.endCursor;
console.log(` Fetched ${allProducts.length} products so far...`);
// Rate limiting: wait 500ms between requests
await new Promise(resolve => setTimeout(resolve, 500));
} catch (error) {
console.error('❌ Error fetching products:', error.message);
if (error.response) {
console.error('Response data:', error.response.data);
}
break;
}
}
console.log(`\n✅ Total products fetched: ${allProducts.length}`);
return allProducts;
}
function filterFlockedProducts(products) {
console.log('\n🔍 Processing flocked products...');
const flockedProducts = [];
// Color families for sorting
const colorFamilies = {
'Black': ['black', 'onyx', 'charcoal', 'ebony', 'midnight'],
'White': ['white', 'ivory', 'cream', 'ecru', 'pearl', 'chalk', 'vanilla'],
'Red': ['red', 'crimson', 'burgundy', 'wine', 'claret', 'cabernet', 'vermillion', 'carnelian'],
'Pink': ['pink', 'blush', 'fuchsia', 'magenta', 'rose'],
'Purple': ['purple', 'plum', 'aubergine', 'amethyst', 'violet', 'lavender'],
'Blue': ['blue', 'navy', 'cobalt', 'azure', 'indigo', 'sapphire', 'slate', 'wedgewood'],
'Green': ['green', 'emerald', 'jade', 'olive', 'viridian', 'hunter', 'evergreen'],
'Aqua': ['aqua', 'teal', 'turquoise', 'cyan'],
'Yellow': ['yellow', 'gold', 'golden'],
'Orange': ['orange', 'coral', 'apricot', 'peach'],
'Brown': ['brown', 'chocolate', 'mocha', 'tan', 'camel', 'java', 'mahogany'],
'Beige': ['beige', 'taupe', 'buff', 'greige', 'birch', 'sand'],
'Gray': ['gray', 'grey', 'silver', 'slate', 'frost', 'glacier'],
'Neutral': ['neutral', 'linen', 'putty', 'concrete']
};
products.forEach(product => {
const node = product.node;
// Only include ACTIVE products
if (node.status !== 'ACTIVE') {
return;
}
// Extract SKU from variants
const variants = node.variants.edges;
const skus = variants.map(v => v.node.sku).filter(sku => sku);
// Determine primary color family
let primaryColor = 'Other';
const tagString = node.tags.join(' ').toLowerCase();
for (const [family, keywords] of Object.entries(colorFamilies)) {
if (keywords.some(keyword => tagString.includes(keyword))) {
primaryColor = family;
break;
}
}
flockedProducts.push({
id: node.id,
title: node.title,
handle: node.handle,
description: node.description,
vendor: node.vendor,
productType: node.productType,
tags: node.tags,
primaryColor: primaryColor,
skus: skus,
primarySku: skus[0] || 'N/A',
price: variants[0]?.node.price || '0.00',
image: node.featuredImage?.url || node.images.edges[0]?.node.url,
imageAlt: node.featuredImage?.altText || node.images.edges[0]?.node.altText,
productUrl: `https://designerwallcoverings.com/products/${node.handle}`,
status: node.status,
createdAt: node.createdAt
});
console.log(` ✅ ${node.title} (SKU: ${skus.join(', ')})`);
});
console.log(`\n✅ Found ${flockedProducts.length} active flocked products`);
return flockedProducts;
}
function sortByColor(products) {
console.log('\n🎨 Sorting products by color...');
// Define color order
const colorOrder = [
'Black', 'White', 'Red', 'Pink', 'Purple',
'Blue', 'Green', 'Aqua', 'Yellow', 'Orange',
'Brown', 'Beige', 'Gray', 'Neutral', 'Other'
];
// Sort products by color family
const sorted = products.sort((a, b) => {
const indexA = colorOrder.indexOf(a.primaryColor);
const indexB = colorOrder.indexOf(b.primaryColor);
if (indexA !== indexB) {
return indexA - indexB;
}
// Within same color, sort by title
return a.title.localeCompare(b.title);
});
// Count products per color
const colorCounts = {};
sorted.forEach(p => {
colorCounts[p.primaryColor] = (colorCounts[p.primaryColor] || 0) + 1;
});
console.log('\n📊 Products per color:');
colorOrder.forEach(color => {
if (colorCounts[color]) {
console.log(` ${color}: ${colorCounts[color]} products`);
}
});
return sorted;
}
async function main() {
try {
console.log('🚀 Starting Flocked Product Fetch Process\n');
console.log(`Store: ${SHOPIFY_STORE}`);
console.log(`API Version: ${API_VERSION}\n`);
// Fetch all products
const allProducts = await fetchAllProducts();
// Filter flocked products
const flockedProducts = filterFlockedProducts(allProducts);
// Sort by color
const sortedProducts = sortByColor(flockedProducts);
// Save to JSON file
const outputPath = './flocked-products.json';
fs.writeFileSync(outputPath, JSON.stringify(sortedProducts, null, 2));
console.log(`\n💾 Saved ${sortedProducts.length} products to ${outputPath}`);
// Generate a summary
console.log('\n📊 SUMMARY:');
console.log(` Total Products: ${allProducts.length}`);
console.log(` Flocked Products: ${flockedProducts.length}`);
console.log(` Percentage: ${((flockedProducts.length / allProducts.length) * 100).toFixed(2)}%`);
// Show first 5 products
console.log('\n📦 Sample Products:');
flockedProducts.slice(0, 5).forEach((product, index) => {
console.log(` ${index + 1}. ${product.title}`);
console.log(` SKU: ${product.primarySku}`);
console.log(` URL: ${product.productUrl}`);
});
} catch (error) {
console.error('\n❌ Fatal Error:', error.message);
process.exit(1);
}
}
main();