UNPKG

defarm-sdk

Version:

DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain

415 lines (372 loc) 11.8 kB
/** * DeFarm Services Client - Reuses existing DeFarm monorepo services * * This client allows the SDK to call existing DeFarm services via API * to reuse duplicate detection, verification engines, and other business logic */ import type { Asset } from '../types'; export interface DeFarmServicesConfig { baseUrl: string; apiKey: string; timeout?: number; retries?: number; } export interface DuplicateDetectionRequest { category: 'livestock' | 'crops' | 'aquaculture' | 'forestry'; valuechain_type?: string; file_name?: string; asset_data: Record<string, any>; } export interface DuplicateDetectionResult { is_duplicate: boolean; confidence_score: number; matches: Array<{ existing_asset_id: string; confidence_score: number; matching_fields: Array<{ field_path: string; existing_value: any; new_value: any; match_type: 'exact' | 'fuzzy' | 'normalized'; similarity_score: number; }>; detection_method: string; }>; suggested_action: 'approve_new' | 'merge' | 'reject' | 'manual_review'; processing_time_ms: number; candidates_checked: number; } export interface VerificationResult { is_valid: boolean; verification_score: number; checks: Array<{ check_name: string; status: 'passed' | 'failed' | 'warning'; message: string; severity: 'low' | 'medium' | 'high' | 'critical'; }>; compliance_status: { eudr_compliant: boolean; deforestation_risk: 'low' | 'medium' | 'high'; sustainability_score: number; }; } /** * Client to reuse existing DeFarm business logic via API calls */ export class DeFarmServicesClient { private config: DeFarmServicesConfig; constructor(config: DeFarmServicesConfig) { this.config = { timeout: 30000, retries: 3, ...config }; } /** * Detect duplicates using existing DuplicateDetectionService */ async detectDuplicates(asset: Asset): Promise<DuplicateDetectionResult> { const request: DuplicateDetectionRequest = { category: asset.category, valuechain_type: this.mapCategoryToValuechain(asset.category, asset.subcategory), file_name: `sdk_${asset.dfid || asset.name}`, asset_data: this.mapAssetToLegacyFormat(asset) }; try { const response = await this.makeRequest('/api/services/duplicate-detection', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(request) }); return response.result || { is_duplicate: false, confidence_score: 0, matches: [], suggested_action: 'approve_new', processing_time_ms: 0, candidates_checked: 0 }; } catch (error) { console.warn('Duplicate detection service unavailable, proceeding without checks:', error); return { is_duplicate: false, confidence_score: 0, matches: [], suggested_action: 'approve_new', processing_time_ms: 0, candidates_checked: 0 }; } } /** * Run verification engines using existing VerificationEngine */ async runVerificationEngines(asset: Asset): Promise<VerificationResult> { const verificationRequest = { asset_data: this.mapAssetToLegacyFormat(asset), category: asset.category, location: asset.ownership.location, engines: [ 'eudr_compliance', 'prodes_deforestation', 'data_quality', 'sustainability_metrics' ] }; try { const response = await this.makeRequest('/api/services/verification-engine', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(verificationRequest) }); return response.result || { is_valid: true, verification_score: 0.8, checks: [], compliance_status: { eudr_compliant: true, deforestation_risk: 'low', sustainability_score: 0.8 } }; } catch (error) { console.warn('Verification engines unavailable, proceeding without checks:', error); return { is_valid: true, verification_score: 0.8, checks: [], compliance_status: { eudr_compliant: true, deforestation_risk: 'low', sustainability_score: 0.8 } }; } } /** * Generate DFID using existing DFIDService */ async generateEnhancedDFID(asset: Asset): Promise<{ dfid: string; components: { prefix: string; category_code: string; location_code: string; timestamp: string; checksum: string; }; }> { try { const response = await this.makeRequest('/api/services/dfid-generation', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ asset_data: this.mapAssetToLegacyFormat(asset), category: asset.category, location: asset.ownership.location }) }); return response.result; } catch (error) { console.warn('DFID service unavailable, using local generation:', error); // Fallback to local DFID generation const timestamp = Date.now().toString(36); const random = Math.random().toString(36).substring(2, 8); const categoryCode = asset.category.substring(0, 2).toUpperCase(); return { dfid: `DF${categoryCode}${timestamp}${random}`.toUpperCase(), components: { prefix: 'DF', category_code: categoryCode, location_code: asset.ownership.location?.country?.substring(0, 2) || 'XX', timestamp, checksum: random } }; } } /** * Calculate data aggregation using existing services */ async calculateDataAggregation(assets: Asset[]): Promise<{ aggregated_data: Record<string, any>; confidence_score: number; sources_count: number; conflicts: Array<{ field: string; values: any[]; resolution: string; }>; }> { try { const response = await this.makeRequest('/api/services/data-aggregation', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ assets: assets.map(asset => this.mapAssetToLegacyFormat(asset)) }) }); return response.result; } catch (error) { console.warn('Data aggregation service unavailable:', error); // Return simple aggregation return { aggregated_data: assets[0]?.data || {}, confidence_score: 1.0, sources_count: assets.length, conflicts: [] }; } } /** * Get enhanced asset timeline using existing TimelineService */ async getAssetTimeline(dfid: string): Promise<{ events: Array<{ timestamp: Date; event_type: string; description: string; source: string; metadata: Record<string, any>; }>; total_events: number; }> { try { const response = await this.makeRequest(`/api/services/asset-timeline/${dfid}`, { method: 'GET' }); return response.result || { events: [], total_events: 0 }; } catch (error) { console.warn('Asset timeline service unavailable:', error); return { events: [], total_events: 0 }; } } /** * Map SDK Asset to legacy DeFarm format for compatibility */ private mapAssetToLegacyFormat(asset: Asset): Record<string, any> { if (asset.category === 'livestock') { return { animal: { identifications: { internal_id: asset.data.ear_tag || asset.data.internal_id, ear_tag: asset.data.ear_tag, rfid: asset.data.rfid, sisbov: asset.data.sisbov }, breed: asset.data.breed, birth_date: asset.data.birth_date, weight_kg: asset.data.weight_kg, age_months: asset.data.age_months, health_status: asset.data.health_status, vaccinations: asset.data.vaccinations }, ownership: { owner_name: asset.ownership.ownerName, brazil: { property_name: asset.ownership.location?.municipality } }, location: asset.ownership.location }; } else if (asset.category === 'crops') { return { crop: { lot_number: asset.data.lot_number, field_id: asset.data.field_id, internal_id: asset.data.internal_id, variety: asset.data.variety, planting_date: asset.data.planting_date, harvest_date: asset.data.harvest_date, area_hectares: asset.data.area_hectares, yield_kg_per_hectare: asset.data.yield_kg_per_hectare }, farm: { owner_name: asset.ownership.ownerName, farm_name: asset.ownership.location?.municipality }, location: asset.ownership.location }; } // Default mapping for other categories return { asset_data: asset.data, ownership: asset.ownership, location: asset.ownership.location, category: asset.category, subcategory: asset.subcategory }; } /** * Map category/subcategory to valuechain type */ private mapCategoryToValuechain(category: string, subcategory: string): string { const mapping: Record<string, Record<string, string>> = { livestock: { cattle: 'bovinos', pigs: 'suinos', poultry: 'aves', sheep: 'ovinos' }, crops: { soybeans: 'soja', corn: 'milho', coffee: 'cafe', cotton: 'algodao', rice: 'arroz', wheat: 'trigo' } }; return mapping[category]?.[subcategory] || `${category}_${subcategory}`; } /** * Make HTTP request to DeFarm services */ private async makeRequest(endpoint: string, options: RequestInit = {}): Promise<any> { let lastError: Error | null = null; for (let attempt = 0; attempt <= (this.config.retries || 3); attempt++) { try { const url = `${this.config.baseUrl}${endpoint}`; const requestOptions: RequestInit = { ...options, headers: { 'Authorization': `Bearer ${this.config.apiKey}`, 'User-Agent': 'DeFarm-SDK/0.1.0', ...options.headers }, signal: AbortSignal.timeout(this.config.timeout || 30000) }; const response = await fetch(url, requestOptions); if (!response.ok) { if (response.status === 429 && attempt < (this.config.retries || 3)) { const retryAfter = response.headers.get('Retry-After'); const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, attempt) * 1000; await this.delay(waitTime); continue; } const errorData = await response.json().catch(() => ({})); throw new Error( `DeFarm services request failed: ${response.status} ${response.statusText}. ${ errorData.message || errorData.error || '' }` ); } return await response.json(); } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); if (error instanceof Error && error.name === 'AbortError') { lastError = new Error(`DeFarm services timeout after ${this.config.timeout}ms`); } if (attempt < (this.config.retries || 3)) { await this.delay(Math.pow(2, attempt) * 1000); continue; } } } throw lastError || new Error('Unknown DeFarm services error'); } private delay(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } }