UNPKG

@ritas-inc/hanaqueryapi-client

Version:

TypeScript client for HANA Query API with full type safety and error handling

747 lines 29.1 kB
/** * HANA Query API Client - Main Client Class * * Complete TypeScript client for the HANA Query API with full type safety, * error handling, retry logic, and logging. */ import type { ClientConfig, RequestOptions, SalesParams, HealthData, HealthMetadata, DocsData, DocsMetadata, ItemsParams, ItemsStatusData, ItemsStatusMetadata, ItemGroupsData, ItemGroupsMetadata, SalesData, SalesMetadata, HierarchiesData, HierarchiesMetadata, TreesData, TreesMetadata, QtyPerTagData, QtyPerTagMetadata, ProductionSectorsData, ProductionSectorsMetadata, DatabaseCompaniesData, DatabaseCompaniesMetadata, TagData, TagMetadata, WorkOrderTagsData, WorkOrderTagsMetadata, InjectionMachinesData, InjectionMachinesMetadata, SingleInjectionMachineData, SingleInjectionMachineMetadata, MoldsData, MoldsMetadata, SingleMoldData, SingleMoldMetadata, BusinessPartnersData, BusinessPartnersMetadata, BusinessPartnersSearchMetadata, ContactsData, ContactsMetadata, ContactsSearchMetadata, BusinessPartnerContactsMetadata, SearchCriteria, PlansData, PlansMetadata, SinglePlanData, SinglePlanMetadata, PlanProductsData, PlanProductsMetadata, PlanWorkOrdersData, PlanWorkOrdersMetadata, PlanTagsData, PlanTagsMetadata, PlanSectorsSummaryData, PlanSectorsSummaryMetadata, AllPlansSectorsSummaryData, AllPlansSectorsSummaryMetadata, UserData, UserMetadata, APIResponse } from './types.ts'; /** * Main HANA Query API Client */ export declare class HanaQueryClient { private readonly config; constructor(config: ClientConfig, customConfig?: Partial<Omit<ClientConfig, 'baseUrl'>>); /** * Get API health status with system information * * @param options - Optional request configuration * @returns Promise resolving to health data and system metadata * * @example * ```typescript * const { data, metadata } = await client.getHealth(); * console.log(`API Status: ${data.status}`); * console.log(`Uptime: ${Math.round(data.uptime / 60)} minutes`); * console.log(`Node Version: ${metadata.nodeVersion}`); * console.log(`Platform: ${metadata.platform}`); * ``` * * @returns {Object} result - Health status result * @returns {Object} result.data - Health status data * @returns {string} result.data.status - Always 'ok' when API is running * @returns {string} result.data.timestamp - Current timestamp in ISO 8601 format * @returns {number} result.data.uptime - Server uptime in seconds * @returns {Object} result.metadata - System information metadata * @returns {boolean} result.metadata.systemInfo - Indicates system info is included * @returns {string} result.metadata.serverTime - Server current time * @returns {string} result.metadata.nodeVersion - Node.js version * @returns {string} result.metadata.platform - Operating system platform * @returns {string} result.metadata.arch - System architecture */ getHealth(options?: RequestOptions): Promise<{ data: HealthData; metadata: HealthMetadata; }>; /** * Get API documentation with endpoint information * * @param options - Optional request configuration * @returns Promise resolving to API documentation and metadata * * @example * ```typescript * const { data, metadata } = await client.getDocs(); * console.log(`API: ${data.name} v${data.version}`); * console.log(`Available endpoints: ${metadata.endpointCount}`); * console.log('Endpoints:', Object.keys(data.endpoints)); * ``` * * @returns {Object} result - API documentation result * @returns {Object} result.data - API documentation data * @returns {string} result.data.name - API name * @returns {string} result.data.version - API version * @returns {Object} result.data.endpoints - Map of endpoint paths to descriptions * @returns {Object} result.metadata - Documentation metadata * @returns {number} result.metadata.endpointCount - Total number of available endpoints * @returns {string} result.metadata.apiVersion - API version identifier * @returns {string} result.metadata.generatedAt - Documentation generation timestamp * @returns {boolean} result.metadata.documentation - Indicates this is documentation endpoint */ getDocs(options?: RequestOptions): Promise<{ data: DocsData; metadata: DocsMetadata; }>; /** * Get items with optional group filtering * * @param params - Optional filtering parameters with numeric group codes * @param options - Optional request configuration * @returns Promise resolving to items data and metadata * * @example * // Get all items * const allItems = await client.getItems(); * * // Get items for specific group codes (numeric) * const filteredItems = await client.getItems({ * groups: [101, 102, 103] * }); */ getItems(params?: ItemsParams, options?: RequestOptions): Promise<{ data: ItemsStatusData; metadata: ItemsStatusMetadata; }>; /** * Get all item groups * * Retrieves all item groups from the SAP HANA database with their codes and names. * Item groups provide categorization information for inventory items. * * @param {RequestOptions} [options] - Optional request configuration * @returns {Promise<{data: ItemGroupsData, metadata: ItemGroupsMetadata}>} Item groups with metadata * * @example * ```typescript * const response = await client.getItemGroups(); * console.log(`Found ${response.metadata.count} item groups`); * response.data.groups.forEach(group => { * console.log(`${group.code}: ${group.name}`); * }); * ``` */ getItemGroups(options?: RequestOptions): Promise<{ data: ItemGroupsData; metadata: ItemGroupsMetadata; }>; /** * Get sales data within a date range */ getSales(params: SalesParams, options?: RequestOptions): Promise<{ data: SalesData; metadata: SalesMetadata; }>; /** * Get all item hierarchies */ getItemHierarchies(options?: RequestOptions): Promise<{ data: HierarchiesData; metadata: HierarchiesMetadata; }>; /** * Get raw tree relationships (parent-child-quantity) */ getItemTrees(options?: RequestOptions): Promise<{ data: TreesData; metadata: TreesMetadata; }>; /** * Get children of a specific parent item (filtered by groups 131, 144) * * @param itemCode - Parent item code to get children for * @param options - Optional request configuration * @returns Promise resolving to tree data and metadata * * @example * ```typescript * const { data, metadata } = await client.getItemTree('PARENT-001'); * console.log(`Found ${metadata.count} children for parent ${metadata.parent}`); * data.trees.forEach(tree => { * console.log(`Child: ${tree.itemcode}, Quantity: ${tree.quantity}`); * }); * ``` * * @returns {Object} result - Tree result * @returns {Object} result.data - Tree data * @returns {Array} result.data.trees - Array of tree relationships (parent-child-quantity) * @returns {Object} result.metadata - Metadata about the response * @returns {number} result.metadata.count - Total number of children returned * @returns {string} result.metadata.parent - Parent item code queried */ getItemTree(itemCode: string, options?: RequestOptions): Promise<{ data: TreesData; metadata: TreesMetadata; }>; /** * Get quantity per tag data for items with mold resources * * @param options - Optional request configuration * @returns Promise resolving to quantity per tag data and metadata * * @example * ```typescript * const { data, metadata } = await client.getQtyPerTag(); * console.log(`Found ${metadata.count} items with quantity per tag data`); * data.qtyPerTag.forEach(item => { * console.log(`${item.itemcode}: ${item.qtyPerTag} units per tag`); * }); * ``` * * @returns {Object} result - Quantity per tag result * @returns {Object} result.data - Quantity per tag data * @returns {Array} result.data.qtyPerTag - Array of items with their quantity per tag values * @returns {Object} result.metadata - Metadata about the response * @returns {number} result.metadata.count - Total number of items returned */ getQtyPerTag(options?: RequestOptions): Promise<{ data: QtyPerTagData; metadata: QtyPerTagMetadata; }>; /** * Get all production sectors */ getProductionSectors(options?: RequestOptions): Promise<{ data: ProductionSectorsData; metadata: ProductionSectorsMetadata; }>; /** * Get all database companies */ getDatabaseCompanies(options?: RequestOptions): Promise<{ data: DatabaseCompaniesData; metadata: DatabaseCompaniesMetadata; }>; /** * Get all production plans */ getPlans(options?: RequestOptions): Promise<{ data: PlansData; metadata: PlansMetadata; }>; /** * Get a specific production plan by ID */ getPlan(planId: number | string, options?: RequestOptions): Promise<{ data: SinglePlanData; metadata: SinglePlanMetadata; }>; /** * Get products for a specific production plan * * @param planId - The plan ID to get products for * @param options - Optional request configuration * @returns Promise resolving to plan products data and metadata * * @throws {NotFoundError} When the plan with the specified ID doesn't exist (HTTP 404) * @throws {ValidationError} When the planId parameter is invalid (HTTP 400) * @throws {AuthorizationError} When the request is not authorized (HTTP 401/403) * @throws {HanaQueryClientError} For other client or server errors * * @example * ```typescript * try { * const result = await client.getPlanProducts(123); * if (result.data.products.length === 0) { * console.log('Plan exists but has no products'); * } else { * console.log(`Found ${result.data.products.length} products`); * } * } catch (error) { * if (error instanceof NotFoundError) { * console.log('Plan does not exist'); * } else { * console.log('Other error:', error.message); * } * } * ``` * * @remarks * - Returns HTTP 200 with empty array when plan exists but has no products * - Returns HTTP 404 only when the plan itself doesn't exist * - Empty products array is a normal, successful response */ getPlanProducts(planId: number | string, options?: RequestOptions): Promise<{ data: PlanProductsData; metadata: PlanProductsMetadata; }>; /** * Get work orders for a specific production plan * * @param planId - The plan ID to get work orders for * @param options - Optional request configuration * @returns Promise resolving to plan work orders data and metadata * * @throws {NotFoundError} When the plan with the specified ID doesn't exist (HTTP 404) * @throws {ValidationError} When the planId parameter is invalid (HTTP 400) * @throws {AuthorizationError} When the request is not authorized (HTTP 401/403) * @throws {HanaQueryClientError} For other client or server errors * * @example * ```typescript * try { * const result = await client.getPlanWorkOrders(123); * if (result.data.workOrders.length === 0) { * console.log('Plan exists but has no work orders'); * } else { * console.log(`Found ${result.data.workOrders.length} work orders`); * } * } catch (error) { * if (error instanceof NotFoundError) { * console.log('Plan does not exist'); * } else { * console.log('Other error:', error.message); * } * } * ``` * * @remarks * - Returns HTTP 200 with empty array when plan exists but has no work orders * - Returns HTTP 404 only when the plan itself doesn't exist * - Empty work orders array is a normal, successful response */ getPlanWorkOrders(planId: number | string, options?: RequestOptions): Promise<{ data: PlanWorkOrdersData; metadata: PlanWorkOrdersMetadata; }>; /** * Get tags for a specific production plan * * @param planId - The plan ID to get tags for * @param options - Optional request configuration * @returns Promise resolving to plan tags data and metadata * * @throws {NotFoundError} When the plan with the specified ID doesn't exist (HTTP 404) * @throws {ValidationError} When the planId parameter is invalid (HTTP 400) * @throws {AuthorizationError} When the request is not authorized (HTTP 401/403) * @throws {HanaQueryClientError} For other client or server errors * * @example * ```typescript * try { * const result = await client.getPlanTags(123); * if (result.data.tags.length === 0) { * console.log('Plan exists but has no tags'); * } else { * console.log(`Found ${result.data.tags.length} tags`); * result.data.tags.forEach(tag => { * console.log(`Tag ${tag.tag_entry}: ${tag.tag_item_name} (WO ${tag.tag_workorder_num})`); * }); * } * } catch (error) { * if (error instanceof NotFoundError) { * console.log('Plan does not exist'); * } else { * console.log('Other error:', error.message); * } * } * ``` * * @remarks * - Returns HTTP 200 with empty array when plan exists but has no tags * - Returns HTTP 404 only when the plan itself doesn't exist * - Empty tags array is a normal, successful response */ getPlanTags(planId: number | string, options?: RequestOptions): Promise<{ data: PlanTagsData; metadata: PlanTagsMetadata; }>; /** * Get sector summaries for a specific production plan * * @param planId - The plan ID to get sector summaries for * @param options - Optional request configuration * @returns Promise resolving to plan sectors summary data and metadata * * @throws {NotFoundError} When the plan with the specified ID doesn't exist (HTTP 404) * @throws {ValidationError} When the planId parameter is invalid (HTTP 400) * @throws {AuthorizationError} When the request is not authorized (HTTP 401/403) * @throws {HanaQueryClientError} For other client or server errors * * @example * ```typescript * try { * const result = await client.getPlanSectorsSummary(123); * if (result.data.sectors.length === 0) { * console.log('Plan exists but has no sectors'); * } else { * console.log(`Found ${result.data.sectors.length} sectors`); * console.log(`Completion rate: ${result.metadata.completion_rate}%`); * } * } catch (error) { * if (error instanceof NotFoundError) { * console.log('Plan does not exist'); * } else { * console.log('Other error:', error.message); * } * } * ``` * * @remarks * - Returns HTTP 200 with empty array when plan exists but has no sectors * - Returns HTTP 404 only when the plan itself doesn't exist * - Empty sectors array is a normal, successful response * - Metadata includes aggregated statistics (total planned, completed, completion rate) */ getPlanSectorsSummary(planId: number | string, options?: RequestOptions): Promise<{ data: PlanSectorsSummaryData; metadata: PlanSectorsSummaryMetadata; }>; /** * Get sector summaries for all production plans * * @param options - Optional request configuration * @returns Promise resolving to all plans sectors summary data and metadata * * @throws {ValidationError} When the request parameters are invalid (HTTP 400) * @throws {AuthorizationError} When the request is not authorized (HTTP 401/403) * @throws {HanaQueryClientError} For other client or server errors * * @example * ```typescript * const result = await client.getAllPlansSectorsSummary(); * console.log(`Found ${result.data.sectors.length} total sectors`); * console.log(`Across ${result.metadata.unique_plans} unique plans`); * console.log(`Overall completion rate: ${result.metadata.overall_completion_rate}%`); * * // Group sectors by plan * const sectorsByPlan = result.data.sectors.reduce((acc, sector) => { * if (!acc[sector.plan_entry]) acc[sector.plan_entry] = []; * acc[sector.plan_entry].push(sector); * return acc; * }, {} as Record<number, PlanSectorSummary[]>); * ``` * * @remarks * - Returns all sector summaries across all plans in the system * - Empty array indicates no sectors exist in any plan * - Metadata includes overall statistics and unique counts */ getAllPlansSectorsSummary(options?: RequestOptions): Promise<{ data: AllPlansSectorsSummaryData; metadata: AllPlansSectorsSummaryMetadata; }>; /** * Get user by username */ getUser(username: string, options?: RequestOptions): Promise<{ data: UserData; metadata: UserMetadata; }>; /** * Get a specific production tag by entry number */ getTag(tagEntry: number | string, options?: RequestOptions): Promise<{ data: TagData; metadata: TagMetadata; }>; /** * Get all tags for a specific work order * * @param workOrderEntry - The work order entry (DocEntry) to get tags for * @param options - Optional request configuration * @returns Promise resolving to work order tags data and metadata * * @throws {ValidationError} When the workOrderEntry parameter is invalid (HTTP 400) * @throws {AuthorizationError} When the request is not authorized (HTTP 401/403) * @throws {HanaQueryClientError} For other client or server errors * * @example * ```typescript * const result = await client.getWorkOrderTags(12345); * console.log(`Found ${result.data.tags.length} tags for work order ${result.metadata.workOrderEntry}`); * * // Iterate through all tags * result.data.tags.forEach(tag => { * console.log(`Tag ${tag.tagEntry}: Status=${tag.status}, Quantity=${tag.quantity}${tag.quantityUoM}`); * if (tag.activationDateTime) { * console.log(` Activated: ${tag.activationDateTime} by ${tag.activationEmployee}`); * } * if (tag.quantificationDateTime) { * console.log(` Quantified: ${tag.quantificationDateTime} by ${tag.quantificationEmployee}`); * } * }); * ``` * * @remarks * - Returns empty array if work order has no tags * - No 404 if work order doesn't exist - just returns empty array * - Each tag includes full details (activation, quantification, use, discard info) * - Useful for tracking tag lifecycle for a specific production order */ getWorkOrderTags(workOrderEntry: number | string, options?: RequestOptions): Promise<{ data: WorkOrderTagsData; metadata: WorkOrderTagsMetadata; }>; /** * Get all injection machines */ getInjectionMachines(options?: RequestOptions): Promise<{ data: InjectionMachinesData; metadata: InjectionMachinesMetadata; }>; /** * Get a specific injection machine by code */ getInjectionMachine(machineCode: string, options?: RequestOptions): Promise<{ data: SingleInjectionMachineData; metadata: SingleInjectionMachineMetadata; }>; /** * Get all molds with specifications */ getMolds(options?: RequestOptions): Promise<{ data: MoldsData; metadata: MoldsMetadata; }>; /** * Get a specific mold by code */ getMold(moldCode: string, options?: RequestOptions): Promise<{ data: SingleMoldData; metadata: SingleMoldMetadata; }>; /** * List all business partners. * * @example * ```typescript * const { data, metadata } = await client.getBusinessPartners(); * console.log(`${metadata.count} partners`); * data.partners.forEach(p => console.log(p.cardcode, p.cardname)); * ``` * * @remarks Returns 404 (mapped to `NotFoundError`) if the table is empty. */ getBusinessPartners(options?: RequestOptions): Promise<{ data: BusinessPartnersData; metadata: BusinessPartnersMetadata; }>; /** * Search business partners by phone, email, name (fuzzy), and/or CardCode. * * At least one of `phone`, `email`, `q`, or `cardCode` must be supplied — * otherwise the server returns 400 (surfaced as `ValidationError`). * * @param criteria - Search criteria. `fuzziness` (0.1–1.0, default 0.7) tunes * the FUZZY threshold for `q`. * @example * ```typescript * const { data, metadata } = await client.searchBusinessPartners({ * q: 'jose silva', * fuzziness: 0.8 * }); * console.log(`${metadata.count} matches`); * ``` * * @remarks * - `phone` is normalized to digits and matched as a substring. * - `email` is exact after canonical email extraction (case-insensitive). * - `q` is HANA `CONTAINS FUZZY` across CardName, City, State, Country, * Block, Address, ZipCode, IndName. Words are OR-ed. * - `cardCode` is an exact match. * - Empty result returns 200 with `partners: []`; no NotFoundError. */ searchBusinessPartners(criteria: SearchCriteria, options?: RequestOptions): Promise<{ data: BusinessPartnersData; metadata: BusinessPartnersSearchMetadata; }>; /** * Get the contacts for a specific business partner. * * @param cardCode - The business partner's CardCode. * @example * ```typescript * const { data, metadata } = await client.getBusinessPartnerContacts('C12345'); * console.log(`${metadata.count} contacts for ${metadata.cardCode}`); * ``` * * @remarks * Returns 200 with an empty array even if the cardCode does not exist — * this endpoint does **not** 404 on unknown partners. Use * `searchBusinessPartners({ cardCode })` first if you need an existence check. */ getBusinessPartnerContacts(cardCode: string, options?: RequestOptions): Promise<{ data: ContactsData; metadata: BusinessPartnerContactsMetadata; }>; /** * List all contacts. * * @example * ```typescript * const { data, metadata } = await client.getContacts(); * console.log(`${metadata.count} contacts`); * ``` * * @remarks * - Returns 404 (NotFoundError) if the table is empty. * - `cardname` on the returned contacts is `null`/absent here — only the * search endpoint joins to ocrd and populates it. */ getContacts(options?: RequestOptions): Promise<{ data: ContactsData; metadata: ContactsMetadata; }>; /** * Search contacts by phone, email, name (fuzzy), and/or CardCode. * * Same criteria shape as {@link searchBusinessPartners}; at least one of * `phone` / `email` / `q` / `cardCode` is required (400 otherwise). * * @param criteria - Search criteria. `fuzziness` (0.1–1.0, default 0.7) tunes * the FUZZY threshold for `q`. * * @example * ```typescript * // Find contacts named "jose" within partner C12345 * const { data } = await client.searchContacts({ q: 'jose', cardCode: 'C12345' }); * data.contacts.forEach(c => console.log(c.contactname, '@', c.cardname)); * ``` * * @remarks * Cross-table — `phone` / `email` / `q` also match the parent BP's fields. * Returned contacts include `cardname` populated from the joined BP row. */ searchContacts(criteria: SearchCriteria, options?: RequestOptions): Promise<{ data: ContactsData; metadata: ContactsSearchMetadata; }>; /** * Check if a plan exists without fetching its full details * * @param planId - The plan ID to check * @param options - Optional request configuration * @returns Promise resolving to true if plan exists, false otherwise * * @example * ```typescript * const exists = await client.planExists(123); * if (exists) { * console.log('Plan exists'); * } else { * console.log('Plan not found'); * } * ``` */ planExists(planId: number | string, options?: RequestOptions): Promise<boolean>; /** * Get plan products with explicit plan existence information * * @param planId - The plan ID to get products for * @param options - Optional request configuration * @returns Promise resolving to plan existence status, products, and metadata * * @example * ```typescript * const result = await client.getPlanProductsSafe(123); * if (!result.planExists) { * console.log('Plan does not exist'); * } else if (result.products.length === 0) { * console.log('Plan exists but has no products'); * } else { * console.log(`Plan has ${result.products.length} products`); * } * ``` */ getPlanProductsSafe(planId: number | string, options?: RequestOptions): Promise<{ planExists: boolean; products: PlanProductsData['products']; metadata: PlanProductsMetadata; }>; /** * Get plan work orders with explicit plan existence information * * @param planId - The plan ID to get work orders for * @param options - Optional request configuration * @returns Promise resolving to plan existence status, work orders, and metadata * * @example * ```typescript * const result = await client.getPlanWorkOrdersSafe(123); * if (!result.planExists) { * console.log('Plan does not exist'); * } else if (result.workOrders.length === 0) { * console.log('Plan exists but has no work orders'); * } else { * console.log(`Plan has ${result.workOrders.length} work orders`); * } * ``` */ getPlanWorkOrdersSafe(planId: number | string, options?: RequestOptions): Promise<{ planExists: boolean; workOrders: PlanWorkOrdersData['workOrders']; metadata: PlanWorkOrdersMetadata; }>; /** * Create a request builder for fluent API usage */ request(endpoint: string): RequestBuilder; /** * Core request method with retry logic and error handling */ private executeAPIRequest; /** * Execute a single HTTP request */ private executeRequest; /** * Parse response headers into typed object */ private parseResponseHeaders; /** * Check authorization status from headers */ private checkAuthorization; /** * Sleep for specified milliseconds */ private sleep; /** * Logging method */ private log; /** * Get current client configuration */ getConfig(): Readonly<Required<ClientConfig>>; /** * Update client configuration */ updateConfig(updates: Partial<ClientConfig>): void; /** * Test connection to the API */ testConnection(): Promise<boolean>; } /** * Request Builder for fluent API usage */ export declare class RequestBuilder { private client; private endpoint; private params; private requestOptions; constructor(client: HanaQueryClient, endpoint: string); /** * Add query parameters */ query(params: Record<string, any>): this; /** * Set request timeout */ timeout(ms: number): this; /** * Set retry count */ retries(count: number): this; /** * Set abort signal */ signal(signal: AbortSignal): this; /** * Execute the request */ execute<T extends APIResponse>(): Promise<T>; } /** * Factory function to create a client instance */ export declare function createClient(config: ClientConfig, customConfig?: Partial<Omit<ClientConfig, 'baseUrl'>>): HanaQueryClient; /** * Factory function to create a client from environment configuration */ export declare function createClientFromEnvironment(environment: string, customConfig?: Partial<ClientConfig>): HanaQueryClient; /** * Default client instance (requires baseUrl to be set via environment or manual configuration) * Example: createClient({ baseUrl: 'http://localhost:3001' }) */ //# sourceMappingURL=hana-query-client.d.ts.map