Updated to handle more then 100 products, and variants

This commit is contained in:
2025-08-27 16:26:17 -05:00
parent 98672fb99d
commit 588dda0413
4 changed files with 89 additions and 36 deletions

View File

@@ -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:00-05:00 # EST timezone
# 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
View File

@@ -151,4 +151,5 @@ coverage/
# Build artifacts
build/
dist/
dist/
.env.test

View File

@@ -22,7 +22,7 @@ async function debugTags() {
const productService = new ProductService();
// 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)
const targetTag = config.targetTag.toLowerCase();
@@ -86,4 +86,4 @@ async function debugTags() {
}
// Run the debug function
debugTags();
debugTags();

View File

@@ -31,16 +31,6 @@ class ProductService {
id
title
tags
variants(first: 100) {
edges {
node {
id
price
compareAtPrice
title
}
}
}
}
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
*/
@@ -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
* @param {string} tag - Tag to filter products by
@@ -141,23 +200,14 @@ class ProductService {
const { edges, pageInfo } = response.products;
// Process products from this page
const pageProducts = edges.map((edge) => ({
id: edge.node.id,
title: edge.node.title,
tags: edge.node.tags,
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,
})),
}));
for (const edge of edges) {
const product = edge.node;
product.variants = await this.fetchAllVariants(product.id);
allProducts.push(product);
}
allProducts.push(...pageProducts);
await this.logger.info(
`Found ${pageProducts.length} products on page ${pageCount}`
`Found ${edges.length} products on page ${pageCount}`
);
// Update pagination info
@@ -214,15 +264,16 @@ class ProductService {
// Check if variants have valid price data
const validVariants = [];
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(
`Skipping variant "${variant.title}" in product "${product.title}" - invalid price: ${variant.price}`
);
continue;
}
if (variant.price < 0) {
if (price < 0) {
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;
}
@@ -250,7 +301,7 @@ class ProductService {
);
}
await this.logger.info(
await this.logger.info(
`Validated ${validProducts.length} products for price updates`
);
return validProducts;
@@ -865,7 +916,7 @@ class ProductService {
try {
// Prepare price update with Compare At price
const priceUpdate = preparePriceUpdate(
variant.price,
parseFloat(variant.price),
priceAdjustmentPercentage
);
@@ -925,7 +976,7 @@ class ProductService {
* @param {number} limit - Maximum number of products to fetch for debugging
* @returns {Promise<Array>} Array of products with their tags
*/
async debugFetchAllProductTags(limit = 50) {
async debugFetchAllProductTags(limit = Infinity) {
await this.logger.info(
`Fetching up to ${limit} products to analyze tags...`
);
@@ -1165,4 +1216,4 @@ class ProductService {
}
}
module.exports = ProductService;
module.exports = ProductService;