UNPKG

@visionfi/server-sdk

Version:

Server-side SDK for VisionFI API access using Google Service Account authentication

1,103 lines (1,102 loc) 42.5 kB
import axios from 'axios'; import { GoogleAuth } from 'google-auth-library'; import * as fs from 'fs'; import * as path from 'path'; import { VisionFiError } from './types/index.js'; import { DocuSignIntegration } from './integrations/docusign/index.js'; /** * Main client for interacting with the VisionFi platform API. * Handles authentication, document analysis, and results retrieval. * * @example * ```typescript * // With explicit service account credentials * const client = new VisionFi({ * serviceAccountPath: './service-account.json' * }); * * // Using Application Default Credentials (ADC) in GCP environments * const client = new VisionFi(); // Automatically uses ADC * * // Verify authentication * const authResult = await client.verifyAuth(); * * // Analyze a document * const job = await client.analyzeDocument(fileBuffer, { * fileName: 'document.pdf', * analysisType: 'auto_loan_abstract' * }); * * // Get results * const results = await client.getResults(job.uuid); * ``` */ export class VisionFi { /** Base URL for the VisionFi API */ apiBaseUrl; /** Google Auth instance for authentication */ auth; /** Axios instance for making HTTP requests */ apiClient; /** DocuSign integration instance */ docuSignIntegration; /** * Utility method to extract Authorization header from various header object types * @param headers - Headers object (could be Map, Headers, or plain object) * @returns Authorization header value or null if not found */ getAuthorizationHeader(headers) { // Handle null/undefined if (!headers) { return null; } // Handle Headers object or Map with .get() method if (typeof headers.get === 'function') { return headers.get('Authorization'); } // Handle plain JavaScript object if (typeof headers === 'object') { return headers['Authorization'] || headers.authorization || null; } return null; } constructor(config = {}) { this.apiBaseUrl = config.apiBaseUrl || 'https://platform.visionfi.ai/api/v1'; if (config.serviceAccountJson) { this.auth = new GoogleAuth({ credentials: config.serviceAccountJson, // No scopes needed for ID tokens }); } else if (config.serviceAccountPath) { const serviceAccountPath = path.resolve(config.serviceAccountPath); try { const serviceAccountJson = JSON.parse(fs.readFileSync(serviceAccountPath, 'utf8')); this.auth = new GoogleAuth({ credentials: serviceAccountJson, // No scopes needed for ID tokens }); } catch (error) { throw new VisionFiError(`Unable to read service account file: ${error.message}`); } } else { // Use Application Default Credentials (ADC) when no explicit credentials are provided // This enables the SDK to work seamlessly in GCP environments (Cloud Run, GKE, App Engine, etc.) // where service account credentials are automatically provided by the environment this.auth = new GoogleAuth({ // ADC will automatically find credentials in this order: // 1. GOOGLE_APPLICATION_CREDENTIALS environment variable // 2. gcloud credentials (for local development) // 3. Metadata server (for GCP services) }); } this.apiClient = axios.create({ baseURL: this.apiBaseUrl, headers: { 'Accept': 'application/json' } }); // Add authentication to all requests this.apiClient.interceptors.request.use(async (config) => { const client = await this.auth.getIdTokenClient("platform.visionfi.ai"); const headers = await client.getRequestHeaders(); config.headers = config.headers || {}; const authHeader = this.getAuthorizationHeader(headers); if (authHeader) { config.headers['Authorization'] = authHeader; } return config; }); // Add global error handling this.apiClient.interceptors.response.use((response) => response, (error) => { const { response } = error; let message = 'An unknown error occurred'; let code = 'unknown_error'; let statusCode = 500; if (response) { statusCode = response.status; message = response.data?.message || `HTTP Error: ${response.status} ${response.statusText}`; code = response.data?.code || `http_${response.status}`; } else if (error.request) { message = 'No response received from server'; code = 'network_error'; } else { message = error.message; code = 'request_setup_error'; } throw new VisionFiError(message, statusCode, code); }); // Initialize DocuSign integration this.docuSignIntegration = new DocuSignIntegration(this.apiClient); } /** * Verifies authentication with the VisionFi API */ async verifyAuth() { try { const response = await this.apiClient.post('/auth/verify'); return response.data; } catch (error) { throw this.handleError(error, 'Authentication verification failed'); } } /** * Retrieves the JWT authentication token to use with the VisionFi API * @returns A promise that resolves to an object containing the token * @example * ```typescript * const { token } = await client.getAuthToken(); * console.log('JWT Token:', token); * * // Can be used in other API clients or tools like Postman * // Example: Authorization: Bearer [token] * ``` */ async getAuthToken() { try { const client = await this.auth.getIdTokenClient("platform.visionfi.ai"); const headers = await client.getRequestHeaders(); const authHeader = this.getAuthorizationHeader(headers); if (!authHeader) { throw new VisionFiError('No Authorization header found in authentication response'); } // Extract token from 'Bearer TOKEN' format const token = authHeader.replace('Bearer ', ''); return { token }; } catch (error) { throw this.handleError(error, 'Failed to generate authentication token'); } } /** * Retrieves client information from the VisionFi API * @returns A promise that resolves to client information and configuration * @example * ```typescript * const clientInfo = await client.getClientInfo(); * * if (clientInfo.success) { * console.log('Client name:', clientInfo.data.name); * * // Check for specific features * if (clientInfo.data.features?.includes('document-analysis')) { * // Enable document analysis features * } * } else { * console.error('Failed to get client info:', clientInfo.message); * } * ``` */ async getClientInfo() { try { const response = await this.apiClient.get('/operations/getClientInfo'); return response.data; } catch (error) { throw this.handleError(error, 'Failed to retrieve client information'); } } /** * Retrieves available document analysis workflows from the VisionFi API * @returns A promise that resolves to the list of available workflows * @example * ```typescript * const workflowsResponse = await client.getWorkflows(); * * if (workflowsResponse.success) { * console.log('Available workflows:'); * workflowsResponse.data?.forEach(workflow => { * console.log(`- ${workflow.workflow_key}: ${workflow.description}`); * }); * } else { * console.error('Failed to get workflows:', workflowsResponse.message); * } * ``` */ async getWorkflows() { try { const response = await this.apiClient.get('/operations/getAvailableWorkflows'); return response.data; } catch (error) { throw this.handleError(error, 'Failed to retrieve available workflows'); } } /** * Uploads and analyzes a document * @param fileBuffer The file buffer to upload * @param options Options for document analysis */ async analyzeDocument(fileBuffer, options) { try { // Convert file buffer to Base64 const fileBase64 = fileBuffer.toString('base64'); const requestData = { fileName: options.fileName, fileBase64: fileBase64, analysisType: options.analysisType }; const response = await this.apiClient.post('/operations/analyze', requestData); return response.data; } catch (error) { throw this.handleError(error, 'Document analysis request failed'); } } /** * Retrieves the results of an analysis job * @param jobUuid The UUID of the analysis job * @param pollInterval Optional interval in ms to poll for results until completed * @param maxAttempts Optional maximum number of polling attempts * @param earlyCancelAttempts Optional number of attempts before emitting a "job likely invalid" warning */ async getResults(jobUuid, pollInterval, maxAttempts, earlyCancelAttempts) { if (!pollInterval) { // Just fetch once without polling try { const response = await this.apiClient.get(`/operations/getAnalysisResults?uuid=${jobUuid}`); return response.data; } catch (error) { throw this.handleError(error, 'Failed to retrieve analysis results'); } } // Poll until completion const interval = pollInterval || 3000; // Default 3 seconds const attempts = maxAttempts || 20; // Default 20 attempts (roughly 1 minute) const earlyWarningAttempts = earlyCancelAttempts || Math.floor(attempts / 4); // Default 25% of max attempts return new Promise((resolve, reject) => { let attemptCount = 0; let earlyWarningEmitted = false; const poll = async () => { attemptCount++; try { const response = await this.apiClient.get(`/operations/getAnalysisResults?uuid=${jobUuid}`); const result = response.data; // Check if results are found if (result.found === false) { // Results not found yet - continue polling if (attemptCount >= earlyWarningAttempts && !earlyWarningEmitted) { // After several attempts with no results, emit a warning through console // This helps identify potentially invalid UUIDs earlier console.warn(`Warning: No results found after ${attemptCount} attempts for UUID ${jobUuid}. This may indicate an invalid UUID or a long-running job.`); earlyWarningEmitted = true; } if (attemptCount >= attempts) { reject(new VisionFiError(`Maximum polling attempts (${attempts}) reached. The job may still be processing.`, 408, 'polling_timeout')); return; } setTimeout(poll, interval); return; } // Results found! Either processed or error resolve(result); } catch (error) { reject(this.handleError(error, 'Error while polling for results')); } }; poll(); }); } handleError(error, defaultMessage) { if (error instanceof VisionFiError) { return error; } const message = error.message || defaultMessage; const statusCode = error.response?.status; const code = error.response?.data?.code || 'unknown_error'; return new VisionFiError(message, statusCode, code); } // DocuSign Configuration Management Methods /** * Retrieves the current DocuSign integration configuration. * * @returns A promise that resolves to the DocuSign configuration response * * @example * ```typescript * const configResponse = await client.getDocuSignConfig(); * * if (configResponse.success) { * console.log('DocuSign configuration:', configResponse.data); * } else { * console.error('Failed to get DocuSign config:', configResponse.message); * } * ``` */ async getDocuSignConfig() { return this.docuSignIntegration.config.getConfig(); } /** * Updates or creates the DocuSign integration configuration. * * @param config - The DocuSign configuration to update or create * @returns A promise that resolves to the DocuSign configuration response * * @example * ```typescript * const configResponse = await client.updateDocuSignConfig({ * userId: '12345678-1234-1234-1234-123456789012', * accountId: '98765432-9876-9876-9876-987654321098', * baseUri: 'https://demo.docusign.net/restapi', * name: 'Production DocuSign Integration', * enabled: true * }); * * if (configResponse.success) { * console.log('DocuSign configuration updated:', configResponse.data); * } else { * console.error('Failed to update DocuSign config:', configResponse.message); * } * ``` */ async updateDocuSignConfig(config) { return this.docuSignIntegration.config.updateConfig(config); } /** * Updates only the authentication status of a DocuSign integration. * * @param statusUpdate - The authentication status update data * @returns A promise that resolves to the DocuSign authentication status response * * @example * ```typescript * const statusResponse = await client.updateDocuSignAuthStatus({ * authenticationStatus: 'authenticated', * lastAuthenticated: new Date().toISOString() * }); * * if (statusResponse.success) { * console.log('DocuSign auth status updated:', statusResponse.data); * } else { * console.error('Failed to update auth status:', statusResponse.message); * } * ``` */ async updateDocuSignAuthStatus(statusUpdate) { return this.docuSignIntegration.config.updateAuthStatus(statusUpdate); } // DocuSign Connect Webhook Management Methods /** * Retrieves all webhook configurations for the client's DocuSign integration. * * @returns A promise that resolves to the list of webhook configurations * * @example * ```typescript * const webhooksResponse = await client.getDocuSignConnectWebhooks(); * * if (webhooksResponse.success) { * console.log('DocuSign webhooks:', webhooksResponse.data); * } else { * console.error('Failed to get webhooks:', webhooksResponse.message); * } * ``` */ async getDocuSignConnectWebhooks() { return this.docuSignIntegration.connect.getWebhooks(); } /** * Retrieves a specific webhook configuration by its GUID. * * @param webhookGuid - The unique identifier for the webhook configuration * @returns A promise that resolves to the specific webhook configuration * * @example * ```typescript * const webhookGuid = 'd0381ee4-5b83-4d6b-9f37-177c2a6a79f2'; * const webhookResponse = await client.getDocuSignConnectWebhook(webhookGuid); * * if (webhookResponse.success) { * console.log('DocuSign webhook:', webhookResponse.data); * } else { * console.error('Failed to get webhook:', webhookResponse.message); * } * ``` */ async getDocuSignConnectWebhook(webhookGuid) { return this.docuSignIntegration.connect.getWebhook(webhookGuid); } /** * Creates a new DocuSign Connect webhook configuration. * * @param webhook - The webhook configuration to create * @returns A promise that resolves to the created webhook configuration * * @example * ```typescript * const newWebhook = { * webhookGuid: 'd0381ee4-5b83-4d6b-9f37-177c2a6a79f2', * hmacSecret: '89c3444a-f4c2-4fda-98b8-59dc2fe3ea29', * workflowId: 'electronic-signatures-workflow' * }; * * const webhookResponse = await client.addDocuSignConnectWebhook(newWebhook); * * if (webhookResponse.success) { * console.log('DocuSign webhook created:', webhookResponse.data); * } else { * console.error('Failed to create webhook:', webhookResponse.message); * } * ``` */ async addDocuSignConnectWebhook(webhook) { return this.docuSignIntegration.connect.addWebhook(webhook); } /** * Updates an existing DocuSign Connect webhook configuration. * * @param webhookGuid - The unique identifier for the webhook configuration * @param webhook - The partial webhook configuration to update * @returns A promise that resolves to the updated webhook configuration * * @example * ```typescript * const webhookGuid = 'd0381ee4-5b83-4d6b-9f37-177c2a6a79f2'; * const webhookUpdate = { * hmacSecret: 'updated-hmac-secret-value', * workflowId: 'updated-workflow-id' * }; * * const webhookResponse = await client.updateDocuSignConnectWebhook(webhookGuid, webhookUpdate); * * if (webhookResponse.success) { * console.log('DocuSign webhook updated:', webhookResponse.data); * } else { * console.error('Failed to update webhook:', webhookResponse.message); * } * ``` */ async updateDocuSignConnectWebhook(webhookGuid, webhook) { return this.docuSignIntegration.connect.updateWebhook(webhookGuid, webhook); } /** * Deletes a DocuSign Connect webhook configuration. * * @param webhookGuid - The unique identifier for the webhook configuration * @returns A promise that resolves to the deletion response * * @example * ```typescript * const webhookGuid = 'd0381ee4-5b83-4d6b-9f37-177c2a6a79f2'; * const deleteResponse = await client.deleteDocuSignConnectWebhook(webhookGuid); * * if (deleteResponse.success) { * console.log('DocuSign webhook deleted:', deleteResponse.message); * } else { * console.error('Failed to delete webhook:', deleteResponse.message); * } * ``` */ async deleteDocuSignConnectWebhook(webhookGuid) { return this.docuSignIntegration.connect.deleteWebhook(webhookGuid); } /** * Gets a DocuSign OAuth consent URL for tenant authorization * @param environment Which DocuSign environment to use ('prod' or 'dev') * @returns A promise that resolves to the consent URL * * @example * ```typescript * // Generate a consent URL for the production DocuSign environment * try { * const consentUrl = await client.getDocuSignConsentUrl('prod'); * console.log('DocuSign consent URL:', consentUrl); * * // Provide this URL to the user to authorize DocuSign integration * } catch (error) { * console.error('Error generating consent URL:', error); * } * ``` */ async getDocuSignConsentUrl(environment) { if (environment !== 'prod' && environment !== 'dev') { throw new Error('Invalid environment parameter. Must be either "prod" or "dev"'); } try { const response = await this.apiClient.get(`/admin/docusign/consent-url?environment=${environment}`); if (response.data.success && response.data.data && response.data.data.consentUrl) { return response.data.data.consentUrl; } else { throw new VisionFiError(response.data.message || 'Failed to generate DocuSign consent URL'); } } catch (error) { throw this.handleError(error, 'Failed to generate DocuSign consent URL'); } } /** * Builds the complete webhook URL for a specific DocuSign Connect webhook * * @param webhookGuid - The unique identifier for the webhook configuration * @returns A promise that resolves to the complete webhook URL that can be used in DocuSign Connect configuration * * @example * ```typescript * // Get the complete webhook URL for a specific webhook * try { * const webhookGuid = 'd0381ee4-5b83-4d6b-9f37-177c2a6a79f2'; * const webhookUrl = await client.getDocuSignWebhookUrl(webhookGuid); * * console.log('DocuSign Connect webhook URL:', webhookUrl); * // Example: https://platform.visionfi.ai/api/v1/webhooks/tenant123/docusign/d0381ee4-5b83-4d6b-9f37-177c2a6a79f2 * * // Use this URL in your DocuSign Connect configuration * } catch (error) { * console.error('Error generating webhook URL:', error.message); * } * ``` */ async getDocuSignWebhookUrl(webhookGuid) { if (!webhookGuid) { throw new Error('Webhook GUID is required'); } try { // First, verify the webhook exists await this.getDocuSignConnectWebhook(webhookGuid); // Get the client info to retrieve the tenant key const clientInfoResponse = await this.getClientInfo(); if (!clientInfoResponse.success || !clientInfoResponse.data?.tenantKey) { throw new VisionFiError('Failed to retrieve tenant key from client information'); } const tenantKey = clientInfoResponse.data.tenantKey; // Construct the webhook URL // Use the apiBaseUrl without the /api/v1 part since it will be included in the path const baseUrl = this.apiBaseUrl; // Construct the complete webhook URL return `${baseUrl}/webhooks/${tenantKey}/docusign/${webhookGuid}`; } catch (error) { throw this.handleError(error, 'Failed to generate DocuSign webhook URL'); } } // Package Management Methods /** * Creates a new package for document processing. * * @param options - The package creation options * @returns A promise that resolves to the created package response * * @example * ```typescript * const packageResponse = await client.createPackage({ * description: 'Test vehicle loan application package', * productType: 'consumer_loan_vehicle_installment', * externalReferenceIds: ['APP-2024-001', 'LOCAL-ID-12345'] * }); * * console.log('Package created:', packageResponse.packageId); * ``` */ async createPackage(options) { try { const response = await this.apiClient.post('/operations/package', options); return response.data; } catch (error) { throw this.handleError(error, 'Failed to create package'); } } /** * Retrieves a list of packages with optional filtering. * * @param options - Optional filtering options * @returns A promise that resolves to the list of packages * * @example * ```typescript * // List all packages * const allPackages = await client.listPackages(); * * // List packages with filters * const filteredPackages = await client.listPackages({ * externalReferences: 'APP-2024-001,LOCAL-ID-12345', * productTypes: 'consumer_loan_vehicle_installment', * createdAfter: '2024-01-15T10:30:00Z' * }); * * if (filteredPackages.success) { * console.log('Found packages:', filteredPackages.data?.length); * } * ``` */ async listPackages(options) { try { const params = new URLSearchParams(); // Add new concise filtering parameters if (options?.tags) params.append('tags', options.tags); if (options?.ref1) params.append('ref1', options.ref1); if (options?.ref2) params.append('ref2', options.ref2); if (options?.ref3) params.append('ref3', options.ref3); if (options?.ref4) params.append('ref4', options.ref4); if (options?.ref5) params.append('ref5', options.ref5); if (options?.category) params.append('category', options.category); if (options?.owner) params.append('owner', options.owner); // Support legacy parameter for backward compatibility if (options?.externalReferences) params.append('externalReferences', options.externalReferences); // Existing parameters if (options?.productTypes) params.append('productTypes', options.productTypes); if (options?.createdAfter) params.append('createdAfter', options.createdAfter); const queryString = params.toString(); const url = queryString ? `/operations/packages?${queryString}` : '/operations/packages'; const response = await this.apiClient.get(url); return response.data; } catch (error) { throw this.handleError(error, 'Failed to list packages'); } } /** * Retrieves a specific package by its ID. * * @param packageId - The unique identifier for the package * @returns A promise that resolves to the package information * * @example * ```typescript * const packageInfo = await client.getPackage('package-uuid-here'); * * console.log('Package status:', packageInfo.status); * console.log('Total files:', packageInfo.totalFiles); * console.log('External references:', packageInfo.externalReferenceIds); * ``` */ async getPackage(packageId) { try { const response = await this.apiClient.get(`/operations/packages/${packageId}`); return response.data; } catch (error) { throw this.handleError(error, 'Failed to get package'); } } /** * Gets audit history for a specific package. * * @param packageId - The unique identifier for the package * @param limit - Optional number of audit entries to return (default: 50, max: 200) * @returns A promise that resolves to the package audit history * * @example * ```typescript * const auditHistory = await client.getPackageAuditHistory('package-uuid-here'); * * console.log('Package audit history:', auditHistory.auditHistory); * console.log('Total entries:', auditHistory.total); * * // With custom limit * const limitedHistory = await client.getPackageAuditHistory('package-uuid-here', 100); * ``` */ async getPackageAuditHistory(packageId, limit) { try { const params = limit ? { limit } : {}; const response = await this.apiClient.get(`/operations/packages/${packageId}/audit`, { params }); return response.data; } catch (error) { throw this.handleError(error, 'Failed to get package audit history'); } } // Document Management Methods /** * Adds one or more documents to an existing package. * * @param packageId - The unique identifier for the package * @param options - The documents to add * @returns A promise that resolves to the add documents response * * @example * ```typescript * // Add single document * const fileBuffer = fs.readFileSync('./document.pdf'); * const addResponse = await client.addDocumentsToPackage('package-uuid', { * files: [{ * fileName: 'drivers_license.pdf', * fileBase64: fileBuffer.toString('base64') * }] * }); * * console.log('Files added:', addResponse.filesAdded); * console.log('Total files:', addResponse.totalFiles); * ``` */ async addDocumentsToPackage(packageId, options) { try { const response = await this.apiClient.put(`/operations/packages/${packageId}/document`, options); return response.data; } catch (error) { throw this.handleError(error, 'Failed to add documents to package'); } } /** * Convenience method to add a single file buffer to a package. * * @param packageId - The unique identifier for the package * @param fileBuffer - The file buffer to add * @param fileName - The name of the file * @returns A promise that resolves to the add documents response * * @example * ```typescript * const fileBuffer = fs.readFileSync('./document.pdf'); * const addResponse = await client.addFileToPackage( * 'package-uuid', * fileBuffer, * 'document.pdf' * ); * * console.log('Files added:', addResponse.filesAdded); * console.log('Total files:', addResponse.totalFiles); * ``` */ async addFileToPackage(packageId, fileBuffer, fileName) { const fileBase64 = fileBuffer.toString('base64'); return this.addDocumentsToPackage(packageId, { files: [{ fileName, fileBase64 }] }); } /** * Soft deletes a document from a package. * * @param packageId - The unique identifier for the package * @param documentUuid - The unique identifier for the document * @returns A promise that resolves to the delete response * * @example * ```typescript * const deleteResponse = await client.deleteDocumentFromPackage( * 'package-uuid', * 'document-uuid' * ); * * if (deleteResponse.success) { * console.log('Document deleted:', deleteResponse.message); * } * ``` */ async deleteDocumentFromPackage(packageId, documentUuid) { try { const response = await this.apiClient.delete(`/operations/packages/${packageId}/document/${documentUuid}`); return response.data; } catch (error) { throw this.handleError(error, 'Failed to delete document from package'); } } // External References Management Methods /** * Adds external reference IDs to a package. * * @param packageId - The unique identifier for the package * @param options - The reference IDs to add * @returns A promise that resolves to the add references response * * @example * ```typescript * const addRefsResponse = await client.addExternalReferences('package-uuid', { * referenceIds: ['NEW-REF-001', 'UPDATED-ID-456'] * }); * * if (addRefsResponse.success) { * console.log('References added:', addRefsResponse.message); * } * ``` */ async addExternalReferences(packageId, options) { try { const response = await this.apiClient.post(`/operations/packages/${packageId}/references`, options); return response.data; } catch (error) { throw this.handleError(error, 'Failed to add external references'); } } /** * Removes external reference IDs from a package. * * @param packageId - The unique identifier for the package * @param options - The reference IDs to remove * @returns A promise that resolves to the remove references response * * @example * ```typescript * const removeRefsResponse = await client.removeExternalReferences('package-uuid', { * referenceIds: ['LOCAL-ID-12345'] * }); * * if (removeRefsResponse.success) { * console.log('References removed:', removeRefsResponse.message); * } * ``` */ async removeExternalReferences(packageId, options) { try { const response = await this.apiClient.delete(`/operations/packages/${packageId}/references`, { data: options }); return response.data; } catch (error) { throw this.handleError(error, 'Failed to remove external references'); } } // Processing Methods /** * Executes a specific prompt configuration on package documents. * * @param packageId - The unique identifier for the package * @param options - The processing execution options * @returns A promise that resolves to the execute processing response * * @example * ```typescript * const processingResponse = await client.executeProcessing('package-uuid', { * configId: 'vehicle-info-extraction', * documentUuids: [] // Optional: process specific documents only * }); * * console.log('Processing ID:', processingResponse.processingId); * console.log('Status:', processingResponse.status); // Always 'queued' initially * ``` */ async executeProcessing(packageId, options) { try { const requestBody = { configId: options.configId, documentUuids: options.documentUuids || [] }; const response = await this.apiClient.post(`/operations/packages/${packageId}/processing/execute`, requestBody); return response.data; } catch (error) { throw this.handleError(error, 'Failed to execute processing'); } } /** * Retrieves all processing history for a package. * * @param packageId - The unique identifier for the package * @param options - Optional parameters for the request * @returns A promise that resolves to the processing history * * @example * ```typescript * const history = await client.getProcessingHistory('package-uuid-here'); * * if (history.success && history.data) { * console.log('Total processing attempts:', history.data.total); * history.data.processingHistory.forEach(entry => { * console.log(`Processing ${entry.processingId}: ${entry.status}`); * }); * } * * // Get history with results included * const historyWithResults = await client.getProcessingHistory('package-uuid-here', { * includeResults: true * }); * ``` */ async getProcessingHistory(packageId, options) { try { const params = options?.includeResults ? { includeResults: 'true' } : undefined; const response = await this.apiClient.get(`/operations/packages/${packageId}/processing`, { params }); return response.data; } catch (error) { throw this.handleError(error, 'Failed to get processing history'); } } /** * Retrieves all processing history with results for a package. * This is a convenience method that automatically includes processing results. * * @param packageId - The unique identifier for the package * @returns A promise that resolves to the processing history with results * * @example * ```typescript * const results = await client.getProcessingWithResults('package-uuid-here'); * * if (results.success && results.data) { * results.data.processingHistory.forEach(entry => { * if (entry.status === 'completed' && entry.result) { * console.log('Processing result:', entry.result.data); * if (entry.result.confidence) { * console.log('Confidence:', entry.result.confidence); * } * } * }); * } * ``` */ async getProcessingWithResults(packageId) { return this.getProcessingHistory(packageId, { includeResults: true }); } /** * Retrieves the status and results of a prompt processing request. * * @param packageId - The unique identifier for the package * @param processingId - The unique identifier for the processing request * @param pollInterval - Optional interval in ms to poll for results until completed * @param maxAttempts - Optional maximum number of polling attempts * @returns A promise that resolves to the processing result * * @example * ```typescript * // Get processing result once * const result = await client.getProcessingResult('package-uuid', 'processing-uuid'); * * // Poll for completion (check every 3 seconds, max 20 attempts) * const result = await client.getProcessingResult( * 'package-uuid', * 'processing-uuid', * 3000, * 20 * ); * * if (result.success && result.data?.status === 'completed') { * console.log('Processing results:', result.data.result); * } * ``` */ async getProcessingResult(packageId, processingId, pollInterval, maxAttempts) { if (!pollInterval) { // Just fetch once without polling try { const response = await this.apiClient.get(`/operations/packages/${packageId}/processing/${processingId}`); return response.data; } catch (error) { throw this.handleError(error, 'Failed to get processing result'); } } // Poll until completion const interval = pollInterval || 3000; // Default 3 seconds const attempts = maxAttempts || 20; // Default 20 attempts (roughly 1 minute) return new Promise((resolve, reject) => { let attemptCount = 0; const poll = async () => { attemptCount++; try { const response = await this.apiClient.get(`/operations/packages/${packageId}/processing/results/${processingId}`); const result = response.data; // Check if processing is complete if (result.success && result.data) { const status = result.data.status; if (status === 'completed' || status === 'failed') { resolve(result); return; } } // Continue polling if not complete if (attemptCount >= attempts) { reject(new VisionFiError(`Maximum polling attempts (${attempts}) reached. Processing may still be in progress.`, 408, 'polling_timeout')); return; } setTimeout(poll, interval); } catch (error) { reject(this.handleError(error, 'Error while polling for processing results')); } }; poll(); }); } // Product Management Methods /** * Retrieves available product types. * * @returns A promise that resolves to the product types response * * @example * ```typescript * const productTypesResponse = await client.getProductTypes(); * * if (productTypesResponse.success) { * console.log('Available product types:'); * productTypesResponse.data?.forEach(productType => { * console.log(`- ${productType.id}: ${productType.name}`); * }); * } * ``` */ async getProductTypes() { try { const response = await this.apiClient.get('/admin/products/types'); return response.data; } catch (error) { throw this.handleError(error, 'Failed to get product types'); } } /** * Retrieves a rendered HTML view for a completed processing result. * * Prerequisites: * - Processing status must be 'completed' * - `hasViews` must be true in the processing result * - `configCollectionId` and `configVersionId` must be present * - Processing result must contain artifacts with data * * @param packageId - The unique identifier for the package * @param processingId - The unique identifier for the processing request * @param options - Optional parameters including viewName * @returns A promise that resolves to the processing view response with base64-encoded HTML * * @example * ```typescript * // Assuming you've already checked that hasViews is true from getProcessingResult * // Get the default view * const viewResponse = await client.getProcessingView( * 'package-uuid', * 'processing-uuid' * ); * * if (viewResponse.success && viewResponse.view) { * // Decode the base64 HTML * const html = Buffer.from(viewResponse.view, 'base64').toString('utf-8'); * * // Save to file * fs.writeFileSync('result.html', html); * * // Or use in your application * console.log('HTML content retrieved successfully'); * } * * // Use a specific template * const viewResponse = await client.getProcessingView( * 'package-uuid', * 'processing-uuid', * { viewName: 'custom-template' } * ); * ``` */ async getProcessingView(packageId, processingId, options) { try { const params = options?.viewName ? { viewName: options.viewName } : undefined; const response = await this.apiClient.get(`/operations/packages/${packageId}/processing/${processingId}/view`, { params }); return response.data; } catch (error) { throw this.handleError(error, 'Failed to get processing view'); } } }