diff --git a/cli/examples/example.resource-type.json b/cli/examples/example.resource-type.json new file mode 100644 index 0000000000..aac0062ef8 --- /dev/null +++ b/cli/examples/example.resource-type.json @@ -0,0 +1,15 @@ +{ + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "apiKey": { + "type": "string", + "description": "", + "default": "keyXXXXXXXXXXXXXX" + } + }, + "required": [], + "type": "object" + }, + "description": "Example Description" +} diff --git a/cli/push.ts b/cli/push.ts index 2a6bb22489..1d200aaf9e 100644 --- a/cli/push.ts +++ b/cli/push.ts @@ -7,6 +7,7 @@ import { pushResource } from "./resource.ts"; import { findContentFile, pushScript } from "./script.ts"; import { GlobalOptions } from "./types.ts"; import { pushVariable } from "./variable.ts"; +import { pushResourceType } from "./resource-type.ts"; type Candidate = { path: string; @@ -49,6 +50,16 @@ async function findCandidateFiles(dir: string): Promise { ); candidates.push(...(await findCandidateFiles(path.join(dir, e.name)))); } + } else { + // handle root files + if (e.name.endsWith(".resource-type.json")) { + candidates.push({ + group: false, + groupOrUsername: "", + path: dir + (dir.endsWith("/") ? "" : "/") + e.name, + }); + console.log(candidates); + } } } return candidates; @@ -100,6 +111,26 @@ async function push(opts: GlobalOptions, dir?: string) { // get the type & filter it for valid ones. const type = fileNameParts.at(-2); + if (type == "resource-type") { + if (candidate.group == false && candidate.groupOrUsername == "") { + console.log("pushing resource type " + fileNameParts.at(-3)!); + await pushResourceType( + workspace, + candidate.path, + fileNameParts.at(-3)! + ); + } else { + console.log( + colors.yellow( + "Found resource type file at " + + candidate.path + + " this appears to be inside a path folder. Resource types are not addressed by path. Place them at the root or inside only an organizational folder. Ignoring this file!" + ) + ); + } + continue; + } + if ( type != "flow" && type != "resource" && diff --git a/cli/resource-type.ts b/cli/resource-type.ts new file mode 100644 index 0000000000..cedfca9e22 --- /dev/null +++ b/cli/resource-type.ts @@ -0,0 +1,87 @@ +import { Command } from "https://deno.land/x/cliffy@v0.25.4/command/command.ts"; +import { ResourceService } from "https://deno.land/x/windmill@v1.50.0/mod.ts"; +import { GlobalOptions } from "./types.ts"; +import { colors } from "https://deno.land/x/cliffy@v0.25.4/ansi/colors.ts"; +import { getContext } from "./context.ts"; +import { Table } from "https://deno.land/x/cliffy@v0.25.4/table/table.ts"; + +type ResourceTypeFile = { + schema?: any; + description?: string; +}; + +export async function pushResourceType( + workspace: string, + filePath: string, + name: string +) { + const data: ResourceTypeFile = JSON.parse(await Deno.readTextFile(filePath)); + if ( + await ResourceService.existsResourceType({ + workspace: workspace, + path: name, + }) + ) { + console.log(colors.yellow("Updating existing resource type...")); + await ResourceService.updateResourceType({ + workspace: workspace, + path: name, + requestBody: { + description: data.description, + schema: data.schema, + }, + }); + } else { + console.log(colors.yellow("Creating new resource type...")); + await ResourceService.createResourceType({ + workspace: workspace, + requestBody: { + name: name, + description: data.description, + schema: data.schema, + workspace_id: workspace, + }, + }); + } +} + +type PushOptions = GlobalOptions; +async function push(opts: PushOptions, filePath: string, name: string) { + const { workspace } = await getContext(opts); + + const fstat = await Deno.stat(filePath); + if (!fstat.isFile) { + throw new Error("file path must refer to a file."); + } + + console.log(colors.bold.yellow("Pushing resource...")); + + await pushResourceType(workspace, filePath, name); + console.log(colors.bold.underline.green("Resource successfully pushed")); +} + +async function list(opts: GlobalOptions) { + const { workspace } = await getContext(opts); + const res = await ResourceService.listResourceType({ + workspace, + }); + + new Table() + .header(["Workspace", "Name"]) + .padding(2) + .border(true) + .body(res.map((x) => [x.workspace_id ?? "Global", x.name])) + .render(); +} + +const command = new Command() + .description("resource type related commands") + .action(list as any) + .command( + "push", + "push a local resource spec. This overrides any remote versions." + ) + .arguments(" ") + .action(push as any); + +export default command;