diff --git a/resource-client/bin/generate-clients.mjs b/resource-client/bin/generate-clients.mjs index 8a6c47b..4258969 100644 --- a/resource-client/bin/generate-clients.mjs +++ b/resource-client/bin/generate-clients.mjs @@ -10,7 +10,7 @@ * npx @major-tech/resource-client list * * Modes: app (default) | tool - * Types: postgresql | mssql | mysql | dynamodb | cosmosdb | snowflake | bigquery | neo4j | hubspot | linkedin | tiktokads | googlecalendar | gmail | googledrive | googlesheets | outreach | custom | graphql | lambda | salesforce | s3 | slack | majorauth | googleanalytics | quickbooks | gong | clerk | stripe | fireflies | attio | dynamics | linear | ringcentral | zohodesk | zohoprojects | sqs | metamarketing | sharepoint | googlesearchconsole | notion + * Types: postgresql | mssql | mysql | clickhouse | dynamodb | cosmosdb | snowflake | bigquery | neo4j | hubspot | linkedin | tiktokads | googlecalendar | gmail | googledrive | googlesheets | outreach | custom | graphql | lambda | salesforce | s3 | slack | majorauth | googleanalytics | quickbooks | gong | clerk | stripe | fireflies | attio | dynamics | linear | ringcentral | zohodesk | zohoprojects | sqs | metamarketing | sharepoint | googlesearchconsole | notion * * Examples: * npx @major-tech/resource-client add "abc-123" "orders-db" "postgresql" "Orders database" "app-123" @@ -227,6 +227,7 @@ function getClientClass(type) { 'sharepoint': 'SharePointResourceClient', 'googlesearchconsole': 'GoogleSearchConsoleResourceClient', 'notion': 'NotionResourceClient', + 'clickhouse': 'ClickhouseResourceClient', }; return typeMap[type] || 'PostgresResourceClient'; } @@ -285,7 +286,7 @@ function generateIndexFile(resources) { } function addResource(resourceId, name, type, description, applicationId, framework, mode) { - const validTypes = ['postgresql', 'mssql', 'mysql', 'dynamodb', 'cosmosdb', 'snowflake', 'bigquery', 'neo4j', 'hubspot', 'linkedin', 'tiktokads', 'googlecalendar', 'gmail', 'googledrive', 'googlesheets', 'outreach', 'custom', 'graphql', 'lambda', 'salesforce', 's3', 'slack', 'majorauth', 'googleanalytics', 'quickbooks', 'gong', 'clerk', 'stripe', 'fireflies', 'attio', 'dynamics', 'linear', 'ringcentral', 'zohodesk', 'zohoprojects', 'sqs', 'metamarketing', 'sharepoint', 'googlesearchconsole', 'notion']; + const validTypes = ['postgresql', 'mssql', 'mysql', 'clickhouse', 'dynamodb', 'cosmosdb', 'snowflake', 'bigquery', 'neo4j', 'hubspot', 'linkedin', 'tiktokads', 'googlecalendar', 'gmail', 'googledrive', 'googlesheets', 'outreach', 'custom', 'graphql', 'lambda', 'salesforce', 's3', 'slack', 'majorauth', 'googleanalytics', 'quickbooks', 'gong', 'clerk', 'stripe', 'fireflies', 'attio', 'dynamics', 'linear', 'ringcentral', 'zohodesk', 'zohoprojects', 'sqs', 'metamarketing', 'sharepoint', 'googlesearchconsole', 'notion']; if (!validTypes.includes(type)) { console.error(`❌ Invalid type: ${type}`); console.error(` Valid types: ${validTypes.join(', ')}`); @@ -440,7 +441,7 @@ function main() { console.log('\nModes: app (default) | tool'); console.log(' app — requires , reads MAJOR_API_BASE_URL'); console.log(' tool — embeds toolId from tool.json at generation time, reads RESOURCE_API_URL'); - console.log('\nTypes: postgresql | mssql | mysql | dynamodb | cosmosdb | snowflake | bigquery | neo4j | hubspot | linkedin | tiktokads | googlecalendar | gmail | googledrive | googlesheets | outreach | custom | graphql | lambda | salesforce | s3 | slack | majorauth | googleanalytics | quickbooks | gong | clerk | stripe | fireflies | attio | dynamics | linear | ringcentral | zohodesk | zohoprojects | sqs | metamarketing | sharepoint | googlesearchconsole | notion'); + console.log('\nTypes: postgresql | mssql | mysql | clickhouse | dynamodb | cosmosdb | snowflake | bigquery | neo4j | hubspot | linkedin | tiktokads | googlecalendar | gmail | googledrive | googlesheets | outreach | custom | graphql | lambda | salesforce | s3 | slack | majorauth | googleanalytics | quickbooks | gong | clerk | stripe | fireflies | attio | dynamics | linear | ringcentral | zohodesk | zohoprojects | sqs | metamarketing | sharepoint | googlesearchconsole | notion'); return; } diff --git a/resource-client/src/clients/clickhouse.ts b/resource-client/src/clients/clickhouse.ts new file mode 100644 index 0000000..22ad73b --- /dev/null +++ b/resource-client/src/clients/clickhouse.ts @@ -0,0 +1,41 @@ +import type { + DbClickhouseParamPrimitive, + DatabaseInvokeResponse, +} from "../schemas"; +import { BaseResourceClient } from "../base"; +import { buildClickhouseInvokePayload } from "../payload-builders/clickhouse"; + +export class ClickhouseResourceClient extends BaseResourceClient { + /** + * Execute a SQL query against ClickHouse + * @param sql The SQL query to execute + * @param params Optional positional parameters (? placeholders) + * @param invocationKey Unique key for tracking this invocation + * @param timeoutMs Optional timeout in milliseconds + * @returns Typed response with rows of type T + * + * @example + * ```ts + * interface Event { id: number; name: string; timestamp: string } + * + * const response = await client.invoke( + * "SELECT id, name, timestamp FROM events WHERE id = ?", + * [123], + * "get-event-123" + * ); + * + * if (response.ok) { + * const events: Event[] = response.result.rows; + * } + * ``` + */ + async invoke>( + sql: string, + params: DbClickhouseParamPrimitive[] | undefined, + invocationKey: string, + timeoutMs?: number + ): Promise> { + const payload = buildClickhouseInvokePayload(sql, params, timeoutMs); + return this.invokeRaw(payload, invocationKey) as Promise>; + } +} diff --git a/resource-client/src/index.ts b/resource-client/src/index.ts index 17e4ecb..fa609a4 100644 --- a/resource-client/src/index.ts +++ b/resource-client/src/index.ts @@ -48,6 +48,7 @@ export { MetaMarketingResourceClient } from "./clients/metamarketing"; export { SharePointResourceClient } from "./clients/sharepoint"; export { GoogleSearchConsoleResourceClient } from "./clients/google-search-console"; export { NotionResourceClient } from "./clients/notion"; +export { ClickhouseResourceClient } from "./clients/clickhouse"; // Export payload builders (for use in testing UIs, etc.) export * from "./payload-builders"; @@ -85,5 +86,6 @@ export type { ZohoProjectsInvokeResponse, SqsInvokeResponse, MysqlInvokeResponse, + ClickhouseInvokeResponse, BaseInvokeSuccess, } from "./schemas/response"; diff --git a/resource-client/src/payload-builders/clickhouse.ts b/resource-client/src/payload-builders/clickhouse.ts new file mode 100644 index 0000000..c6e7d7c --- /dev/null +++ b/resource-client/src/payload-builders/clickhouse.ts @@ -0,0 +1,21 @@ +import type { DbClickhousePayload, DbClickhouseParamPrimitive } from "../schemas"; + +/** + * Build a ClickHouse invoke payload + * @param sql The SQL query to execute + * @param params Optional positional parameters (? placeholders) + * @param timeoutMs Optional timeout in milliseconds + */ +export function buildClickhouseInvokePayload( + sql: string, + params?: DbClickhouseParamPrimitive[], + timeoutMs?: number +): DbClickhousePayload { + return { + type: "database", + subtype: "clickhouse", + sql, + params, + timeoutMs, + }; +} diff --git a/resource-client/src/payload-builders/from-extracted-params.ts b/resource-client/src/payload-builders/from-extracted-params.ts index 2a50201..ee0e66c 100644 --- a/resource-client/src/payload-builders/from-extracted-params.ts +++ b/resource-client/src/payload-builders/from-extracted-params.ts @@ -13,6 +13,7 @@ import { } from "./cosmosdb"; import { buildMssqlInvokePayload } from "./mssql"; import { buildMysqlInvokePayload } from "./mysql"; +import { buildClickhouseInvokePayload } from "./clickhouse"; import { buildSnowflakeInvokePayload, buildSnowflakeExecutePayload, @@ -263,6 +264,16 @@ export function buildPayloadFromExtractedParams( return buildMysqlInvokePayload(sql, params as (string | number | boolean | null)[], timeoutMs); } + // ========================================================================= + // ClickHouse + // ========================================================================= + case "clickhouse": { + const sql = findParam(extractedParams, "SQL") as string; + const params = findParam(extractedParams, "Params") as unknown[] | undefined; + const timeoutMs = findParam(extractedParams, "Timeout") as number | undefined; + return buildClickhouseInvokePayload(sql, params as (string | number | boolean | null)[], timeoutMs); + } + // ========================================================================= // MSSQL // ========================================================================= diff --git a/resource-client/src/schemas/clickhouse.ts b/resource-client/src/schemas/clickhouse.ts new file mode 100644 index 0000000..94c6eb8 --- /dev/null +++ b/resource-client/src/schemas/clickhouse.ts @@ -0,0 +1,18 @@ +/** + * Allowed types for ClickHouse query parameters (positional ? placeholders) + */ +export type DbClickhouseParamPrimitive = string | number | boolean | null; + +/** + * Payload for invoking a ClickHouse database resource + */ +export interface DbClickhousePayload { + type: "database"; + subtype: "clickhouse"; + /** SQL query to execute */ + sql: string; + /** Optional positional parameters for the query (? placeholders) */ + params?: DbClickhouseParamPrimitive[]; + /** Optional timeout in milliseconds */ + timeoutMs?: number; +} diff --git a/resource-client/src/schemas/index.ts b/resource-client/src/schemas/index.ts index 97c6a28..2a68c55 100644 --- a/resource-client/src/schemas/index.ts +++ b/resource-client/src/schemas/index.ts @@ -3,6 +3,7 @@ export * from "./common"; export * from "./postgres"; export * from "./mssql"; export * from "./mysql"; +export * from "./clickhouse"; export * from "./dynamodb"; export * from "./cosmosdb"; export * from "./snowflake"; @@ -58,6 +59,7 @@ import type { ApiSlackPayload } from "./api-slack"; import type { DbPostgresPayload } from "./postgres"; import type { DbMssqlPayload } from "./mssql"; import type { DbMysqlPayload } from "./mysql"; +import type { DbClickhousePayload } from "./clickhouse"; import type { DbDynamoDBPayload } from "./dynamodb"; import type { DbCosmosDBPayload } from "./cosmosdb"; import type { DbSnowflakePayload } from "./snowflake"; @@ -129,4 +131,5 @@ export type ResourceInvokePayload = | ApiNotionPayload | ApiSqsPayload | ApiGmailPayload - | ApiGoogleDrivePayload; + | ApiGoogleDrivePayload + | DbClickhousePayload; diff --git a/resource-client/src/schemas/response.ts b/resource-client/src/schemas/response.ts index b39e62e..e65e0d7 100644 --- a/resource-client/src/schemas/response.ts +++ b/resource-client/src/schemas/response.ts @@ -214,6 +214,13 @@ export type MysqlInvokeResponse> = | BaseInvokeSuccess> | InvokeFailure; +/** + * Response from ClickHouse database resource invocation + */ +export type ClickhouseInvokeResponse> = + | BaseInvokeSuccess> + | InvokeFailure; + /** * Response from Google Search Console resource invocation */