UNPKG

@smartsamurai/krapi-sdk

Version:

KRAPI TypeScript SDK - Easy-to-use client SDK for connecting to self-hosted KRAPI servers (like Appwrite SDK)

369 lines (347 loc) 11 kB
import { AxiosInstance } from "axios"; import { CollectionsHttpClient } from "../http-clients/collections-http-client"; import { ApiResponse } from "../types"; import { ResponseNormalizer } from "../utils/response-normalizer"; /** * Collections API Manager * * Handles all collection and document-related API operations for the KRAPI client SDK. */ export class CollectionsApiManager { private axiosInstance: AxiosInstance; private collectionsClient: CollectionsHttpClient; constructor(axiosInstance: AxiosInstance, collectionsClient: CollectionsHttpClient) { this.axiosInstance = axiosInstance; this.collectionsClient = collectionsClient; } /** * Documents API methods */ documents = { /** * List documents in a collection */ list: async ( projectId: string, collectionName: string, options?: { limit?: number; offset?: number; filter?: Record<string, unknown>; sort?: Record<string, "asc" | "desc">; } ): Promise<ApiResponse<unknown[]>> => { // Construct correct endpoint: /krapi/k1/projects/{projectId}/collections/{collectionName}/documents const url = `/projects/${projectId}/collections/${collectionName}/documents`; const params = new URLSearchParams(); if (options?.limit) params.append("limit", options.limit.toString()); if (options?.offset) params.append("offset", options.offset.toString()); if (options?.filter) params.append("filter", JSON.stringify(options.filter)); if (options?.sort) params.append("sort", JSON.stringify(options.sort)); // Let interceptor handle all headers - no explicit headers needed const response = await this.axiosInstance.get< | ApiResponse<unknown[]> | { success: boolean; data?: { documents?: unknown[]; [key: string]: unknown }; documents?: unknown[]; } >( `/krapi/k1${url}${params.toString() ? `?${params.toString()}` : ""}` ); // Normalize response format using utility const normalizedData = ResponseNormalizer.normalizeDocumentListResponse(response.data); return { success: true, data: normalizedData, }; }, /** * Get a single document */ get: async ( projectId: string, collectionName: string, documentId: string ): Promise<ApiResponse<unknown>> => { const response = await this.collectionsClient.getDocument(projectId, collectionName, documentId); const result: ApiResponse<unknown> = { success: response.success, data: ResponseNormalizer.normalizeDocumentResponse(response.data), }; if (response.error) { result.error = response.error; } return result; }, /** * Create a new document */ create: async ( projectId: string, collectionName: string, data: Record<string, unknown> ): Promise<ApiResponse<unknown>> => { // Construct correct endpoint: /krapi/k1/projects/{projectId}/collections/{collectionName}/documents // Let interceptor handle all headers - Content-Type is already in defaults const response = await this.axiosInstance.post< | ApiResponse<unknown> | { success: boolean; data?: unknown; id?: string; [key: string]: unknown; } >( `/krapi/k1/projects/${projectId}/collections/${collectionName}/documents`, { data, created_by: "client" } ); // Normalize response format using utility const normalized = ResponseNormalizer.normalizeDocumentResponse(response.data); return { success: true, data: normalized, }; }, /** * Update a document */ update: async ( projectId: string, collectionName: string, documentId: string, data: Record<string, unknown> ): Promise<ApiResponse<unknown>> => { return this.collectionsClient.updateDocument( projectId, collectionName, documentId, { data, updated_by: "client" } ); }, /** * Delete a document */ delete: async ( projectId: string, collectionName: string, documentId: string ): Promise<ApiResponse<{ success: boolean }>> => { return this.collectionsClient.deleteDocument(projectId, collectionName, documentId); }, /** * Bulk create documents */ bulkCreate: async ( projectId: string, collectionName: string, documents: Record<string, unknown>[] ): Promise< ApiResponse<{ created: unknown[]; errors: Array<{ index: number; error: string }>; }> > => { // Convert array to CreateDocumentRequest[] const createRequests = documents.map((doc) => ({ data: doc, created_by: "client", })); return this.collectionsClient.bulkCreateDocuments(projectId, collectionName, createRequests); }, /** * Bulk update documents with filter */ bulkUpdate: async ( projectId: string, collectionName: string, filter: Record<string, unknown>, updates: Record<string, unknown> ): Promise<ApiResponse<unknown>> => { // For bulk update with filter, we need to use a different endpoint // This is a simplified version - may need to adjust based on actual API const response = await this.axiosInstance.put<ApiResponse<unknown>>( `/krapi/k1/projects/${projectId}/collections/${collectionName}/documents/bulk`, { filter, updates } ); return response.data; }, /** * Bulk delete documents with filter */ bulkDelete: async ( projectId: string, collectionName: string, filter: Record<string, unknown> ): Promise<ApiResponse<{ success: boolean }>> => { // For bulk delete with filter, we need to fetch IDs first or use a filter endpoint // This is a simplified version - may need to adjust based on actual API const response = await this.axiosInstance.post< ApiResponse<{ success: boolean }> >( `/krapi/k1/projects/${projectId}/collections/${collectionName}/documents/bulk-delete`, { filter } ); return response.data; }, /** * Search documents */ search: async ( projectId: string, collectionName: string, query: string, options?: { fields?: string[]; limit?: number; } ): Promise<ApiResponse<unknown[]>> => { const searchOptions: { text: string; fields?: string[]; limit?: number; } = { text: query, }; if (options?.fields !== undefined) searchOptions.fields = options.fields; if (options?.limit !== undefined) searchOptions.limit = options.limit; return this.collectionsClient.searchDocuments(projectId, collectionName, searchOptions); }, /** * Aggregate documents */ aggregate: async ( projectId: string, collectionName: string, options: { groupBy?: string; operations: Array<{ field: string; operation: string }>; } ): Promise< ApiResponse<{ groups: Record<string, Record<string, number>>; total_groups: number; }> > => { const aggregations: Record< string, { type: "count" | "sum" | "avg" | "min" | "max"; field?: string } > = {}; if (options.operations) { for (const op of options.operations) { const opType = op.operation as "count" | "sum" | "avg" | "min" | "max"; if (["count", "sum", "avg", "min", "max"].includes(op.operation)) { aggregations[op.field] = { type: opType, field: op.field }; } } } const aggregateOptions: { group_by?: string[]; aggregations: Record< string, { type: "count" | "sum" | "avg" | "min" | "max"; field?: string } >; } = { aggregations, }; if (options.groupBy) { aggregateOptions.group_by = [options.groupBy]; } return this.collectionsClient.aggregateDocuments(projectId, collectionName, aggregateOptions); }, }; /** * Collection management methods */ collections = { /** * List collections in a project */ list: async (projectId: string): Promise<ApiResponse<unknown[]>> => { const result = await this.collectionsClient.getProjectCollections(projectId); // Handle different response formats if (result && typeof result === "object") { if ("collections" in result && Array.isArray(result.collections)) { return { success: true, data: result.collections, } as ApiResponse<unknown[]>; } if ("data" in result && Array.isArray(result.data)) { return { success: true, data: result.data, } as ApiResponse<unknown[]>; } } return { success: true, data: [], } as ApiResponse<unknown[]>; }, /** * Get a single collection */ get: async ( projectId: string, collectionName: string ): Promise<ApiResponse<unknown>> => { return this.collectionsClient.getCollection(projectId, collectionName); }, /** * Create a new collection */ create: async ( projectId: string, collectionData: { name: string; description?: string; fields: Array<{ name: string; type: string; required?: boolean; default?: unknown; }>; indexes?: Array<{ name: string; fields: string[]; unique?: boolean; }>; } ): Promise<ApiResponse<unknown>> => { const response = await this.collectionsClient.createCollection( projectId, collectionData ); // Normalize response using utility const normalizedData = ResponseNormalizer.normalizeCollectionResponse(response.data); const result: ApiResponse<unknown> = { success: response.success, data: normalizedData, }; if (response.error) { result.error = response.error; } return result; }, /** * Update a collection */ update: async ( projectId: string, collectionName: string, updates: Record<string, unknown> ): Promise<ApiResponse<unknown>> => { return this.collectionsClient.updateCollection(projectId, collectionName, updates); }, /** * Delete a collection */ delete: async ( projectId: string, collectionName: string ): Promise<ApiResponse<{ success: boolean }>> => { return this.collectionsClient.deleteCollection(projectId, collectionName); }, }; }