From 090037602c078097550199d7b6acb48de959dbc2 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Thu, 6 Jul 2023 10:38:12 +0100 Subject: [PATCH] WIP create-integration CLI command --- integrations/slack/src/index.ts | 5 +- integrations/slack/src/tasks.ts | 8 +- packages/cli/package.json | 1 + packages/cli/src/cli/index.ts | 21 ++++ .../cli/src/commands/createIntegration.ts | 114 ++++++++++++++++++ pnpm-lock.yaml | 2 + 6 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 packages/cli/src/commands/createIntegration.ts diff --git a/integrations/slack/src/index.ts b/integrations/slack/src/index.ts index 96f4aecc5..7e1c8469e 100644 --- a/integrations/slack/src/index.ts +++ b/integrations/slack/src/index.ts @@ -1,6 +1,5 @@ import { WebClient } from "@slack/web-api"; import type { IntegrationClient, TriggerIntegration } from "@trigger.dev/sdk"; -import { clientFactory } from "./client"; import { joinConversation, postMessage } from "./tasks"; const tasks = { @@ -21,7 +20,9 @@ export class Slack this.client = { tasks, usesLocalAuth: false, - clientFactory, + clientFactory: (auth) => { + return new WebClient(auth.accessToken); + }, }; } diff --git a/integrations/slack/src/tasks.ts b/integrations/slack/src/tasks.ts index 38ee896e7..3caf0fd92 100644 --- a/integrations/slack/src/tasks.ts +++ b/integrations/slack/src/tasks.ts @@ -4,11 +4,11 @@ import type { MessageAttachment, MessageMetadata, WebAPIPlatformError, + WebClient, } from "@slack/web-api"; -import { clientFactory } from "./client"; import type { AuthenticatedTask } from "@trigger.dev/sdk"; -type SlackClientType = ReturnType; +type SlackClientType = InstanceType; export type ChatPostMessageArguments = { channel: string; @@ -40,7 +40,7 @@ function isPlatformError(error: unknown): error is WebAPIPlatformError { } export const postMessage: AuthenticatedTask< - ReturnType, + SlackClientType, ChatPostMessageArguments, Awaited> > = { @@ -102,7 +102,7 @@ type ConversationsJoinResponse = Awaited< >; export const joinConversation: AuthenticatedTask< - ReturnType, + SlackClientType, { channel: string }, ConversationsJoinResponse > = { diff --git a/packages/cli/package.json b/packages/cli/package.json index 212fbe9ed..60d5690eb 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -65,6 +65,7 @@ "localtunnel": "^2.0.2", "ngrok": "5.0.0-beta.2", "node-fetch": "^3.3.0", + "openai": "^3.3.0", "ora": "^6.1.2", "path-to-regexp": "^6.2.1", "simple-git": "^3.19.0", diff --git a/packages/cli/src/cli/index.ts b/packages/cli/src/cli/index.ts index 5ed9ef01c..e120d93d1 100644 --- a/packages/cli/src/cli/index.ts +++ b/packages/cli/src/cli/index.ts @@ -57,6 +57,27 @@ program await devCommand(path, options); }); +// program +// .command("create-integration") +// .description("Create a new integration package for Trigger.dev") +// .argument( +// "[path]", +// "The path where you would like the package to be created", +// "." +// ) +// .option( +// "-n, --package-name ", +// "The name of the package to create (e.g. @trigger.dev/slack)" +// ) +// .option( +// "-s, --sdk-package ", +// "The name of the SDK package to use (e.g. @slack/web-api)" +// ) +// .version(getVersion(), "-v, --version", "Display the version number") +// .action(async (path, options) => { +// await createIntegrationCommand(path, options); +// }); + export const promptTriggerUrl = async (): Promise => { const { instanceType } = await inquirer.prompt<{ instanceType: "cloud" | "self-hosted"; diff --git a/packages/cli/src/commands/createIntegration.ts b/packages/cli/src/commands/createIntegration.ts new file mode 100644 index 000000000..1ed04fda9 --- /dev/null +++ b/packages/cli/src/commands/createIntegration.ts @@ -0,0 +1,114 @@ +import { z } from "zod"; +import { logger } from "../utils/logger.js"; +import { resolvePath } from "../utils/parseNameAndPath.js"; +import { COMMAND_NAME } from "../consts.js"; +import inquirer from "inquirer"; +// import { OpenAIApi } from "openai"; + +const CLIOptionsSchema = z.object({ + packageName: z.string().optional(), + sdkPackage: z.string().optional(), +}); + +type CLIOptions = z.infer; +type ResolvedCLIOptions = Required; + +export async function createIntegrationCommand(path: string, cliOptions: any) { + const result = CLIOptionsSchema.safeParse(cliOptions); + + if (!result.success) { + logger.error(result.error.message); + + process.exit(1); + } + + const options = result.data; + + const resolvedPath = resolvePath(path); + + const resolvedOptions = await resolveOptionsWithPrompts( + options, + resolvedPath + ); + + console.log(resolvedOptions); +} + +const resolveOptionsWithPrompts = async ( + options: CLIOptions, + _path: string +): Promise => { + const resolvedOptions: CLIOptions = { ...options }; + + try { + if (!options.packageName) { + resolvedOptions.packageName = await promptPackageName(); + } + + if (!options.sdkPackage) { + resolvedOptions.sdkPackage = await promptSdkPackage(); + } + } catch (err) { + // If the user is not calling the command from an interactive terminal, inquirer will throw an error with isTTYError = true + // If this happens, we catch the error, tell the user what has happened, and then continue to run the program with a default trigger project + // Otherwise we have to do some fancy namespace extension logic on the Error type which feels overkill for one line + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (err instanceof Error && (err as any).isTTYError) { + logger.warn( + `'${COMMAND_NAME} create-integration' needs an interactive terminal to provide options` + ); + + const { shouldContinue } = await inquirer.prompt<{ + shouldContinue: boolean; + }>({ + name: "shouldContinue", + type: "confirm", + message: `Continue initializing your trigger.dev project?`, + default: true, + }); + + if (!shouldContinue) { + logger.info("Exiting..."); + process.exit(0); + } + } else { + throw err; + } + } + + return resolvedOptions as ResolvedCLIOptions; +}; + +export const promptPackageName = async (): Promise => { + const { packageName } = await inquirer.prompt<{ packageName: string }>({ + type: "input", + name: "packageName", + message: "What is the name of your integration package?", + validate: (input) => { + if (!input) { + return "Please enter a package name"; + } + + return true; + }, + }); + + return packageName; +}; + +export const promptSdkPackage = async (): Promise => { + const { sdkPackage } = await inquirer.prompt<{ sdkPackage: string }>({ + type: "input", + name: "sdkPackage", + message: "What is the name of the SDK package you would like to use?", + validate: (input) => { + if (!input) { + return "Please enter an SDK package name"; + } + + return true; + }, + }); + + return sdkPackage; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd3975176..f0ba01180 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -678,6 +678,7 @@ importers: localtunnel: ^2.0.2 ngrok: 5.0.0-beta.2 node-fetch: ^3.3.0 + openai: ^3.3.0 ora: ^6.1.2 path-to-regexp: ^6.2.1 rimraf: ^3.0.2 @@ -701,6 +702,7 @@ importers: localtunnel: 2.0.2 ngrok: 5.0.0-beta.2 node-fetch: 3.3.0 + openai: 3.3.0 ora: 6.1.2 path-to-regexp: 6.2.1 simple-git: 3.19.0