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: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
3
.gitignore
vendored
@@ -151,4 +151,5 @@ coverage/
|
||||
|
||||
# Build artifacts
|
||||
build/
|
||||
dist/
|
||||
dist/
|
||||
.env.test
|
||||
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user