Updated to handle more then 100 products, and variants
This commit is contained in:
@@ -24,4 +24,5 @@ PRICE_ADJUSTMENT_PERCENTAGE=10
|
|||||||
# SCHEDULED_EXECUTION_TIME=2024-12-25T10:30:00Z # UTC timezone
|
# SCHEDULED_EXECUTION_TIME=2024-12-25T10:30:00Z # UTC timezone
|
||||||
# SCHEDULED_EXECUTION_TIME=2024-12-25T10:30:00-05:00 # EST timezone
|
# SCHEDULED_EXECUTION_TIME=2024-12-25T10:30:00-05:00 # EST timezone
|
||||||
# Leave commented out or remove to execute immediately (default behavior)
|
# Leave commented out or remove to execute immediately (default behavior)
|
||||||
# SCHEDULED_EXECUTION_TIME=2024-12-25T10:30:00
|
# Note: It uses military time (24-hour format), and wants months and days to be two digits (e.g., 08 for August, 09 for September)
|
||||||
|
# SCHEDULED_EXECUTION_TIME=2024-12-25T10:30:00
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -151,4 +151,5 @@ coverage/
|
|||||||
|
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
|
.env.test
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ async function debugTags() {
|
|||||||
const productService = new ProductService();
|
const productService = new ProductService();
|
||||||
|
|
||||||
// Fetch products and analyze tags
|
// Fetch products and analyze tags
|
||||||
const products = await productService.debugFetchAllProductTags(100);
|
const products = await productService.debugFetchAllProductTags();
|
||||||
|
|
||||||
// Check if the target tag exists (case-insensitive search)
|
// Check if the target tag exists (case-insensitive search)
|
||||||
const targetTag = config.targetTag.toLowerCase();
|
const targetTag = config.targetTag.toLowerCase();
|
||||||
@@ -86,4 +86,4 @@ async function debugTags() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run the debug function
|
// Run the debug function
|
||||||
debugTags();
|
debugTags();
|
||||||
@@ -31,16 +31,6 @@ class ProductService {
|
|||||||
id
|
id
|
||||||
title
|
title
|
||||||
tags
|
tags
|
||||||
variants(first: 100) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
price
|
|
||||||
compareAtPrice
|
|
||||||
title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cursor
|
cursor
|
||||||
}
|
}
|
||||||
@@ -77,6 +67,34 @@ class ProductService {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GraphQL query to fetch variants for a product with pagination
|
||||||
|
*/
|
||||||
|
getVariantsByProductIdQuery() {
|
||||||
|
return `
|
||||||
|
query getVariantsByProductId($productId: ID!, $first: Int!, $after: String) {
|
||||||
|
node(id: $productId) {
|
||||||
|
... on Product {
|
||||||
|
variants(first: $first, after: $after) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
price
|
||||||
|
compareAtPrice
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GraphQL mutation to update product variant price and Compare At price
|
* GraphQL mutation to update product variant price and Compare At price
|
||||||
*/
|
*/
|
||||||
@@ -98,6 +116,47 @@ class ProductService {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all variants for a given product using pagination.
|
||||||
|
* @param {string} productId - The ID of the product to fetch variants for.
|
||||||
|
* @returns {Promise<Array>} A list of all variants for the product.
|
||||||
|
*/
|
||||||
|
async fetchAllVariants(productId) {
|
||||||
|
const allVariants = [];
|
||||||
|
let hasNextPage = true;
|
||||||
|
let cursor = null;
|
||||||
|
|
||||||
|
while (hasNextPage) {
|
||||||
|
const variables = {
|
||||||
|
productId: productId,
|
||||||
|
first: 250,
|
||||||
|
after: cursor,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await this.shopifyService.executeWithRetry(
|
||||||
|
() =>
|
||||||
|
this.shopifyService.executeQuery(
|
||||||
|
this.getVariantsByProductIdQuery(),
|
||||||
|
variables
|
||||||
|
),
|
||||||
|
this.logger
|
||||||
|
);
|
||||||
|
|
||||||
|
const productNode = response.node;
|
||||||
|
if (productNode && productNode.variants) {
|
||||||
|
const variants = productNode.variants.edges.map((edge) => edge.node);
|
||||||
|
allVariants.push(...variants);
|
||||||
|
|
||||||
|
hasNextPage = productNode.variants.pageInfo.hasNextPage;
|
||||||
|
cursor = productNode.variants.pageInfo.endCursor;
|
||||||
|
} else {
|
||||||
|
hasNextPage = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allVariants;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch all products with the specified tag using cursor-based pagination
|
* Fetch all products with the specified tag using cursor-based pagination
|
||||||
* @param {string} tag - Tag to filter products by
|
* @param {string} tag - Tag to filter products by
|
||||||
@@ -141,23 +200,14 @@ class ProductService {
|
|||||||
const { edges, pageInfo } = response.products;
|
const { edges, pageInfo } = response.products;
|
||||||
|
|
||||||
// Process products from this page
|
// Process products from this page
|
||||||
const pageProducts = edges.map((edge) => ({
|
for (const edge of edges) {
|
||||||
id: edge.node.id,
|
const product = edge.node;
|
||||||
title: edge.node.title,
|
product.variants = await this.fetchAllVariants(product.id);
|
||||||
tags: edge.node.tags,
|
allProducts.push(product);
|
||||||
variants: edge.node.variants.edges.map((variantEdge) => ({
|
}
|
||||||
id: variantEdge.node.id,
|
|
||||||
price: parseFloat(variantEdge.node.price),
|
|
||||||
compareAtPrice: variantEdge.node.compareAtPrice
|
|
||||||
? parseFloat(variantEdge.node.compareAtPrice)
|
|
||||||
: null,
|
|
||||||
title: variantEdge.node.title,
|
|
||||||
})),
|
|
||||||
}));
|
|
||||||
|
|
||||||
allProducts.push(...pageProducts);
|
|
||||||
await this.logger.info(
|
await this.logger.info(
|
||||||
`Found ${pageProducts.length} products on page ${pageCount}`
|
`Found ${edges.length} products on page ${pageCount}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update pagination info
|
// Update pagination info
|
||||||
@@ -214,15 +264,16 @@ class ProductService {
|
|||||||
// Check if variants have valid price data
|
// Check if variants have valid price data
|
||||||
const validVariants = [];
|
const validVariants = [];
|
||||||
for (const variant of product.variants) {
|
for (const variant of product.variants) {
|
||||||
if (typeof variant.price !== "number" || isNaN(variant.price)) {
|
const price = parseFloat(variant.price);
|
||||||
|
if (typeof price !== "number" || isNaN(price)) {
|
||||||
await this.logger.warning(
|
await this.logger.warning(
|
||||||
`Skipping variant "${variant.title}" in product "${product.title}" - invalid price: ${variant.price}`
|
`Skipping variant "${variant.title}" in product "${product.title}" - invalid price: ${variant.price}`
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (variant.price < 0) {
|
if (price < 0) {
|
||||||
await this.logger.warning(
|
await this.logger.warning(
|
||||||
`Skipping variant "${variant.title}" in product "${product.title}" - negative price: ${variant.price}`
|
`Skipping variant "${variant.title}" in product "${product.title}" - negative price: ${price}`
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -250,7 +301,7 @@ class ProductService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.logger.info(
|
await this.logger.info(
|
||||||
`Validated ${validProducts.length} products for price updates`
|
`Validated ${validProducts.length} products for price updates`
|
||||||
);
|
);
|
||||||
return validProducts;
|
return validProducts;
|
||||||
@@ -865,7 +916,7 @@ class ProductService {
|
|||||||
try {
|
try {
|
||||||
// Prepare price update with Compare At price
|
// Prepare price update with Compare At price
|
||||||
const priceUpdate = preparePriceUpdate(
|
const priceUpdate = preparePriceUpdate(
|
||||||
variant.price,
|
parseFloat(variant.price),
|
||||||
priceAdjustmentPercentage
|
priceAdjustmentPercentage
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -925,7 +976,7 @@ class ProductService {
|
|||||||
* @param {number} limit - Maximum number of products to fetch for debugging
|
* @param {number} limit - Maximum number of products to fetch for debugging
|
||||||
* @returns {Promise<Array>} Array of products with their tags
|
* @returns {Promise<Array>} Array of products with their tags
|
||||||
*/
|
*/
|
||||||
async debugFetchAllProductTags(limit = 50) {
|
async debugFetchAllProductTags(limit = Infinity) {
|
||||||
await this.logger.info(
|
await this.logger.info(
|
||||||
`Fetching up to ${limit} products to analyze tags...`
|
`Fetching up to ${limit} products to analyze tags...`
|
||||||
);
|
);
|
||||||
@@ -1165,4 +1216,4 @@ class ProductService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ProductService;
|
module.exports = ProductService;
|
||||||
Reference in New Issue
Block a user