# Design Document ## Overview The Shopify Price Updater is a Node.js command-line script that uses Shopify's GraphQL Admin API to bulk update product prices based on tag filtering. The script will be built using the `@shopify/shopify-api` package and will implement proper error handling, rate limiting, and progress tracking. ## Architecture The application follows a modular architecture with clear separation of concerns: ``` shopify-price-updater/ ├── src/ │ ├── config/ │ │ └── environment.js # Environment variable loading and validation │ ├── services/ │ │ ├── shopify.js # Shopify API client and authentication │ │ ├── product.js # Product querying and updating logic │ │ └── progress.js # Progress logging functionality │ ├── utils/ │ │ ├── price.js # Price calculation utilities │ │ └── logger.js # Console and file logging utilities │ └── index.js # Main application entry point ├── .env.example # Environment variable template ├── package.json └── Progress.md # Generated progress log file ``` ## Compare At Price Workflow The Compare At price functionality works as follows: 1. **Price Capture**: When a product variant is processed, the current price is captured as the "original price" 2. **Price Calculation**: The new price is calculated by applying the percentage adjustment to the original price 3. **Compare At Assignment**: The original price (before adjustment) is set as the Compare At price 4. **Simultaneous Update**: Both the new price and Compare At price are updated in a single GraphQL mutation 5. **Progress Tracking**: All three values (original, new, and Compare At) are logged for transparency This creates a promotional pricing display where customers can see both the current discounted price and the original price for comparison. ## Components and Interfaces ### Environment Configuration (`config/environment.js`) - Loads and validates environment variables from `.env` file - Required variables: `SHOPIFY_SHOP_DOMAIN`, `SHOPIFY_ACCESS_TOKEN`, `TARGET_TAG`, `PRICE_ADJUSTMENT_PERCENTAGE` - Validates that percentage is a valid number and tag is not empty - Exports configuration object with validated values ### Shopify Service (`services/shopify.js`) - Initializes Shopify GraphQL client using `@shopify/shopify-api` - Handles authentication and session management - Provides methods for executing GraphQL queries and mutations - Implements retry logic for rate limiting (HTTP 429 responses) ### Product Service (`services/product.js`) - Implements GraphQL queries to fetch products by tag - Handles pagination for large product sets using cursor-based pagination - Implements GraphQL mutations for price and Compare At price updates - Manages product variant price updates (products can have multiple variants) - Sets Compare At price to the original price before applying percentage adjustment - Automatically formats tag queries with "tag:" prefix if not already present ### Progress Service (`services/progress.js`) - Creates and manages Progress.md file - Logs operation start, product updates, errors, and completion summary - Appends to existing file with timestamps for multiple runs - Formats progress entries in markdown for readability ### Price Utilities (`utils/price.js`) - Calculates new prices based on percentage adjustment - Handles rounding to appropriate decimal places for currency - Validates price ranges and handles edge cases (negative prices, zero prices) - Provides functions to prepare price update objects with both price and Compare At price values ### Logger Utilities (`utils/logger.js`) - Provides consistent logging to console and progress file - Implements different log levels (info, warning, error) - Formats output for both human readability and progress tracking ## Data Models ### Product Query Response ```graphql query getProductsByTag($query: String!, $first: Int!, $after: String) { products(first: $first, after: $after, query: $query) { edges { node { id title tags variants(first: 100) { edges { node { id price compareAtPrice } } } } cursor } pageInfo { hasNextPage endCursor } } } ``` ### Product Update Mutation ```graphql mutation productVariantUpdate($input: ProductVariantInput!) { productVariantUpdate(input: $input) { productVariant { id price compareAtPrice } userErrors { field message } } } ``` ### Configuration Object ```javascript { shopDomain: string, accessToken: string, targetTag: string, priceAdjustmentPercentage: number } ``` ### Progress Entry ```javascript { timestamp: Date, productId: string, productTitle: string, variantId: string, oldPrice: number, newPrice: number, compareAtPrice: number, status: 'success' | 'error', errorMessage?: string } ``` ## Error Handling ### Rate Limiting - Implement exponential backoff for HTTP 429 responses - Use Shopify's rate limit headers to determine retry timing - Maximum retry attempts: 3 with increasing delays (1s, 2s, 4s) ### API Errors - Parse GraphQL userErrors from mutation responses - Log specific error messages for debugging - Continue processing remaining products when individual updates fail ### Network Errors - Catch and handle network connectivity issues - Implement retry logic for transient network failures - Graceful degradation when API is temporarily unavailable ### Data Validation - Validate product data before attempting updates - Skip products with invalid price data - Handle edge cases like products without variants ### Progress Tracking Errors - Continue script execution even if progress logging fails - Fallback to console-only logging if file writing fails - Ensure critical operations aren't blocked by logging issues ## Testing Strategy ### Unit Tests - Test price calculation utilities with various percentage values - Test environment configuration loading and validation - Test GraphQL query and mutation builders - Test error handling scenarios with mocked API responses ### Integration Tests - Test Shopify API authentication with test credentials - Test product querying with known test data - Test price update operations in Shopify development store - Test progress logging functionality ### End-to-End Tests - Test complete workflow with development store - Verify price updates are applied correctly - Test error recovery and continuation scenarios - Validate progress logging accuracy ### Manual Testing Checklist - Test with various percentage values (positive, negative, decimal) - Test with products having multiple variants - Test with large product sets requiring pagination - Test error scenarios (invalid credentials, network issues) - Verify Progress.md file generation and formatting