UNPKG

@smartsamurai/krapi-sdk

Version:

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

304 lines (290 loc) 10.1 kB
/** * Collections Adapter * * Unifies CollectionsHttpClient and CollectionsService/CollectionsSchemaManager behind a common interface. */ import { CollectionsSchemaManager } from "../../collections-schema-manager"; import { CollectionsService } from "../../collections-service"; import { KrapiError } from "../../core/krapi-error"; import { CollectionsHttpClient } from "../../http-clients/collections-http-client"; import { Collection, FieldType } from "../../types"; import { createAdapterInitError } from "./error-handler"; type Mode = "client" | "server"; export class CollectionsAdapter { private mode: Mode; private httpClient: CollectionsHttpClient | undefined; private service: CollectionsService | undefined; private schemaManager: CollectionsSchemaManager | undefined; constructor( mode: Mode, httpClient?: CollectionsHttpClient, service?: CollectionsService, schemaManager?: CollectionsSchemaManager ) { this.mode = mode; this.httpClient = httpClient; this.service = service; this.schemaManager = schemaManager; } async create( projectId: string, collectionData: { name: string; description?: string; fields: Array<{ name: string; type: string; required?: boolean; unique?: boolean; indexed?: boolean; default?: unknown; validation?: Record<string, unknown>; }>; indexes?: Array<{ name: string; fields: string[]; unique?: boolean; }>; } ): Promise<Collection> { if (this.mode === "client") { if (!this.httpClient) { throw createAdapterInitError("HTTP client", this.mode); } const response = await this.httpClient.createCollection(projectId, collectionData); if (response && typeof response === "object") { if ("collection" in response && response.collection) { return response.collection as unknown as Collection; } if ("data" in response && response.data) { return response.data as unknown as Collection; } if ("id" in response && "name" in response) { return response as unknown as Collection; } } return {} as Collection; } else { if (!this.schemaManager) { throw createAdapterInitError("Collections schema manager", this.mode); } const result = await this.schemaManager.createCollection({ ...collectionData, fields: collectionData.fields.map((f) => ({ ...f, type: f.type as FieldType, required: f.required ?? false, unique: f.unique ?? false, indexed: f.indexed ?? false, })), }); result.project_id = projectId; return result as unknown as Collection; } } async get(projectId: string, collectionName: string): Promise<Collection> { if (this.mode === "client") { if (!this.httpClient) { throw createAdapterInitError("HTTP client", this.mode); } const response = await this.httpClient.getCollection(projectId, collectionName); const collection = response.data as unknown as Collection; if (!collection) { throw KrapiError.notFound(`Collection '${collectionName}' not found in project '${projectId}'`, { collectionName, projectId }); } return collection; } else { if (!this.service) { throw createAdapterInitError("Collections service", this.mode); } // Use CollectionsService.getCollection which supports UUID and case-insensitive name lookup const collection = await this.service.getCollection(projectId, collectionName); if (!collection) { throw KrapiError.notFound(`Collection '${collectionName}' not found in project '${projectId}'`, { collectionName, projectId }); } return collection; } } async getAll( projectId: string, options?: { limit?: number; offset?: number; search?: string; } ): Promise<Collection[]> { if (this.mode === "client") { if (!this.httpClient) { throw createAdapterInitError("HTTP client", this.mode); } const response = await this.httpClient.getProjectCollections(projectId, options); if (response && typeof response === "object") { if ("collections" in response && Array.isArray(response.collections)) { return response.collections as unknown as Collection[]; } if ("data" in response && Array.isArray(response.data)) { return response.data as unknown as Collection[]; } if (Array.isArray(response)) { return response as unknown as Collection[]; } } return []; } else { if (!this.schemaManager) { throw createAdapterInitError("Collections schema manager", this.mode); } const collections = await this.schemaManager.getCollections(); return collections.filter((c) => c.project_id === projectId) as unknown as Collection[]; } } async update( projectId: string, collectionName: string, updates: { description?: string; fields?: Array<{ name: string; type: string; required?: boolean; unique?: boolean; indexed?: boolean; default?: unknown; validation?: Record<string, unknown>; }>; indexes?: Array<{ name: string; fields: string[]; unique?: boolean; }>; } ): Promise<Collection> { if (this.mode === "client") { if (!this.httpClient) { throw createAdapterInitError("HTTP client", this.mode); } const response = await this.httpClient.updateCollection(projectId, collectionName, updates); return (response.data as unknown as Collection) || ({} as Collection); } else { const collection = await this.get(projectId, collectionName); if (!collection) { throw KrapiError.notFound("Collection not found"); } if (!this.schemaManager) { throw createAdapterInitError("Collections schema manager", this.mode); } return this.schemaManager.updateCollection(collection.id, updates as Partial<Collection>) as unknown as Collection; } } async delete(projectId: string, collectionName: string): Promise<{ success: boolean }> { if (this.mode === "client") { if (!this.httpClient) { throw createAdapterInitError("HTTP client", this.mode); } const response = await this.httpClient.deleteCollection(projectId, collectionName); return { success: response.data?.success || false }; } else { const collection = await this.get(projectId, collectionName); if (!collection) { return { success: false }; } if (!this.schemaManager) { throw createAdapterInitError("Collections schema manager", this.mode); } const success = await this.schemaManager.deleteCollection(collection.id); return { success }; } } async getSchema(projectId: string, collectionName: string): Promise<Collection> { return this.get(projectId, collectionName); } async validateSchema( projectId: string, collectionName: string ): Promise<{ valid: boolean; issues: Array<{ type: string; field?: string; message: string; severity: "error" | "warning" | "info"; }>; }> { if (this.mode === "client") { if (!this.httpClient) { throw createAdapterInitError("HTTP client", this.mode); } const response = await this.httpClient.validateCollectionSchema(projectId, collectionName); return response.data || { valid: false, issues: [] }; } else { if (!this.schemaManager) { throw createAdapterInitError("Collections schema manager", this.mode); } const collection = await this.get(projectId, collectionName); if (!collection) { throw KrapiError.notFound(`Collection '${collectionName}' not found in project '${projectId}'`, { collectionName, projectId }); } const validation = await this.schemaManager.validateCollectionSchema(collection.id); const issues = validation.issues.map((issue) => { let severity: "error" | "warning" | "info" = "error"; if (issue.type === "extra_field") { severity = "warning"; } else if (issue.type === "missing_index") { severity = "info"; } return { type: issue.type, field: issue.field, message: issue.description, severity, }; }); return { valid: validation.isValid, issues: issues.map((issue) => { const result: { type: string; field?: string; message: string; severity: "info" | "warning" | "error"; } = { type: issue.type, message: issue.message, severity: issue.severity, }; if (issue.field !== undefined) { result.field = issue.field; } return result; }), }; } } async getStatistics( projectId: string, collectionName: string ): Promise<{ total_documents: number; total_size_bytes: number }> { if (this.mode === "client") { if (!this.httpClient) { throw createAdapterInitError("HTTP client", this.mode); } const response = await this.httpClient.getCollectionStatistics(projectId, collectionName); return ( (response.data as unknown as { total_documents: number; total_size_bytes: number; }) || { total_documents: 0, total_size_bytes: 0 } ); } else { if (!this.service) { throw createAdapterInitError("Collections service", this.mode); } const documents = await this.service.getDocuments(projectId, collectionName); const totalSize = JSON.stringify(documents).length; return { total_documents: documents.length, total_size_bytes: totalSize, }; } } }