- Full Node.js application with Shopify GraphQL API integration - Compare At price support for promotional pricing - Comprehensive error handling and retry logic - Progress tracking with markdown logging - Complete test suite with unit and integration tests - Production-ready with proper exit codes and signal handling
225 lines
6.9 KiB
Markdown
225 lines
6.9 KiB
Markdown
# 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
|