The job_run_logs table tracks scheduled job orchestration, not individual worker jobs. Worker info (worker_id, worker_hostname) belongs on dispensary_crawl_jobs, not job_run_logs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
180 lines
7.7 KiB
JavaScript
180 lines
7.7 KiB
JavaScript
"use strict";
|
|
/**
|
|
* Test script to validate Dutchie GraphQL API access and capture response structure
|
|
*/
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
// @ts-ignore - node-fetch type declaration not installed
|
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
const GRAPHQL_HASHES = {
|
|
ConsumerDispensaries: '0a5bfa6ca1d64ae47bcccb7c8077c87147cbc4e6982c17ceec97a2a4948b311b',
|
|
GetAddressBasedDispensaryData: '13461f73abf7268770dfd05fe7e10c523084b2bb916a929c08efe3d87531977b',
|
|
FilteredProducts: 'ee29c060826dc41c527e470e9ae502c9b2c169720faa0a9f5d25e1b9a530a4a0',
|
|
MenuFiltersV2: '2f0b3233b8a2426b391649ca3f0f7a5d43b9aefd683f6286d7261a2517e3568e',
|
|
FilteredSpecials: '0dfb85a4fc138c55a076d4d11bf6d1a25f7cbd511428e1cf5a5b863b3eb23f25',
|
|
};
|
|
async function fetchProducts(dispensaryId, page = 0, perPage = 25) {
|
|
const session = 'crawlsy-session-' + Date.now();
|
|
const variables = {
|
|
includeEnterpriseSpecials: false,
|
|
productsFilter: {
|
|
dispensaryId,
|
|
pricingType: 'rec',
|
|
Status: null, // null to include all (in-stock and out-of-stock)
|
|
types: [],
|
|
useCache: true,
|
|
isDefaultSort: true,
|
|
sortBy: 'popularSortIdx',
|
|
sortDirection: 1,
|
|
bypassOnlineThresholds: true,
|
|
isKioskMenu: false,
|
|
removeProductsBelowOptionThresholds: false
|
|
},
|
|
page,
|
|
perPage
|
|
};
|
|
const qs = new URLSearchParams({
|
|
operationName: 'FilteredProducts',
|
|
variables: JSON.stringify(variables),
|
|
extensions: JSON.stringify({ persistedQuery: { version: 1, sha256Hash: GRAPHQL_HASHES.FilteredProducts } })
|
|
});
|
|
const res = await (0, node_fetch_1.default)(`https://dutchie.com/api-3/graphql?${qs.toString()}`, {
|
|
headers: {
|
|
'x-dutchie-session': session,
|
|
'apollographql-client-name': 'Marketplace (production)',
|
|
'content-type': 'application/json',
|
|
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
|
}
|
|
});
|
|
if (!res.ok) {
|
|
const text = await res.text();
|
|
console.error('HTTP Status:', res.status);
|
|
console.error('Response:', text.substring(0, 500));
|
|
throw new Error(`HTTP ${res.status}: ${text.substring(0, 200)}`);
|
|
}
|
|
return res.json();
|
|
}
|
|
async function resolveDispensaryId(cName) {
|
|
const session = 'crawlsy-session-' + Date.now();
|
|
const variables = { input: { dispensaryId: cName } };
|
|
const qs = new URLSearchParams({
|
|
operationName: 'GetAddressBasedDispensaryData',
|
|
variables: JSON.stringify(variables),
|
|
extensions: JSON.stringify({ persistedQuery: { version: 1, sha256Hash: GRAPHQL_HASHES.GetAddressBasedDispensaryData } })
|
|
});
|
|
const res = await (0, node_fetch_1.default)(`https://dutchie.com/graphql?${qs.toString()}`, {
|
|
headers: {
|
|
'x-dutchie-session': session,
|
|
'apollographql-client-name': 'Marketplace (production)',
|
|
'content-type': 'application/json',
|
|
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
}
|
|
});
|
|
if (!res.ok) {
|
|
console.error('Failed to resolve dispensary ID:', res.status);
|
|
return null;
|
|
}
|
|
const data = await res.json();
|
|
return data?.data?.getAddressBasedDispensaryData?.dispensaryData?.dispensaryId || null;
|
|
}
|
|
function enumerateFields(obj, prefix = '') {
|
|
const fields = [];
|
|
for (const [key, value] of Object.entries(obj)) {
|
|
const path = prefix ? `${prefix}.${key}` : key;
|
|
if (value === null) {
|
|
fields.push(`${path}: null`);
|
|
}
|
|
else if (Array.isArray(value)) {
|
|
fields.push(`${path}: Array[${value.length}]`);
|
|
if (value.length > 0 && typeof value[0] === 'object') {
|
|
const subFields = enumerateFields(value[0], `${path}[0]`);
|
|
fields.push(...subFields);
|
|
}
|
|
}
|
|
else if (typeof value === 'object') {
|
|
fields.push(`${path}: Object`);
|
|
const subFields = enumerateFields(value, path);
|
|
fields.push(...subFields);
|
|
}
|
|
else {
|
|
const typeStr = typeof value;
|
|
const preview = String(value).substring(0, 50);
|
|
fields.push(`${path}: ${typeStr} = "${preview}"`);
|
|
}
|
|
}
|
|
return fields;
|
|
}
|
|
async function main() {
|
|
console.log('='.repeat(80));
|
|
console.log('DUTCHIE GRAPHQL API TEST');
|
|
console.log('='.repeat(80));
|
|
const cName = 'AZ-Deeply-Rooted';
|
|
// Step 1: Resolve dispensary ID
|
|
console.log(`\n1. Resolving dispensary ID for "${cName}"...`);
|
|
const dispensaryId = await resolveDispensaryId(cName);
|
|
const finalDispensaryId = dispensaryId || '6405ef617056e8014d79101b'; // Fallback to known ID
|
|
if (!dispensaryId) {
|
|
console.log(' Failed to resolve via API, using hardcoded ID: 6405ef617056e8014d79101b');
|
|
}
|
|
console.log(` Final ID: ${finalDispensaryId}`);
|
|
// Step 2: Fetch first page of products
|
|
console.log('\n2. Fetching products (page 0, perPage 5)...');
|
|
const result = await fetchProducts(finalDispensaryId, 0, 5);
|
|
if (result.errors) {
|
|
console.error('\nGraphQL Errors:');
|
|
console.error(JSON.stringify(result.errors, null, 2));
|
|
return;
|
|
}
|
|
const products = result?.data?.filteredProducts?.products || [];
|
|
console.log(` Found ${products.length} products in this page`);
|
|
if (products.length === 0) {
|
|
console.log('No products returned. Full response:');
|
|
console.log(JSON.stringify(result, null, 2));
|
|
return;
|
|
}
|
|
// Step 3: Enumerate all fields from first product
|
|
console.log('\n3. PRODUCT FIELD STRUCTURE (from first product):');
|
|
console.log('-'.repeat(80));
|
|
const product = products[0];
|
|
const fields = enumerateFields(product);
|
|
fields.forEach(f => console.log(` ${f}`));
|
|
// Step 4: Show full sample product JSON
|
|
console.log('\n4. FULL SAMPLE PRODUCT JSON:');
|
|
console.log('-'.repeat(80));
|
|
console.log(JSON.stringify(product, null, 2));
|
|
// Step 5: Summary of key fields for schema design
|
|
console.log('\n5. KEY FIELDS FOR SCHEMA DESIGN:');
|
|
console.log('-'.repeat(80));
|
|
const keyFields = [
|
|
{ field: 'id', value: product.id },
|
|
{ field: 'name', value: product.name },
|
|
{ field: 'slug', value: product.slug },
|
|
{ field: 'brand', value: product.brand },
|
|
{ field: 'brandId', value: product.brandId },
|
|
{ field: 'type', value: product.type },
|
|
{ field: 'category', value: product.category },
|
|
{ field: 'subcategory', value: product.subcategory },
|
|
{ field: 'strainType', value: product.strainType },
|
|
{ field: 'THCContent', value: product.THCContent },
|
|
{ field: 'CBDContent', value: product.CBDContent },
|
|
{ field: 'description', value: product.description?.substring(0, 100) + '...' },
|
|
{ field: 'image', value: product.image },
|
|
{ field: 'options.length', value: product.options?.length },
|
|
{ field: 'pricing', value: product.pricing },
|
|
{ field: 'terpenes.length', value: product.terpenes?.length },
|
|
{ field: 'effects.length', value: product.effects?.length },
|
|
];
|
|
keyFields.forEach(({ field, value }) => {
|
|
console.log(` ${field}: ${JSON.stringify(value)}`);
|
|
});
|
|
// Step 6: Show an option (variant) if available
|
|
if (product.options && product.options.length > 0) {
|
|
console.log('\n6. SAMPLE OPTION/VARIANT:');
|
|
console.log('-'.repeat(80));
|
|
console.log(JSON.stringify(product.options[0], null, 2));
|
|
}
|
|
}
|
|
main().catch(console.error);
|