Files
cannaiq/backend/dist/scripts/test-dutchie-graphql.js
Kelly 66e07b2009 fix(monitor): remove non-existent worker columns from job_run_logs query
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>
2025-12-03 18:45:05 -07:00

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);