- Implemented setup_ordrestyring_import.sh for initial database setup and data import from API. - Created sync_ordrestyring_data.sh for incremental updates to the local database. - Developed test_ordrestyring_import.js to validate API data import functionality. - Added test_enhanced_quote_demo.js to demonstrate the enhanced quote system using hybrid data sources. - Introduced test_ordrestyring_system.sh for comprehensive testing of the database system and API functionality. - Enhanced logging and error handling across scripts for better traceability. - Included analysis script creation for post-import data analysis.
253 lines
7.9 KiB
JavaScript
253 lines
7.9 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const axios = require('axios');
|
|
const fs = require('fs');
|
|
|
|
// Load environment
|
|
require('dotenv').config({ path: '.env.ordrestyring' });
|
|
|
|
const API_BASE = 'https://v2.api.ordrestyring.dk';
|
|
const TOKEN = process.env.ORDRESTYRING_TOKEN;
|
|
|
|
if (!TOKEN) {
|
|
console.error('❌ ORDRESTYRING_TOKEN not found in environment');
|
|
process.exit(1);
|
|
}
|
|
|
|
// API endpoints to analyze
|
|
const endpoints = [
|
|
'/cases',
|
|
'/case-materials',
|
|
'/hours',
|
|
'/debtors',
|
|
'/debtor-invoices',
|
|
'/users',
|
|
'/departments',
|
|
'/case-types',
|
|
'/case-statuses',
|
|
'/employee-types'
|
|
];
|
|
|
|
async function makeRequest(endpoint, params = {}) {
|
|
try {
|
|
const config = {
|
|
method: 'GET',
|
|
url: `${API_BASE}${endpoint}`,
|
|
auth: {
|
|
username: TOKEN,
|
|
password: 'x'
|
|
},
|
|
params: {
|
|
pagesize: 5, // Just get 5 samples to analyze format
|
|
...params
|
|
}
|
|
};
|
|
|
|
console.log(`🌐 Making request to: ${config.url}`);
|
|
console.log(`🔑 Auth: ${TOKEN.substring(0, 8)}***`);
|
|
|
|
const response = await axios(config);
|
|
console.log(`📦 Response status: ${response.status}`);
|
|
console.log(`📊 Response data structure:`, {
|
|
hasData: !!response.data,
|
|
dataType: typeof response.data,
|
|
dataKeys: response.data ? Object.keys(response.data) : [],
|
|
totalProperty: response.data?.total,
|
|
dataLength: response.data?.data?.length
|
|
});
|
|
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error(`❌ Error fetching ${endpoint}:`, {
|
|
status: error.response?.status,
|
|
statusText: error.response?.statusText,
|
|
message: error.message,
|
|
responseData: error.response?.data
|
|
});
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function analyzeDataFormats() {
|
|
console.log('🔍 Analyzing Ordrestyring API Data Formats');
|
|
console.log('==========================================');
|
|
|
|
const analysis = {
|
|
timestamp: new Date().toISOString(),
|
|
endpoints: {}
|
|
};
|
|
|
|
for (const endpoint of endpoints) {
|
|
console.log(`\n📊 Analyzing ${endpoint}...`);
|
|
|
|
const data = await makeRequest(endpoint);
|
|
|
|
if (data && typeof data === 'object') {
|
|
// Check if response is an array-like object with numeric keys
|
|
const dataKeys = Object.keys(data);
|
|
const isArrayLike = dataKeys.length > 0 && dataKeys.every(key => !isNaN(key));
|
|
|
|
let records = [];
|
|
let totalCount = 0;
|
|
|
|
if (isArrayLike) {
|
|
// Convert object with numeric keys to array
|
|
records = Object.values(data);
|
|
totalCount = records.length;
|
|
} else if (data.data && Array.isArray(data.data)) {
|
|
// Standard API response format
|
|
records = data.data;
|
|
totalCount = data.total || records.length;
|
|
} else if (Array.isArray(data)) {
|
|
// Direct array response
|
|
records = data;
|
|
totalCount = records.length;
|
|
}
|
|
|
|
if (records.length > 0) {
|
|
const sampleRecord = records[0];
|
|
const recordStructure = analyzeRecordStructure(sampleRecord);
|
|
|
|
analysis.endpoints[endpoint] = {
|
|
available: true,
|
|
totalRecords: totalCount,
|
|
sampleRecord: sampleRecord,
|
|
structure: recordStructure,
|
|
suggestedTableName: endpoint.replace('/', '').replace('-', '_'),
|
|
sqlSchema: generateSQLSchema(endpoint.replace('/', '').replace('-', '_'), recordStructure)
|
|
};
|
|
|
|
console.log(`✅ ${endpoint}: ${totalCount} records available`);
|
|
console.log(` Structure: ${Object.keys(recordStructure).length} fields`);
|
|
console.log(` Key fields: ${Object.keys(recordStructure).slice(0, 5).join(', ')}`);
|
|
console.log(` Sample ID: ${sampleRecord.id || 'N/A'}`);
|
|
|
|
} else {
|
|
analysis.endpoints[endpoint] = {
|
|
available: false,
|
|
error: 'No records in response'
|
|
};
|
|
console.log(`❌ ${endpoint}: No records found`);
|
|
}
|
|
} else {
|
|
analysis.endpoints[endpoint] = {
|
|
available: false,
|
|
error: 'No data or empty response'
|
|
};
|
|
console.log(`❌ ${endpoint}: No data available`);
|
|
}
|
|
|
|
// Small delay to be respectful to API
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
}
|
|
|
|
// Save analysis
|
|
fs.writeFileSync('api_data_formats_analysis.json', JSON.stringify(analysis, null, 2));
|
|
console.log('\n💾 Analysis saved to api_data_formats_analysis.json');
|
|
|
|
// Generate combined SQL schema
|
|
generateCombinedSchema(analysis);
|
|
|
|
return analysis;
|
|
}
|
|
|
|
function analyzeRecordStructure(record) {
|
|
const structure = {};
|
|
|
|
for (const [key, value] of Object.entries(record)) {
|
|
let type = typeof value;
|
|
let sqlType = 'TEXT';
|
|
let maxLength = null;
|
|
|
|
if (value === null) {
|
|
type = 'null';
|
|
sqlType = 'TEXT NULL';
|
|
} else if (type === 'string') {
|
|
maxLength = value.length;
|
|
if (maxLength <= 255) {
|
|
sqlType = `VARCHAR(${Math.max(255, maxLength * 2)})`;
|
|
} else {
|
|
sqlType = 'TEXT';
|
|
}
|
|
|
|
// Check if it's a date
|
|
if (isDateString(value)) {
|
|
type = 'date';
|
|
sqlType = 'DATETIME';
|
|
}
|
|
} else if (type === 'number') {
|
|
if (Number.isInteger(value)) {
|
|
sqlType = 'INT';
|
|
} else {
|
|
sqlType = 'DECIMAL(10,2)';
|
|
}
|
|
} else if (type === 'boolean') {
|
|
sqlType = 'BOOLEAN';
|
|
} else if (type === 'object') {
|
|
sqlType = 'JSON';
|
|
}
|
|
|
|
structure[key] = {
|
|
type,
|
|
sqlType,
|
|
maxLength,
|
|
sampleValue: value
|
|
};
|
|
}
|
|
|
|
return structure;
|
|
}
|
|
|
|
function isDateString(str) {
|
|
if (typeof str !== 'string') return false;
|
|
const date = new Date(str);
|
|
return !isNaN(date.getTime()) && (str.includes('T') || str.includes('-'));
|
|
}
|
|
|
|
function generateSQLSchema(tableName, structure) {
|
|
const fields = [];
|
|
|
|
// Add primary key
|
|
if (structure.id) {
|
|
fields.push(`id ${structure.id.sqlType} PRIMARY KEY`);
|
|
} else {
|
|
fields.push(`id INT AUTO_INCREMENT PRIMARY KEY`);
|
|
}
|
|
|
|
// Add other fields
|
|
for (const [fieldName, fieldInfo] of Object.entries(structure)) {
|
|
if (fieldName !== 'id') {
|
|
const nullable = fieldInfo.type === 'null' ? '' : ' NULL';
|
|
fields.push(`${fieldName} ${fieldInfo.sqlType}${nullable}`);
|
|
}
|
|
}
|
|
|
|
return `CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
${fields.join(',\n ')}
|
|
);`;
|
|
}
|
|
|
|
function generateCombinedSchema(analysis) {
|
|
let combinedSchema = `-- Ordrestyring API Database Schema
|
|
-- Generated: ${new Date().toISOString()}
|
|
--
|
|
-- This schema supports all data types from Ordrestyring API
|
|
|
|
USE ordrestyring_local;
|
|
|
|
`;
|
|
|
|
for (const [endpoint, data] of Object.entries(analysis.endpoints)) {
|
|
if (data.available && data.sqlSchema) {
|
|
combinedSchema += `-- Table for ${endpoint}\n`;
|
|
combinedSchema += data.sqlSchema + '\n\n';
|
|
}
|
|
}
|
|
|
|
fs.writeFileSync('ordrestyring_complete_schema.sql', combinedSchema);
|
|
console.log('📝 Complete SQL schema saved to ordrestyring_complete_schema.sql');
|
|
}
|
|
|
|
// Run analysis
|
|
analyzeDataFormats().catch(console.error);
|