/** * GCS (Google Cloud Storage) client implementation. * * Uses the native GCS plugin type from @superblocksteam/types. */ import type { PartialMessage } from "@bufbuild/protobuf"; import type { z } from "zod"; import type { Plugin as GCSPlugin } from "@superblocksteam/types/dist/src/plugins/gcs/v1/plugin_pb"; import { RestApiValidationError } from "../../errors.js"; import { IntegrationError } from "../../runtime/errors.js"; import type { QueryExecutor, TraceMetadata } from "../registry.js"; import type { IntegrationConfig, IntegrationClientImpl } from "../types.js"; import type { GCSClient, GCSAction, GCSListObjectsOptions, GCSGetObjectOptions, } from "./types.js"; /** * Internal implementation of GCSClient. */ export class GCSClientImpl implements GCSClient, IntegrationClientImpl { readonly config: IntegrationConfig; private readonly executeQuery: QueryExecutor; constructor(config: IntegrationConfig, executeQuery: QueryExecutor) { this.config = config; this.executeQuery = executeQuery; } get name(): string { return this.config.name; } get pluginId(): string { return this.config.pluginId; } /** * Builds a GCS plugin request and executes it. */ private async execute( action: GCSAction, request: PartialMessage, metadata?: TraceMetadata, ): Promise { const fullRequest: PartialMessage = { ...request, action, }; return this.executeQuery( fullRequest as Record, undefined, metadata, ); } /** * Validates a result against a Zod schema. */ private validate( result: unknown, schema: z.ZodSchema, operation: string, ): T { const parseResult = schema.safeParse(result); if (!parseResult.success) { throw new RestApiValidationError( `Result validation failed: ${parseResult.error.message}`, { zodError: parseResult.error, data: result, }, ); } return parseResult.data; } /** * Wraps an operation with standard error handling. */ private async withErrorHandling( operation: string, fn: () => Promise, ): Promise { try { return await fn(); } catch (error) { if ( error instanceof RestApiValidationError || error instanceof IntegrationError ) { throw error; } throw new IntegrationError(this.config.name, operation, error); } } async listBuckets( schema: z.ZodSchema, metadata?: TraceMetadata, ): Promise { return this.withErrorHandling("listBuckets", async () => { const result = await this.execute("LIST_BUCKETS", {}, metadata); return this.validate(result, schema, "listBuckets"); }); } async listObjects( bucket: string, schema: z.ZodSchema, options?: GCSListObjectsOptions, metadata?: TraceMetadata, ): Promise { return this.withErrorHandling("listObjects", async () => { const request: PartialMessage = { resource: bucket, }; if (options?.prefix) { request.prefix = options.prefix; } const result = await this.execute("LIST_OBJECTS", request, metadata); return this.validate(result, schema, "listObjects"); }); } async getObject( bucket: string, path: string, schema: z.ZodSchema, options?: GCSGetObjectOptions, metadata?: TraceMetadata, ): Promise { return this.withErrorHandling("getObject", async () => { const request: PartialMessage = { resource: bucket, path, }; if (options?.responseType) { request.responseType = options.responseType; } const result = await this.execute("GET_OBJECT", request, metadata); return this.validate(result, schema, "getObject"); }); } async deleteObject( bucket: string, path: string, metadata?: TraceMetadata, ): Promise { return this.withErrorHandling("deleteObject", async () => { await this.execute( "DELETE_OBJECT", { resource: bucket, path, }, metadata, ); }); } async uploadObject( bucket: string, path: string, body: string, metadata?: TraceMetadata, ): Promise { return this.withErrorHandling("uploadObject", async () => { await this.execute( "UPLOAD_OBJECT", { resource: bucket, path, body, }, metadata, ); }); } async uploadMultipleObjects( bucket: string, fileObjects: string, metadata?: TraceMetadata, ): Promise { return this.withErrorHandling("uploadMultipleObjects", async () => { await this.execute( "UPLOAD_MULTIPLE_OBJECTS", { resource: bucket, fileObjects, }, metadata, ); }); } async generateSignedUrl( bucket: string, path: string, schema: z.ZodSchema, metadata?: TraceMetadata, ): Promise { return this.withErrorHandling("generateSignedUrl", async () => { const result = await this.execute( "GENERATE_PRESIGNED_URL", { resource: bucket, path, }, metadata, ); return this.validate(result, schema, "generateSignedUrl"); }); } }