diff --git a/server/node-service/src/common/util.test.ts b/server/node-service/src/common/util.test.ts new file mode 100644 index 000000000..d099a35de --- /dev/null +++ b/server/node-service/src/common/util.test.ts @@ -0,0 +1,112 @@ +import { specsToOptions, version2spec } from "./util"; + +describe('version2spec', () => { + test('should return the spec for the given version', () => { + const specs = { + v1: 'spec for version 1', + v2: 'spec for version 2', + v3: 'spec for version 3' + }; + + expect(version2spec(specs, 'v2')).toBe('spec for version 2'); + }); + + test('should return the first spec if version is undefined', () => { + const specs = { + v1: 'spec for version 1', + v2: 'spec for version 2', + v3: 'spec for version 3' + }; + + expect(version2spec(specs, undefined)).toBe('spec for version 1'); + }); + + test('should return the first spec if version is an empty string', () => { + const specs = { + v1: 'spec for version 1', + v2: 'spec for version 2', + v3: 'spec for version 3' + }; + + expect(version2spec(specs, "")).toBe('spec for version 1'); + }); + + test('should return undefined if specs is an empty object and version is undefined', () => { + const specs = {}; + + expect(version2spec(specs, undefined)).toBeUndefined(); + }); + + test('should return undefined if specs is an empty object and version is an empty string', () => { + const specs = {}; + + expect(version2spec(specs, "")).toBeUndefined(); + }); + + test('should return undefined if the specified version does not exist in specs', () => { + const specs = { + v1: 'spec for version 1', + v2: 'spec for version 2', + v3: 'spec for version 3' + }; + + expect(version2spec(specs, 'v4')).toBeUndefined(); + }); +}); + +describe('specsToOptions', () => { + test('should convert specs object to options array', () => { + const specs = { + color: 'red', + size: 'large', + weight: 'light' + }; + + const expectedOptions = [ + { value: 'color', label: 'color' }, + { value: 'size', label: 'size' }, + { value: 'weight', label: 'weight' } + ]; + + expect(specsToOptions(specs)).toEqual(expectedOptions); + }); + + test('should return an empty array if specs object is empty', () => { + const specs = {}; + const expectedOptions: any[] = []; + + expect(specsToOptions(specs)).toEqual(expectedOptions); + }); + + test('should handle specs object with non-string values', () => { + const specs = { + color: 'red', + size: 42, + available: true + }; + + const expectedOptions = [ + { value: 'color', label: 'color' }, + { value: 'size', label: 'size' }, + { value: 'available', label: 'available' } + ]; + + expect(specsToOptions(specs)).toEqual(expectedOptions); + }); + + test('should handle specs object with numeric keys', () => { + const specs = { + 1: 'one', + 2: 'two', + 3: 'three' + }; + + const expectedOptions = [ + { value: '1', label: '1' }, + { value: '2', label: '2' }, + { value: '3', label: '3' } + ]; + + expect(specsToOptions(specs)).toEqual(expectedOptions); + }); +}); \ No newline at end of file diff --git a/server/node-service/src/common/util.ts b/server/node-service/src/common/util.ts index 316a850f0..821c07639 100644 --- a/server/node-service/src/common/util.ts +++ b/server/node-service/src/common/util.ts @@ -1,5 +1,9 @@ import yaml from "yaml"; import fs from "fs"; +import { MultiOpenApiSpecItem } from "../plugins/openApi/parse"; +import path from "path"; +import { appendTags } from "../plugins/openApi/util"; +import _ from "lodash"; export function kvToRecord( kvs: { key: string; value: string }[], @@ -85,3 +89,45 @@ export function safeJsonStringify(data: any) { return null; } } + +export function specsToOptions(specs: any) { + return Object.keys(specs).map(k => ({value: k, label: k})); +} + +export function version2spec(specs: any, version: any) { + if(version == undefined || version == "") { + const keys = Object.keys(specs); + if(keys.length == 0) return; + return specs[keys[0]]; + } + return specs[version]; +} + +function genTagFromFileName(name: string) { + const fileName = name.replace(/\.yaml|twilio_|\.json/g, ""); + const parts = fileName.split("_"); + return parts.reduce((a, b) => { + if (/v\d+/.test(b)) { + return `${a}(${b})`; + } + return a + _.upperFirst(b); + }, ""); +} + +export function dirToSpecList(specDir: string) { + const specList: MultiOpenApiSpecItem[] = []; + + const start = performance.now(); + const specFiles = fs.readdirSync(specDir); + specFiles.forEach((specFile) => { + const spec = readYaml(path.join(specDir, specFile)); + const tag = genTagFromFileName(specFile); + appendTags(spec, tag); + specList.push({ + id: tag, + spec, + }); + }); + logger.info("spec list loaded %s, duration: %d ms",specDir, performance.now() - start); + return specList; +} \ No newline at end of file diff --git a/server/node-service/src/plugins/asana/index.ts b/server/node-service/src/plugins/asana/index.ts index 38cf121f8..eb4f72b4f 100644 --- a/server/node-service/src/plugins/asana/index.ts +++ b/server/node-service/src/plugins/asana/index.ts @@ -1,4 +1,4 @@ -import { readYaml } from "../../common/util"; +import { readYaml, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; import path from "path"; import { OpenAPIV3, OpenAPI } from "openapi-types"; @@ -8,7 +8,9 @@ import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; const spec = readYaml(path.join(__dirname, "./asana.spec.yaml")); - +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -23,7 +25,15 @@ const dataSourceConfig = { "key": "personalAccessToken.value", "label": "Token", "tooltip": "A [personal access token](https://developers.asana.com/docs/personal-access-token) allows access to the api for the user who created it. This should be kept a secret and be treated like a password.", - } + }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ] } as const; @@ -41,8 +51,8 @@ const asanaPlugin: DataSourcePlugin = { icon: "asana.svg", category: "Project Management", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec as OpenAPI.Document, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion) as OpenAPI.Document, parseOptions); return { type: "query", label: "Action", @@ -58,8 +68,9 @@ const asanaPlugin: DataSourcePlugin = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/circleCi/index.ts b/server/node-service/src/plugins/circleCi/index.ts index 4028c8180..23761ff74 100644 --- a/server/node-service/src/plugins/circleCi/index.ts +++ b/server/node-service/src/plugins/circleCi/index.ts @@ -5,6 +5,10 @@ import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import spec from "./circleCi.spec.json"; +import { specsToOptions, version2spec } from "../../common/util"; +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -16,6 +20,14 @@ const dataSourceConfig = { tooltip: "[Personal API Token](https://circleci.com/docs/managing-api-tokens/#creating-a-personal-api-token)", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -33,9 +45,9 @@ const circleCiPlugin: DataSourcePlugin = { icon: "circleCI.svg", category: "DevOps", dataSourceConfig, - queryConfig: async () => { + queryConfig: async (data) => { const { actions, categories } = await parseOpenApi( - spec as unknown as OpenAPI.Document, + version2spec(specs, data.specVersion) as unknown as OpenAPI.Document, parseOptions ); return { @@ -53,8 +65,9 @@ const circleCiPlugin: DataSourcePlugin = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as unknown as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as unknown as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/cloudinary/index.ts b/server/node-service/src/plugins/cloudinary/index.ts index a9b1b94cb..5d9066589 100644 --- a/server/node-service/src/plugins/cloudinary/index.ts +++ b/server/node-service/src/plugins/cloudinary/index.ts @@ -1,4 +1,4 @@ -import { readYaml } from "../../common/util"; +import { readYaml, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; import path from "path"; import { OpenAPI } from "openapi-types"; @@ -14,6 +14,9 @@ const specList = [ { spec: adminApiSpec, id: "admin" }, { spec: uploadApiSpec, id: "upload" }, ]; +const specs = { + "v1.0": specList, +} const dataSourceConfig = { type: "dataSource", @@ -32,6 +35,14 @@ const dataSourceConfig = { tooltip: "Basic auth password", placeholder: "", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -46,7 +57,7 @@ const parseOptions: ParseOpenApiOptions = { type DataSourceConfigType = ConfigToType; -let queryConfig: QueryConfig; +let queryConfig: any = {}; const cloudinaryPlugin: DataSourcePlugin = { id: "cloudinary", @@ -54,10 +65,10 @@ const cloudinaryPlugin: DataSourcePlugin = { icon: "cloudinary.svg", category: "Assets", dataSourceConfig, - queryConfig: async () => { - if (!queryConfig) { - const { actions, categories } = await parseMultiOpenApi(specList, parseOptions); - queryConfig = { + queryConfig: async (data) => { + if (!queryConfig[data.specVersion as keyof typeof queryConfig]) { + const { actions, categories } = await parseMultiOpenApi(version2spec(specs, data.specVersion), parseOptions); + queryConfig[data.specVersion as keyof typeof queryConfig] = { type: "query", label: "Action", categories: { @@ -67,15 +78,16 @@ const cloudinaryPlugin: DataSourcePlugin = { actions, }; } - return queryConfig; + return queryConfig[data.specVersion as keyof typeof queryConfig]; }, run: function (actionData, dataSourceConfig): Promise { const runApiDsConfig = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, specList); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion)); }, }; diff --git a/server/node-service/src/plugins/couchdb/index.ts b/server/node-service/src/plugins/couchdb/index.ts index 7c5731307..00c1dc70e 100644 --- a/server/node-service/src/plugins/couchdb/index.ts +++ b/server/node-service/src/plugins/couchdb/index.ts @@ -4,7 +4,10 @@ import { ConfigToType, DataSourcePlugin } from "lowcoder-sdk/dataSource"; import { runOpenApi } from "../openApi"; import { defaultParseOpenApiOptions, parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import spec from "./CouchDB-3.1.1-resolved.json"; - +import { specsToOptions, version2spec } from "../../common/util"; +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", params: [ @@ -38,6 +41,14 @@ const dataSourceConfig = { tooltip: "", placeholder: "", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -59,8 +70,8 @@ const couchdbPlugin: DataSourcePlugin = { icon: "couchdb.svg", category: "database", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec as OpenAPI.Document, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion) as OpenAPI.Document, parseOptions); return { type: "query", label: "Operation", @@ -77,8 +88,9 @@ const couchdbPlugin: DataSourcePlugin = { url: "", serverURL: serverURL, dynamicParamsConfig: otherDataSourceConfig, + specVersion: dataSourceConfig.specVersion }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV2.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV2.Document); }, }; diff --git a/server/node-service/src/plugins/datadog/index.ts b/server/node-service/src/plugins/datadog/index.ts index 93b8840c5..05cda2516 100644 --- a/server/node-service/src/plugins/datadog/index.ts +++ b/server/node-service/src/plugins/datadog/index.ts @@ -1,4 +1,4 @@ -import { readYaml } from "../../common/util"; +import { readYaml, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; import path from "path"; import { OpenAPIV3, OpenAPI } from "openapi-types"; @@ -7,6 +7,9 @@ import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; const spec = readYaml(path.join(__dirname, "./datadog.spec.yaml")); +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -27,6 +30,14 @@ const dataSourceConfig = { key: "appKeyAuth.value", label: "Application Key", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -47,8 +58,8 @@ const datadogPlugin: DataSourcePlugin = { icon: "datadog.svg", category: "DevOps", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion), parseOptions); return { type: "query", label: "Action", @@ -64,8 +75,9 @@ const datadogPlugin: DataSourcePlugin = { url: "", serverURL: dataSourceConfig.serverURL, dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/did/index.ts b/server/node-service/src/plugins/did/index.ts index 19431ccb4..9b823a2ab 100644 --- a/server/node-service/src/plugins/did/index.ts +++ b/server/node-service/src/plugins/did/index.ts @@ -1,26 +1,14 @@ -import { readYaml } from "../../common/util"; +import { dirToSpecList, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; import path from "path"; -import { OpenAPIV3, OpenAPI } from "openapi-types"; -import { QueryConfig, ConfigToType, DataSourcePlugin } from "lowcoder-sdk/dataSource"; +import { OpenAPI } from "openapi-types"; +import { ConfigToType, DataSourcePlugin, QueryConfig } from "lowcoder-sdk/dataSource"; import { runOpenApi } from "../openApi"; -import { MultiOpenApiSpecItem, parseMultiOpenApi, ParseOpenApiOptions } from "../openApi/parse"; -import { appendTags } from "../../plugins/openApi/util"; -import { readdirSync } from "fs"; +import { parseMultiOpenApi, ParseOpenApiOptions } from "../openApi/parse"; -const specList: MultiOpenApiSpecItem[] = []; -const specFiles = readdirSync(path.join(__dirname, "./did.spec")); -const start = performance.now(); -specFiles.forEach((specFile) => { - const spec = readYaml(path.join(__dirname, "./did.spec", specFile)); - const tag = _.upperFirst(specFile.replace(".json", "")); - appendTags(spec, tag); - specList.push({ - spec, - id: tag, - }); -}); -logger.info("did spec list loaded, duration: %d ms", performance.now() - start); +const specs = { + "v1.0": dirToSpecList(path.join(__dirname, "./did.spec")), +} const dataSourceConfig = { type: "dataSource", @@ -44,6 +32,14 @@ const dataSourceConfig = { tooltip: "Basic auth password", placeholder: "", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -55,7 +51,7 @@ const parseOptions: ParseOpenApiOptions = { type DataSourceConfigType = ConfigToType; -let queryConfig: QueryConfig | undefined; +let queryConfig: any = {}; const didPlugin: DataSourcePlugin = { id: "did", @@ -63,10 +59,10 @@ const didPlugin: DataSourcePlugin = { icon: "did.svg", category: "AI", dataSourceConfig, - queryConfig: async () => { - if (!queryConfig) { - const { actions, categories } = await parseMultiOpenApi(specList, parseOptions); - queryConfig = { + queryConfig: async (data) => { + if (!queryConfig[data.specVersion as keyof typeof queryConfig]) { + const { actions, categories } = await parseMultiOpenApi(version2spec(specs, data.specVersion), parseOptions); + queryConfig[data.specVersion as keyof typeof queryConfig] = { type: "query", label: "Action", categories: { @@ -76,15 +72,16 @@ const didPlugin: DataSourcePlugin = { actions, }; } - return queryConfig; + return queryConfig[data.specVersion as keyof typeof queryConfig]; }, run: function (actionData, dataSourceConfig): Promise { const runApiDsConfig = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, specList); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion)); }, }; diff --git a/server/node-service/src/plugins/dynamodb/dataSourceConfig.ts b/server/node-service/src/plugins/dynamodb/dataSourceConfig.ts index 999cf14ea..89d03b7ce 100644 --- a/server/node-service/src/plugins/dynamodb/dataSourceConfig.ts +++ b/server/node-service/src/plugins/dynamodb/dataSourceConfig.ts @@ -1,5 +1,8 @@ import { ConfigToType } from "lowcoder-sdk/dataSource"; - +import { specsToOptions } from "../../common/util"; +const specs = { + "v1.0": "" +} const dataSourceConfig = { type: "dataSource", params: [ @@ -27,6 +30,14 @@ const dataSourceConfig = { label: "Region", defaultValue: "us-west-1", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; diff --git a/server/node-service/src/plugins/dynamodb/index.ts b/server/node-service/src/plugins/dynamodb/index.ts index 03131239f..5938fb878 100644 --- a/server/node-service/src/plugins/dynamodb/index.ts +++ b/server/node-service/src/plugins/dynamodb/index.ts @@ -2,6 +2,11 @@ import { DataSourcePlugin } from "lowcoder-sdk/dataSource"; import dataSourceConfig, { DataSourceDataType } from "./dataSourceConfig"; import queryConfig, { ActionDataType } from "./queryConfig"; import { runDynamoDbQuery } from "./run"; +import { version2spec } from "../../common/util"; + +const specs = { + "v1.0": queryConfig +} const dynamoDBPlugin: DataSourcePlugin = { id: "dynamodb", @@ -9,7 +14,9 @@ const dynamoDBPlugin: DataSourcePlugin = { category: "database", icon: "dynamodb.svg", dataSourceConfig, - queryConfig, + queryConfig: async (data) => { + return version2spec(specs, data.specVersion); + }, run: async function (actionData, dataSourceConfig): Promise { return runDynamoDbQuery(actionData, dataSourceConfig); }, diff --git a/server/node-service/src/plugins/dynamodb/run.test.ts b/server/node-service/src/plugins/dynamodb/run.test.ts index 6ce0e2eaa..f02a73228 100644 --- a/server/node-service/src/plugins/dynamodb/run.test.ts +++ b/server/node-service/src/plugins/dynamodb/run.test.ts @@ -8,6 +8,7 @@ test.skip("run dynamodb query", async () => { accessKey: "", secretKey: "", endpointUrl: "http://localhost:6789", + specVersion: "", }; const createTableActionData: ActionDataType = { diff --git a/server/node-service/src/plugins/firebase/dataSourceConfig.ts b/server/node-service/src/plugins/firebase/dataSourceConfig.ts index 84966c8c9..86fe2cb5d 100644 --- a/server/node-service/src/plugins/firebase/dataSourceConfig.ts +++ b/server/node-service/src/plugins/firebase/dataSourceConfig.ts @@ -1,5 +1,9 @@ import { ConfigToType } from "lowcoder-sdk/dataSource"; +import { specsToOptions } from "../../common/util"; +const specs = { + "v1.0": "" +} const dataSourceConfig = { type: "dataSource", params: [ @@ -23,6 +27,14 @@ const dataSourceConfig = { tooltip: "The [document](https://firebase.google.com/docs/admin/setup) on how to obtain the private key.", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; diff --git a/server/node-service/src/plugins/firebase/index.ts b/server/node-service/src/plugins/firebase/index.ts index ec9a1012a..508f5a3a7 100644 --- a/server/node-service/src/plugins/firebase/index.ts +++ b/server/node-service/src/plugins/firebase/index.ts @@ -2,13 +2,20 @@ import { DataSourcePlugin } from "lowcoder-sdk/dataSource"; import dataSourceConfig, { DataSourceDataType } from "./dataSourceConfig"; import queryConfig, { ActionDataType } from "./queryConfig"; import { runFirebasePlugin } from "./run"; +import { version2spec } from "../../common/util"; + +const specs = { + "v1.0": queryConfig +} const firebasePlugin: DataSourcePlugin = { id: "firebase", icon: "firebase.svg", name: "Google Firebase", category: "App Development", - queryConfig, + queryConfig: async (data) => { + return version2spec(specs, data.specVersion); + }, dataSourceConfig, run: function (actionData, dataSourceConfig): Promise { return runFirebasePlugin(actionData, dataSourceConfig); diff --git a/server/node-service/src/plugins/firebase/run.test.ts b/server/node-service/src/plugins/firebase/run.test.ts index bc11e4740..2355ac3d0 100644 --- a/server/node-service/src/plugins/firebase/run.test.ts +++ b/server/node-service/src/plugins/firebase/run.test.ts @@ -9,6 +9,7 @@ test.skip("realtime database", async () => { databaseUrl: "https://sarike-a3de9-default-rtdb.asia-southeast1.firebasedatabase.app/", privateKey, firestoreId: "", + specVersion: "", } ); console.info(res); diff --git a/server/node-service/src/plugins/front/index.ts b/server/node-service/src/plugins/front/index.ts index 03a4b3a95..1f66f4e62 100644 --- a/server/node-service/src/plugins/front/index.ts +++ b/server/node-service/src/plugins/front/index.ts @@ -5,6 +5,10 @@ import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import spec from "./front.spec.json"; +import { specsToOptions, version2spec } from "../../common/util"; +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -14,6 +18,14 @@ const dataSourceConfig = { key: "http.value", label: "Token", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -34,8 +46,8 @@ const frontPlugin: DataSourcePlugin = { icon: "front.svg", category: "CRM", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec as OpenAPI.Document, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion) as OpenAPI.Document, parseOptions); return { type: "query", label: "Action", @@ -51,8 +63,9 @@ const frontPlugin: DataSourcePlugin = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/github/index.ts b/server/node-service/src/plugins/github/index.ts index 1477c5323..079707951 100644 --- a/server/node-service/src/plugins/github/index.ts +++ b/server/node-service/src/plugins/github/index.ts @@ -1,4 +1,4 @@ -import { readYaml } from "../../common/util"; +import { readYaml, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; import path from "path"; import { OpenAPIV3, OpenAPI } from "openapi-types"; @@ -8,6 +8,9 @@ import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import SwaggerParser from "@apidevtools/swagger-parser"; const spec = readYaml(path.join(__dirname, "./github.spec.yaml")); +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -27,6 +30,14 @@ const dataSourceConfig = { "[Document](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) about how to create a personal access token", placeholder: "", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -45,8 +56,8 @@ const gitHubPlugin: DataSourcePlugin = { icon: "github.svg", category: "App Development", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion), parseOptions); return { type: "query", label: "Action", @@ -62,8 +73,9 @@ const gitHubPlugin: DataSourcePlugin = { url: "", serverURL: "https://api.github.com", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document, undefined, await deRefedSpec); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV3.Document, undefined, await deRefedSpec); }, }; diff --git a/server/node-service/src/plugins/googleCloudStorage/dataSourceConfig.ts b/server/node-service/src/plugins/googleCloudStorage/dataSourceConfig.ts index 45c75ddd5..6c55c507a 100644 --- a/server/node-service/src/plugins/googleCloudStorage/dataSourceConfig.ts +++ b/server/node-service/src/plugins/googleCloudStorage/dataSourceConfig.ts @@ -1,5 +1,9 @@ import { ConfigToType } from "lowcoder-sdk/dataSource"; +import { specsToOptions } from "../../common/util"; +const specs = { + "v1.0": "" +} export const dataSourceConfig = { type: "dataSource", params: [ @@ -18,6 +22,14 @@ export const dataSourceConfig = { type: "textInput", label: "Region", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; diff --git a/server/node-service/src/plugins/googleCloudStorage/index.ts b/server/node-service/src/plugins/googleCloudStorage/index.ts index bc0d44405..dd9e37c77 100644 --- a/server/node-service/src/plugins/googleCloudStorage/index.ts +++ b/server/node-service/src/plugins/googleCloudStorage/index.ts @@ -6,14 +6,20 @@ import queryConfig, { ActionDataType } from "./queryConfig"; import { DataSourceDataType } from "./dataSourceConfig"; import run, { validateDataSourceConfig } from "./run"; import { dataSourceConfig } from "./dataSourceConfig"; +import { version2spec } from "../../common/util"; +const specs = { + "v1.0": queryConfig +} const gcsPlugin = { id: "googleCloudStorage", name: "Google Cloud Storage", icon: "gcs.svg", category: "Assets", dataSourceConfig, - queryConfig: queryConfig, + queryConfig: async (data: any) => { + return version2spec(specs, data.specVersion); + }, validateDataSourceConfig: async (dataSourceConfig: DataSourceDataType) => { return validateDataSourceConfig(dataSourceConfig); diff --git a/server/node-service/src/plugins/huggingFaceEndpoint/index.ts b/server/node-service/src/plugins/huggingFaceEndpoint/index.ts index e814a0ffe..39ba40e18 100644 --- a/server/node-service/src/plugins/huggingFaceEndpoint/index.ts +++ b/server/node-service/src/plugins/huggingFaceEndpoint/index.ts @@ -5,6 +5,10 @@ import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import spec from "./huggingFace.spec.json"; +import { specsToOptions, version2spec } from "../../common/util"; +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -25,6 +29,14 @@ const dataSourceConfig = { tooltip: "You can get an Access Token [in your profile setting page](https://huggingface.co/settings/tokens)", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -42,9 +54,9 @@ const huggingFacePlugin: DataSourcePlugin = { icon: "huggingFace.svg", category: "AI", dataSourceConfig, - queryConfig: async () => { + queryConfig: async (data) => { const { actions, categories } = await parseOpenApi( - spec as unknown as OpenAPI.Document, + version2spec(specs, data.specVersion) as unknown as OpenAPI.Document, parseOptions ); return { @@ -62,8 +74,9 @@ const huggingFacePlugin: DataSourcePlugin = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as unknown as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as unknown as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/huggingFaceInference/index.ts b/server/node-service/src/plugins/huggingFaceInference/index.ts index cbd802de6..e75ee2a57 100644 --- a/server/node-service/src/plugins/huggingFaceInference/index.ts +++ b/server/node-service/src/plugins/huggingFaceInference/index.ts @@ -1,19 +1,6 @@ import _ from "lodash"; import { ConfigToType, DataSourcePlugin } from "lowcoder-sdk/dataSource"; - -const dataSourceConfig = { - type: "dataSource", - params: [ - { - type: "password", - key: "token", - label: "Access Token", - rules: [{ required: true }], - tooltip: - "You can get an Access Token [in your profile setting page](https://huggingface.co/settings/tokens)", - }, - ], -} as const; +import { specsToOptions, version2spec } from "../../common/util"; const queryConfig = { type: "query", @@ -42,6 +29,30 @@ const queryConfig = { }, ], } as const; +const specs = { + "v1.0": queryConfig +} +const dataSourceConfig = { + type: "dataSource", + params: [ + { + type: "password", + key: "token", + label: "Access Token", + rules: [{ required: true }], + tooltip: + "You can get an Access Token [in your profile setting page](https://huggingface.co/settings/tokens)", + }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, + ], +} as const; type DataSourceConfigType = ConfigToType; type ActionConfigType = ConfigToType; @@ -52,7 +63,9 @@ const huggingFaceInferencePlugin: DataSourcePlugin { + return version2spec(specs, data.specVersion); + }, run: async (actionData, dataSourceConfig) => { const response = await fetch( `https://api-inference.huggingface.co/models/${actionData.model}`, diff --git a/server/node-service/src/plugins/jira/index.ts b/server/node-service/src/plugins/jira/index.ts index 996bf75cb..5fca64eca 100644 --- a/server/node-service/src/plugins/jira/index.ts +++ b/server/node-service/src/plugins/jira/index.ts @@ -5,8 +5,13 @@ import { ConfigToType, DataSourcePlugin, QueryConfig } from "lowcoder-sdk/dataSo import path from "path"; import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; +import { specsToOptions, version2spec } from "../../common/util"; const specJson = readFileSync(path.join(__dirname, "./jira.spec.json")).toString(); +const specs = { + "v1.0": specJson, +} +//TODO: Thomas const dataSourceConfig = { type: "dataSource", @@ -36,6 +41,14 @@ const dataSourceConfig = { tooltip: "Basic auth password", placeholder: "", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -50,7 +63,7 @@ const parseOptions: ParseOpenApiOptions = { type DataSourceConfigType = ConfigToType; -let queryConfig: QueryConfig; +let queryConfig: any = {}; const jiraPlugin: DataSourcePlugin = { id: "jira", @@ -58,10 +71,10 @@ const jiraPlugin: DataSourcePlugin = { icon: "jira.svg", category: "Project Management", dataSourceConfig, - queryConfig: async () => { - if (!queryConfig) { - const { actions, categories } = await parseOpenApi(JSON.parse(specJson), parseOptions); - queryConfig = { + queryConfig: async (data) => { + if (!queryConfig[data.specVersion as keyof typeof queryConfig]) { + const { actions, categories } = await parseOpenApi(JSON.parse(version2spec(specs, data.specVersion)), parseOptions); + queryConfig[data.specVersion as keyof typeof queryConfig] = { type: "query", label: "Action", categories: { @@ -71,14 +84,15 @@ const jiraPlugin: DataSourcePlugin = { actions, }; } - return queryConfig; + return queryConfig[data.specVersion as keyof typeof queryConfig]; }, run: function (actionData, dataSourceConfig): Promise { - const spec = JSON.parse(specJson); + const spec = JSON.parse(version2spec(specs, dataSourceConfig.specVersion)); const runApiDsConfig = { url: "", serverURL: dataSourceConfig.serverUrl, dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; return runOpenApi(actionData, runApiDsConfig, spec); }, diff --git a/server/node-service/src/plugins/lowcoder/index.ts b/server/node-service/src/plugins/lowcoder/index.ts index 4bf53d1d0..5e1aed298 100644 --- a/server/node-service/src/plugins/lowcoder/index.ts +++ b/server/node-service/src/plugins/lowcoder/index.ts @@ -1,4 +1,4 @@ -import { readYaml } from "../../common/util"; +import { readYaml, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; import path from "path"; import { OpenAPIV3, OpenAPI } from "openapi-types"; @@ -7,6 +7,9 @@ import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import spec from './lowcoder.spec.json'; +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -30,7 +33,15 @@ const dataSourceConfig = { label: "Authorization", "tooltip": "API Key Authentication with a Bearer token. Copy your API Key here. (e.g. 'Bearer eyJhbGciO...')", "placeholder": "API Key Authentication with a Bearer token. Copy your API Key here. (e.g. 'Bearer eyJhbGciO...')" - } + }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ] } as const; @@ -48,8 +59,8 @@ const lowcoderPlugin: DataSourcePlugin = { icon: "lowcoder.svg", category: "App Development", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec as unknown as OpenAPI.Document, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion) as unknown as OpenAPI.Document, parseOptions); return { type: "query", label: "Action", @@ -66,8 +77,9 @@ const lowcoderPlugin: DataSourcePlugin = { url: "", serverURL: serverURL, dynamicParamsConfig: otherDataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/n8n/index.ts b/server/node-service/src/plugins/n8n/index.ts index d134218f6..e71a9371d 100644 --- a/server/node-service/src/plugins/n8n/index.ts +++ b/server/node-service/src/plugins/n8n/index.ts @@ -4,6 +4,10 @@ import { ConfigToType, DataSourcePlugin } from "lowcoder-sdk/dataSource"; import { runOpenApi } from "../openApi"; import { defaultParseOpenApiOptions, parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import spec from "./spec.json"; +import { specsToOptions, version2spec } from "../../common/util"; +const specs = { + "v1.0": spec, +} export function prepareServerUrl(url: string) { if (/\/api\/v[12]$/.test(url)) { @@ -35,6 +39,14 @@ const dataSourceConfig = { tooltip: "You api key, doc: [n8n API authentication](https://docs.n8n.io/api/authentication/)", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -57,8 +69,8 @@ const n8nPlugin: DataSourcePlugin = { icon: "n8n.svg", category: "Workflow", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec as OpenAPI.Document, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion) as OpenAPI.Document, parseOptions); return { type: "query", label: "Operation", @@ -77,8 +89,9 @@ const n8nPlugin: DataSourcePlugin = { dynamicParamsConfig: { "ApiKeyAuth.value": apiKey, }, + specVersion: dataSourceConfig.specVersion }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/notion/index.ts b/server/node-service/src/plugins/notion/index.ts index eb887b4e9..cb2104a01 100644 --- a/server/node-service/src/plugins/notion/index.ts +++ b/server/node-service/src/plugins/notion/index.ts @@ -1,4 +1,4 @@ -import { readYaml } from "../../common/util"; +import { readYaml, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; import path from "path"; import { OpenAPIV3, OpenAPI } from "openapi-types"; @@ -7,6 +7,9 @@ import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; const spec = readYaml(path.join(__dirname, "./notion.spec.yaml")); +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -24,6 +27,14 @@ const dataSourceConfig = { key: "bearerAuth.value", label: "Token", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -41,8 +52,8 @@ const notionPlugin: DataSourcePlugin = { icon: "notion.svg", category: "Project Management", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion), parseOptions); return { type: "query", label: "Action", @@ -58,8 +69,9 @@ const notionPlugin: DataSourcePlugin = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document, { + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV3.Document, { "Notion-Version": dataSourceConfig.notionVersion, }); }, diff --git a/server/node-service/src/plugins/oneSignal/index.ts b/server/node-service/src/plugins/oneSignal/index.ts index 86e3e6133..eab1e16b8 100644 --- a/server/node-service/src/plugins/oneSignal/index.ts +++ b/server/node-service/src/plugins/oneSignal/index.ts @@ -5,6 +5,10 @@ import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import spec from "./oneSignal.spec.json"; +import { specsToOptions, version2spec } from "../../common/util"; +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -23,6 +27,14 @@ const dataSourceConfig = { tooltip: "Another type of REST API key used for viewing Apps and related updates. [Documentation](https://documentation.onesignal.com/docs/accounts-and-keys#user-auth-key)", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -40,9 +52,9 @@ const oneSignalPlugin: DataSourcePlugin = { icon: "oneSignal.svg", category: "Messaging", dataSourceConfig, - queryConfig: async () => { + queryConfig: async (data) => { const { actions, categories } = await parseOpenApi( - spec as unknown as OpenAPI.Document, + version2spec(specs, data.specVersion) as unknown as OpenAPI.Document, parseOptions ); return { @@ -60,8 +72,9 @@ const oneSignalPlugin: DataSourcePlugin = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/openAi/index.ts b/server/node-service/src/plugins/openAi/index.ts index a63b9e1f5..3e03b0d48 100644 --- a/server/node-service/src/plugins/openAi/index.ts +++ b/server/node-service/src/plugins/openAi/index.ts @@ -1,4 +1,4 @@ -import { readYaml } from "../../common/util"; +import { readYaml, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; import path from "path"; import { OpenAPIV3, OpenAPI } from "openapi-types"; @@ -7,6 +7,9 @@ import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; const spec = readYaml(path.join(__dirname, "./openAi.yaml")); +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -20,6 +23,14 @@ const dataSourceConfig = { tooltip: "[In your Open AI account page](https://platform.openai.com/account/api-keys) on which you can create your api key", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -37,8 +48,8 @@ const openAiPlugin: DataSourcePlugin = { icon: "openAI.svg", category: "AI", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion), parseOptions); return { type: "query", label: "Action", @@ -54,8 +65,9 @@ const openAiPlugin: DataSourcePlugin = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/openApi/index.ts b/server/node-service/src/plugins/openApi/index.ts index f3e3df262..d53ffef07 100644 --- a/server/node-service/src/plugins/openApi/index.ts +++ b/server/node-service/src/plugins/openApi/index.ts @@ -39,7 +39,7 @@ const dataSourceConfig = { type: "textInput", tooltip: "Change the default server url written in the spec file.", placeholder: "https://example.com/api/v1", - }, + } ], } as const; diff --git a/server/node-service/src/plugins/postmanEcho/index.ts b/server/node-service/src/plugins/postmanEcho/index.ts index b73512e6a..c70ac5246 100644 --- a/server/node-service/src/plugins/postmanEcho/index.ts +++ b/server/node-service/src/plugins/postmanEcho/index.ts @@ -1,4 +1,4 @@ -import { readYaml } from "../../common/util"; +import { readYaml, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; import path from "path"; import { OpenAPIV3, OpenAPI } from "openapi-types"; @@ -7,11 +7,23 @@ import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import spec from './postmanEcho.spec.json'; +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", - params: [] + params: [ + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, + ] } as const; const parseOptions: ParseOpenApiOptions = { @@ -28,8 +40,8 @@ const postmanEchoPlugin: DataSourcePlugin = { icon: "postmanEcho.svg", category: "App Development", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec as unknown as OpenAPI.Document, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion) as unknown as OpenAPI.Document, parseOptions); return { type: "query", label: "Action", @@ -45,8 +57,9 @@ const postmanEchoPlugin: DataSourcePlugin = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/s3/dataSourceConfig.ts b/server/node-service/src/plugins/s3/dataSourceConfig.ts index 8e9e7de61..d408a9fd9 100644 --- a/server/node-service/src/plugins/s3/dataSourceConfig.ts +++ b/server/node-service/src/plugins/s3/dataSourceConfig.ts @@ -1,7 +1,8 @@ import { ConfigToType } from "lowcoder-sdk/dataSource"; import { S3I18nTranslator } from "./i18n"; +import { specsToOptions } from "../../common/util"; -const getDataSourceConfig = (i18n: S3I18nTranslator) => { +const getDataSourceConfig = (i18n: S3I18nTranslator, specs: any) => { const dataSourceConfig = { type: "dataSource", params: [ @@ -30,6 +31,14 @@ const getDataSourceConfig = (i18n: S3I18nTranslator) => { label: i18n.trans("region"), defaultValue: "us-west-1", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; return dataSourceConfig; diff --git a/server/node-service/src/plugins/s3/index.ts b/server/node-service/src/plugins/s3/index.ts index 92d2cad54..7b7d71988 100644 --- a/server/node-service/src/plugins/s3/index.ts +++ b/server/node-service/src/plugins/s3/index.ts @@ -8,6 +8,13 @@ import run, { validateDataSourceConfig } from "./run"; import getI18nTranslator from "./i18n"; import getDataSourceConfig from "./dataSourceConfig"; import getQueryConfig from "./queryConfig"; +import { dirToSpecList } from "../../common/util"; +import path from "path"; + + +const specs = { + "V1.0": "Nothing to do", +} const s3Plugin: DataSourcePluginFactory = (context: PluginContext) => { const i18n = getI18nTranslator(context.languages); @@ -17,8 +24,10 @@ const s3Plugin: DataSourcePluginFactory = (context: PluginContext) => { icon: "s3.svg", description: i18n.trans("description"), category: "Assets", - dataSourceConfig: getDataSourceConfig(i18n), - queryConfig: getQueryConfig(i18n), + dataSourceConfig: getDataSourceConfig(i18n, specs), + queryConfig: async (data) => { + return getQueryConfig(i18n, data.specVersion as keyof typeof specs) + }, validateDataSourceConfig: async (dataSourceConfig: DataSourceDataType) => { return validateDataSourceConfig(dataSourceConfig); diff --git a/server/node-service/src/plugins/s3/queryConfig.ts b/server/node-service/src/plugins/s3/queryConfig.ts index a4105db0f..12559bca9 100644 --- a/server/node-service/src/plugins/s3/queryConfig.ts +++ b/server/node-service/src/plugins/s3/queryConfig.ts @@ -1,7 +1,7 @@ import { ActionParamConfig, Config, ConfigToType, QueryConfig } from "lowcoder-sdk/dataSource"; import { S3I18nTranslator } from "./i18n"; -function getQueryConfig(i18n: S3I18nTranslator) { +function getQueryConfig(i18n: S3I18nTranslator, version: string) { const bucketActionParam = { key: "bucket", type: "textInput", diff --git a/server/node-service/src/plugins/s3/run.test.ts b/server/node-service/src/plugins/s3/run.test.ts index 60632fd46..a1c6089b2 100644 --- a/server/node-service/src/plugins/s3/run.test.ts +++ b/server/node-service/src/plugins/s3/run.test.ts @@ -6,6 +6,7 @@ const dataSourceConfig = { secretKey: "", endpointUrl: "", region: "us-west-2", + specVersion: "", }; const bucket = "lowcoder-demo"; diff --git a/server/node-service/src/plugins/sendGrid/index.ts b/server/node-service/src/plugins/sendGrid/index.ts index 9ae3e9766..4102889fc 100644 --- a/server/node-service/src/plugins/sendGrid/index.ts +++ b/server/node-service/src/plugins/sendGrid/index.ts @@ -5,6 +5,10 @@ import { runOpenApi } from "../openApi"; import { parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import spec from "./sendGrid.spec.json"; +import { specsToOptions, version2spec } from "../../common/util"; +const specs = { + "v1.0": spec, +} const dataSourceConfig = { type: "dataSource", @@ -16,6 +20,14 @@ const dataSourceConfig = { tooltip: "[Documentation](https://docs.sendgrid.com/ui/account-and-settings/api-keys#creating-an-api-key)", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -32,9 +44,9 @@ const sendGridPlugin: DataSourcePlugin = { icon: "sendGrid.svg", category: "Messaging", dataSourceConfig, - queryConfig: async () => { + queryConfig: async (data) => { const { actions, categories } = await parseOpenApi( - spec as unknown as OpenAPI.Document, + version2spec(specs, data.specVersion) as unknown as OpenAPI.Document, parseOptions ); return { @@ -55,8 +67,9 @@ const sendGridPlugin: DataSourcePlugin = { ...dataSourceConfig, "Authorization.value": "Bearer " + dataSourceConfig["Authorization.value"], }, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, spec as unknown as OpenAPIV3.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as unknown as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/stripe/index.ts b/server/node-service/src/plugins/stripe/index.ts index 03cf22317..04ef7d968 100644 --- a/server/node-service/src/plugins/stripe/index.ts +++ b/server/node-service/src/plugins/stripe/index.ts @@ -1,4 +1,4 @@ -import { readYaml } from "../../common/util"; +import { readYaml, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; import path from "path"; import { OpenAPIV3, OpenAPI } from "openapi-types"; @@ -9,6 +9,9 @@ import { readFileSync } from "fs"; import { parse } from "yaml"; const yamlContent = readFileSync(path.join(__dirname, "./stripe.spec.yaml"), "utf-8"); +const specs = { + "v1.0": yamlContent, +} const dataSourceConfig = { type: "dataSource", @@ -25,6 +28,14 @@ const dataSourceConfig = { tooltip: "You can gen a Secret Key [here](https://dashboard.stripe.com/test/apikeys)", placeholder: "", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -42,8 +53,8 @@ const stripePlugin: DataSourcePlugin = { icon: "stripe.svg", category: "eCommerce", dataSourceConfig, - queryConfig: async () => { - const spec = parse(yamlContent); + queryConfig: async (data) => { + const spec = parse(version2spec(specs, data.specVersion)); const { actions, categories } = await parseOpenApi(spec as OpenAPI.Document, parseOptions); return { type: "query", @@ -60,10 +71,11 @@ const stripePlugin: DataSourcePlugin = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; // always use a new spec object // because of this bug: https://github.com/APIDevTools/json-schema-ref-parser/issues/271 - const spec = parse(yamlContent); + const spec = parse(version2spec(specs, dataSourceConfig.specVersion)); return runOpenApi(actionData, runApiDsConfig, spec as OpenAPIV3.Document); }, }; diff --git a/server/node-service/src/plugins/twilio/did.spec/animations.json b/server/node-service/src/plugins/twilio/did.spec/animations.json new file mode 100644 index 000000000..9ecb43602 --- /dev/null +++ b/server/node-service/src/plugins/twilio/did.spec/animations.json @@ -0,0 +1,686 @@ +{ + "components": { + "examples": {}, + "headers": {}, + "parameters": {}, + "requestBodies": {}, + "responses": {}, + "schemas": { + "AnimationStatus": { "enum": ["created", "done", "error", "started"], "type": "string" }, + "CreateAnimationResponseDto": { + "properties": { + "id": { "type": "string", "description": "The ID of the animation" }, + "object": { "type": "string", "description": "An identifier of this animation" }, + "created_by": { + "type": "string", + "description": "The user id of the user that created the animation" + }, + "created_at": { + "type": "string", + "description": "Animation creation time as iso-8601 string" + }, + "status": { + "$ref": "#/components/schemas/AnimationStatus", + "description": "The status of the animation" + } + }, + "required": ["id", "object", "created_by", "created_at", "status"], + "type": "object", + "additionalProperties": true + }, + "JsonError": { + "properties": { + "kind": { "type": "string" }, + "description": { "type": "string" }, + "details": {} + }, + "required": ["kind", "description"], + "type": "object", + "additionalProperties": true + }, + "RemoteImageUrl": { + "type": "string", + "example": "https://path.to.directory/image.jpg", + "pattern": "(https|s3):.*\\.(?:jpg|jpeg|tiff|png|bmp|JPG|JPEG|TIFF|PNG|BMP)" + }, + "DriverUrl": { "type": "string", "pattern": "(https|s3|bank):\\/\\/.+" }, + "RemoteUrl": { + "type": "string", + "example": "https://path.to.directory/movie.mp4", + "pattern": "(https|s3):\\/\\/.+" + }, + "OverlapKind": { "enum": ["NO", "PARTIAL", "YES", "UNKNOWN"], "type": "string" }, + "Rect": { + "properties": { + "top": { "type": "number", "format": "double" }, + "left": { "type": "number", "format": "double" }, + "bottom": { "type": "number", "format": "double" }, + "right": { "type": "number", "format": "double" } + }, + "required": ["top", "left", "bottom", "right"], + "type": "object", + "additionalProperties": true + }, + "FaceSquareDto": { + "properties": { + "size": { + "type": "integer", + "format": "int32", + "description": "Size of the square side", + "minimum": 0 + }, + "top_left": { + "items": { "type": "integer", "format": "int32" }, + "type": "array", + "description": "Top left location of the face in the frame - can be negative", + "minItems": 2, + "maxItems": 2 + }, + "overlap": { "$ref": "#/components/schemas/OverlapKind" }, + "face_id": { "type": "string" }, + "detect_confidence": { "type": "number", "format": "double" }, + "detection": { "$ref": "#/components/schemas/Rect" } + }, + "required": ["size", "top_left"], + "type": "object", + "additionalProperties": true + }, + "SecuredUrl": { "type": "string", "pattern": "^(?:https):\\/\\/[\\S]+$" }, + "Logo": { + "properties": { + "url": { + "type": "string", + "description": "https url to an ARGB jpg/png image, a default logo is used otherwise", + "pattern": "((https|s3):\\/\\/.+)" + }, + "position": { + "items": { "type": "integer", "format": "int32" }, + "type": "array", + "description": "position of the logo in pixels from the top left corner (w,h) negative values are subtracted from last pixel", + "example": [0, 500], + "minItems": 2, + "maxItems": 2 + } + }, + "required": ["url", "position"], + "type": "object", + "additionalProperties": true + }, + "LogoDto": { "anyOf": [{ "$ref": "#/components/schemas/Logo" }, { "type": "boolean" }] }, + "VideoPath": { + "type": "string", + "example": "https://path.to.directory/movie.mp4", + "pattern": "(https|s3):.*\\.(?:mp4|mov|mpeg)" + }, + "ResultFormat": { "type": "string", "enum": ["mp4", "gif", "mov"] }, + "AnimateConfigDto": { + "properties": { + "logo": { "$ref": "#/components/schemas/LogoDto" }, + "overlay": { + "$ref": "#/components/schemas/VideoPath", + "description": "URL to overlay video to add on the animated result" + }, + "stitch": { + "type": "boolean", + "description": "stitch back the animated result to the original image" + }, + "max_animated_faces": { + "type": "number", + "format": "double", + "description": "the amount of faces to animate when stitch: true. Defaults to 5", + "minimum": 1, + "maximum": 5 + }, + "result_format": { + "$ref": "#/components/schemas/ResultFormat", + "description": "the file format of the animated video result" + } + }, + "type": "object", + "additionalProperties": true + }, + "UserData": { + "type": "string", + "minLength": 1, + "maxLength": 500, + "pattern": "^(?!\\s*$).+$" + }, + "CreateAnimationDto": { + "properties": { + "source_url": { + "$ref": "#/components/schemas/RemoteImageUrl", + "description": "The URL of the source image to be animated by the driver video. Image type of jpg|jpeg|tiff|png|bmp", + "example": "https://path.to/image.jpg" + }, + "driver_url": { + "$ref": "#/components/schemas/DriverUrl", + "description": "The URL of the driver video to drive the animation, if not provided a driver video will be selected for you from the predefined DriversBank", + "example": "bank://nostalgia/" + }, + "result_url": { + "$ref": "#/components/schemas/RemoteUrl", + "description": "The URL of the animation video, if not provided use default destination\nSupports S3 Signed URLs", + "example": "https://path.to.directory/movie.mp4" + }, + "face": { + "$ref": "#/components/schemas/FaceSquareDto", + "description": "the face to animate - otherwise detects the dominant face", + "example": { "top_left": [0, 0], "size": 512 } + }, + "webhook": { + "$ref": "#/components/schemas/SecuredUrl", + "description": "A webhook URL for sending the payload including animate details. In a case of empty value, the webhook will not be triggered", + "example": "https://host.domain.tld/to/webhook" + }, + "config": { + "$ref": "#/components/schemas/AnimateConfigDto", + "description": "Advanced configuration option" + }, + "user_data": { + "$ref": "#/components/schemas/UserData", + "description": "Non-sensitive custom data that will be added to the animation response and webhook." + } + }, + "required": ["source_url"], + "type": "object", + "additionalProperties": true + }, + "Record_string.any_": { + "properties": {}, + "type": "object", + "description": "Construct a type with a set of properties K of type T" + }, + "DriverError": { + "properties": { + "kind": { "type": "string", "default": "DriverError" }, + "description": { + "type": "string", + "default": "Driver provided is invalid or cannot be loaded" + } + }, + "type": "object", + "additionalProperties": true + }, + "LogoError": { + "properties": { + "kind": { "type": "string", "default": "LogoError" }, + "description": { + "type": "string", + "default": "Could not load logo image or invalid format or position" + } + }, + "type": "object", + "additionalProperties": true + }, + "SourceError": { + "properties": { + "kind": { "type": "string", "default": "SourceError" }, + "description": { + "type": "string", + "default": "Could not load source image or invalid format" + } + }, + "type": "object", + "additionalProperties": true + }, + "FaceError": { + "properties": { + "kind": { "type": "string", "default": "FaceError" }, + "description": { "type": "string", "default": "face not detected" } + }, + "type": "object", + "additionalProperties": true + }, + "VideoEnhanceError": { + "properties": { + "kind": { "type": "string", "default": "VideoEnhanceError" }, + "description": { + "type": "string", + "default": "Failed to create enhanced video (logo, subsample)" + } + }, + "type": "object", + "additionalProperties": true + }, + "UnknownError": { + "properties": { + "kind": { "type": "string", "default": "UnknownError" }, + "description": { "type": "string", "default": "Unknown internal error" } + }, + "type": "object", + "additionalProperties": true + }, + "AnimationError": { + "anyOf": [ + { "$ref": "#/components/schemas/DriverError" }, + { "$ref": "#/components/schemas/LogoError" }, + { "$ref": "#/components/schemas/SourceError" }, + { "$ref": "#/components/schemas/FaceError" }, + { "$ref": "#/components/schemas/VideoEnhanceError" }, + { "$ref": "#/components/schemas/UnknownError" } + ] + }, + "GetAnimationResponseDto": { + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object" }, + "user_id": { + "type": "string", + "description": "Unique identifier of the user that submitted the animation" + }, + "source_url": { + "type": "string", + "description": "The URL of the source image to be animated by the driver video. Image type of .jpg | .png" + }, + "driver_url": { + "type": "string", + "description": "The URL of the driver video to drive the animation, if not provided a driver video will be selected for you from the predefined DriversBank" + }, + "created_at": { + "type": "string", + "description": "Animation creation time as iso-8601 string" + }, + "created_by": { + "type": "string", + "description": "The user id of the user that created the animation" + }, + "started_at": { + "type": "string", + "description": "Animation start time as iso-8601 string" + }, + "modified_at": { + "type": "string", + "description": "last modified time as iso-8601 string" + }, + "status": { + "$ref": "#/components/schemas/AnimationStatus", + "description": "The status of the animation" + }, + "result_url": { "type": "string", "description": "s3 uri to the resulting video" }, + "metadata": { + "$ref": "#/components/schemas/Record_string.any_", + "description": "metadata that has been collected through the process" + }, + "error": { + "$ref": "#/components/schemas/AnimationError", + "description": "The error that failed the animation" + }, + "webhook": { + "type": "string", + "description": "A webhook URL for sending the payload including animate details. In a case of empty value, the webhook will not be triggered" + }, + "config": { + "$ref": "#/components/schemas/AnimateConfigDto", + "description": "The configuration that used to process the animation" + } + }, + "required": ["id", "user_id", "source_url", "created_at", "modified_at", "status"], + "type": "object", + "additionalProperties": true + }, + "GetAnimationsResponseDto": { + "properties": { + "animations": { + "items": { "$ref": "#/components/schemas/GetAnimationResponseDto" }, + "type": "array" + }, + "token": { "type": "string" } + }, + "required": ["animations"], + "type": "object", + "additionalProperties": true + } + }, + "securitySchemes": { + "basic": { "type": "http", "scheme": "basic" }, + "bearer": { "type": "http", "scheme": "bearer" } + } + }, + "info": { + "title": "animations", + "version": "1.3.0", + "description": "Animations", + "license": { "name": "ISC" }, + "contact": { "name": "D-ID" } + }, + "openapi": "3.0.0", + "paths": { + "/animations": { + "post": { + "operationId": "Create", + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateAnimationResponseDto" }, + "examples": { + "Example 1": { + "value": { + "id": "52907745-7672-470e-a803-a2f8feb52944", + "object": "animation", + "created_at": "2020-09-03T13:56:54.995", + "created_by": "995", + "status": "created" + } + } + } + } + } + }, + "400": { + "description": "BadRequestError | InvalidFileSizeError | InvalidImageResolutionError | ConfigError | InvalidFaceError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "BadRequestError", "description": "invalid source url" } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "402": { + "description": "InsufficientCreditsError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "InsufficientCreditsError", + "description": "not enough credits" + } + } + } + } + } + }, + "403": { + "description": "PermissionError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "PermissionError", + "description": "user has no permission for stitch" + } + } + } + } + } + } + }, + "description": "Create an animation", + "summary": "Create an animation", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/CreateAnimationDto" } } + } + } + }, + "get": { + "operationId": "GetMany", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetAnimationsResponseDto" } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + } + }, + "description": "Get animations", + "summary": "Get animations", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { + "description": "pagination - the amount of animations to return", + "in": "query", + "name": "limit", + "required": false, + "schema": { "default": 100, "format": "double", "type": "number" } + }, + { + "description": "pagination - the pagination token from the previous response, default = 100", + "in": "query", + "name": "token", + "required": false, + "schema": { "type": "string" } + } + ] + } + }, + "/animations/tasks": { + "post": { + "operationId": "CreateTask", + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateAnimationResponseDto" }, + "examples": { + "Example 1": { + "value": { + "id": "52907745-7672-470e-a803-a2f8feb52944", + "object": "animation", + "created_at": "2020-09-03T13:56:54.995", + "created_by": "995", + "status": "created" + } + } + } + } + } + }, + "400": { + "description": "BadRequestError | InvalidFileSizeError | InvalidImageResolutionError | ConfigError | InvalidFaceError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "BadRequestError", "description": "invalid source url" } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "402": { + "description": "InsufficientCreditsError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "InsufficientCreditsError", + "description": "not enough credits" + } + } + } + } + } + }, + "403": { + "description": "PermissionError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "PermissionError", + "description": "user has no permission for stitch" + } + } + } + } + } + } + }, + "description": "Create an animation task", + "summary": "Create an animation task", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/CreateAnimationDto" } } + } + } + } + }, + "/animations/{id}": { + "get": { + "operationId": "GetById", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetAnimationResponseDto" } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { "value": { "kind": "NotFoundError", "description": "not found" } } + } + } + } + } + }, + "description": "Get a specific animation", + "summary": "Get a specific animation", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } + ] + }, + "delete": { + "operationId": "Delete", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetAnimationResponseDto" } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { "value": { "kind": "NotFoundError", "description": "not found" } } + } + } + } + }, + "409": { + "description": "NotReadyError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "NotReadyError", + "description": "animation is in process, try again later" + } + } + } + } + } + } + }, + "description": "Delete a specific animation", + "summary": "Delete a specific animation", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } + ] + } + } + }, + "servers": [{ "url": "https://api.d-id.com/" }] +} diff --git a/server/node-service/src/plugins/twilio/did.spec/audios.json b/server/node-service/src/plugins/twilio/did.spec/audios.json new file mode 100644 index 000000000..3acb79451 --- /dev/null +++ b/server/node-service/src/plugins/twilio/did.spec/audios.json @@ -0,0 +1,228 @@ +{ + "components": { + "examples": {}, + "headers": {}, + "parameters": {}, + "requestBodies": {}, + "responses": {}, + "schemas": { + "Record_string.any_": { + "properties": {}, + "type": "object", + "description": "Construct a type with a set of properties K of type T" + }, + "UploadAudioResponseDto": { + "properties": { + "url": { + "type": "string", + "description": "The temporary URL of the audio.\nThis URL should be provided when creating an animation via the /animations endpoint." + }, + "id": { + "type": "string", + "description": "A unique identifier for the audio.\nThis identifier should be used when deleting the audio via the /audio/{id} endpoint." + }, + "duration": { + "type": "number", + "format": "double", + "description": "The duration of the audio in seconds" + }, + "moderation_metadata": { + "$ref": "#/components/schemas/Record_string.any_", + "description": "The audio moderation results metadata." + } + }, + "required": ["url"], + "type": "object", + "additionalProperties": false + }, + "JsonError": { + "properties": { + "kind": { "type": "string" }, + "description": { "type": "string" }, + "details": {} + }, + "required": ["kind", "description"], + "type": "object", + "additionalProperties": false + } + }, + "securitySchemes": { + "basic": { "type": "http", "scheme": "basic" }, + "bearer": { "type": "http", "scheme": "bearer" } + } + }, + "info": { + "title": "audios", + "version": "1.1.0", + "description": "Audios", + "license": { "name": "ISC" }, + "contact": { "name": "D-ID" } + }, + "openapi": "3.0.0", + "paths": { + "/audios": { + "post": { + "operationId": "Upload an audio", + "responses": { + "201": { + "description": "The audio has been uploaded successfully", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/UploadAudioResponseDto" }, + "examples": { "Example 1": { "value": { "id": "DE8sPnUCMg7MBcFJisx5Z" } } } + } + } + }, + "400": { + "description": "BadRequestError | InvalidFileSizeError | InvalidAudioDurationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "BadRequestError", "description": "invalid audio" } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "402": { + "description": "InsufficientCreditsError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "InsufficientCreditsError", + "description": "not enough credits" + } + } + } + } + } + }, + "415": { + "description": "UnsupportedMimeTypeError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "UnsupportedMimeTypeError", + "description": "The provided mime type is not supported", + "details": { "received": "image/jpg", "supported": ["audio/*", "video/*"] } + } + } + } + } + } + }, + "451": { + "description": "AudioModerationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "AudioModerationError", + "description": "Automatic audio moderation failed - Please try a different audio file" + } + } + } + } + } + } + }, + "description": "Upload an audio file to a temporary storage before creating an animation.\nThe audio is uploaded using `multipart/form-data`; the filename directive is optional and if provided should contain up to 50 valid characters long.\nThe resulting file is stored as a .wav file in a 16kHz sample rate.\n\nValid characters: a-z A-Z 0-9 . _ -\nSupported mime types: \"audio/*, video/*\"\nStorage duration: 24-48H", + "summary": "Upload audio file", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [], + "requestBody": { + "required": false, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "audio": { + "type": "string", + "format": "binary", + "description": "The uploaded audio file, the file must not exceed 6MB in size." + }, + "source_url": { "type": "string" }, + "result_url": { + "type": "string", + "description": "A URL to upload the audio to. If provided, the audio will be uploaded to this URL instead of the default bucket. https presigned URL supported. S3 presigned urls should have the putObject permission with content type audio/wav." + } + } + } + } + } + } + } + }, + "/audios/{id}": { + "delete": { + "operationId": "Delete", + "responses": { + "204": { "description": "OK" }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "NotFoundError", "description": "audio not found" } + } + } + } + } + } + }, + "description": "Delete an audio file", + "summary": "Delete an audio file", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { + "description": "The id of the audio file", + "in": "path", + "name": "id", + "required": true, + "schema": { "type": "string" } + } + ] + } + } + }, + "servers": [{ "url": "https://api.d-id.com/" }] +} diff --git a/server/node-service/src/plugins/twilio/did.spec/clips.json b/server/node-service/src/plugins/twilio/did.spec/clips.json new file mode 100644 index 000000000..0866de0e8 --- /dev/null +++ b/server/node-service/src/plugins/twilio/did.spec/clips.json @@ -0,0 +1,795 @@ +{ + "components": { + "examples": {}, + "headers": {}, + "parameters": {}, + "requestBodies": {}, + "responses": {}, + "schemas": { + "ClipActorDto": { + "properties": { + "id": { "type": "string", "description": "The id of the actor", "example": "amy" }, + "gender": { + "type": "string", + "description": "The gender of the presenter.\nmale / female / other" + }, + "created_at": { "type": "string", "description": "Creation time as iso-8601 string" }, + "modified_at": { + "type": "string", + "description": "Last modified time as iso-8601 string" + }, + "image_url": { "type": "string", "description": "High resolution image of the actor" }, + "thumbnail_url": { + "type": "string", + "description": "A low resolution image representing the actor" + } + }, + "required": ["id", "gender", "created_at", "modified_at", "image_url", "thumbnail_url"], + "type": "object", + "additionalProperties": true + }, + "GetClipsActorsResponse": { + "properties": { + "actors": { "items": { "$ref": "#/components/schemas/ClipActorDto" }, "type": "array" }, + "token": { "type": "string" } + }, + "required": ["actors"], + "type": "object", + "additionalProperties": true + }, + "ClipDriverDto": { + "properties": { + "presenter_id": { "type": "string", "example": "amy-zjBKSuTooU" }, + "driver_id": { "type": "string", "example": "a5PgMbeGQE" }, + "gender": { + "type": "string", + "description": "The gender of the presenter.\nmale / female / other" + }, + "created_at": { "type": "string", "description": "Creation time as iso-8601 string" }, + "owner_id": { "type": "string", "description": "The owner id of the driver" }, + "modified_at": { + "type": "string", + "description": "Last modified time as iso-8601 string" + }, + "width": { + "type": "number", + "format": "double", + "description": "The video width in pixels" + }, + "height": { + "type": "number", + "format": "double", + "description": "The video height in pixels" + }, + "driver_image_url": { + "type": "string", + "description": "High resolution image of the driver image" + }, + "thumbnail_url": { + "type": "string", + "description": "A low resolution image representing the presenter in the video" + }, + "video_url": { + "type": "string", + "description": "A short video, trimmed from the original driver in high resolution" + }, + "preview_url": { + "type": "string", + "description": "A short gif, trimmed from the original driver in low resolution" + } + }, + "required": [ + "presenter_id", + "driver_id", + "gender", + "created_at", + "owner_id", + "modified_at", + "width", + "height", + "driver_image_url", + "thumbnail_url", + "video_url", + "preview_url" + ], + "type": "object", + "additionalProperties": true + }, + "GetClipsDriversResponse": { + "properties": { + "clips_drivers": { + "items": { "$ref": "#/components/schemas/ClipDriverDto" }, + "type": "array" + }, + "token": { "type": "string" } + }, + "required": ["clips_drivers"], + "type": "object", + "additionalProperties": true + }, + "ClipStatus": { + "enum": ["created", "done", "error", "started", "rejected"], + "type": "string" + }, + "CreateClipResponse": { + "properties": { + "id": { "type": "string", "description": "The ID of the clip" }, + "object": { + "type": "string", + "enum": ["clip"], + "nullable": false, + "description": "An identifier of this clip" + }, + "created_at": { + "type": "string", + "description": "Clip creation time as iso-8601 string" + }, + "status": { + "$ref": "#/components/schemas/ClipStatus", + "description": "The status of the clip" + } + }, + "required": ["id", "object", "created_at", "status"], + "type": "object", + "additionalProperties": true + }, + "JsonError": { + "properties": { + "kind": { "type": "string" }, + "description": { "type": "string" }, + "details": {} + }, + "required": ["kind", "description"], + "type": "object", + "additionalProperties": true + }, + "VoiceConfigMicrosoft": { + "properties": { + "style": { + "type": "string", + "description": "The style of the voice.\nAvailable styles change between voices." + }, + "rate": { + "type": "string", + "description": "The speed of the voice.\nThe value is relative to 1, 0.5 being half speed, 2 being twice as fast, etc.\nAnother option is a constant value from x-slow/slow/medium/fast/x-fast.", + "example": "0.5" + }, + "pitch": { + "type": "string", + "description": "The pitch of the voice.\nValue could be an absolute value in Hz (including units), a relative value in Hz or st(semitones)\nor a constant value from x-low/low/medium/high/x-high.", + "example": "+2st" + } + }, + "type": "object", + "additionalProperties": true + }, + "Microsoft_tts_provider": { + "description": "AzureMicrosoft provider details, contains the provider type and requested voice id and style", + "properties": { + "type": { "type": "string", "enum": ["microsoft"], "nullable": false }, + "voice_id": { + "type": "string", + "description": "The voice_id from the list of available voices.\nFor full list of voice_ids: https://d-id.readme.io/reference/microsoft-azure", + "example": "Jenny", + "default": "Jenny" + }, + "voice_config": { + "$ref": "#/components/schemas/VoiceConfigMicrosoft", + "description": "Voice customization options" + } + }, + "required": ["type", "voice_id"], + "type": "object", + "additionalProperties": true + }, + "TextToSpeechProviders": { "$ref": "#/components/schemas/Microsoft_tts_provider" }, + "TextScript": { + "description": "Text script contains an input text to be spoken by the actor", + "properties": { + "type": { + "type": "string", + "enum": ["text"], + "nullable": false, + "description": "The type of the script." + }, + "provider": { + "$ref": "#/components/schemas/Microsoft_tts_provider", + "description": "text-to-speech provider from list of supported providers. default is microsoft tts" + }, + "input": { + "type": "string", + "description": "The input text that will be synthesized to an audio file.\nNote that each provider has its own limitations on the text length.", + "example": "This is an example text", + "maxLength": 40000, + "minLength": 3 + }, + "ssml": { + "type": "boolean", + "description": "Is the text provided in ssml form.", + "default": "false" + } + }, + "required": ["type", "input"], + "type": "object", + "additionalProperties": true + }, + "AudioUrl": { + "type": "string", + "example": "https://path.to/audio.mp3", + "pattern": "^(https|s3):\\/\\/[\\S]+\\.(?:m4a|M4A|flac|FLAC|mp3|MP3|mp4|MP4|wav|WAV)([?#][\\w.\\/=&#%-]+)?$" + }, + "ScriptType": { "type": "string", "enum": ["text", "audio"] }, + "AudioScript": { + "description": "Audio script contains an audio file url to be spoken by the actor", + "properties": { + "type": { + "type": "string", + "enum": ["audio"], + "nullable": false, + "description": "The type of the script." + }, + "audio_url": { + "$ref": "#/components/schemas/AudioUrl", + "description": "The URL of the audio file which will be used by the actor.\nFile size is limit to 15MB." + } + }, + "required": ["type", "audio_url"], + "type": "object", + "additionalProperties": true + }, + "Script": { + "anyOf": [ + { "$ref": "#/components/schemas/TextScript" }, + { "$ref": "#/components/schemas/AudioScript" } + ] + }, + "Logo": { + "properties": { + "url": { + "type": "string", + "description": "https url to an ARGB jpg/png image, a default logo is used otherwise", + "pattern": "((https|s3):\\/\\/.+)" + }, + "position": { + "items": { "type": "integer", "format": "int32" }, + "type": "array", + "description": "position of the logo in pixels from the top left corner (w,h) negative values are subtracted from last pixel", + "example": [0, 500], + "minItems": 2, + "maxItems": 2 + } + }, + "required": ["url", "position"], + "type": "object", + "additionalProperties": true + }, + "ClipConfig": { + "properties": { + "logo": { "anyOf": [{ "$ref": "#/components/schemas/Logo" }, { "type": "boolean" }] }, + "result_format": { + "type": "string", + "enum": ["mp4", "gif", "mov", "webm"], + "description": "File format of the animated result", + "default": "mp4" + } + }, + "type": "object", + "additionalProperties": true + }, + "CropType": { "type": "string", "enum": ["rectangle"], "nullable": false }, + "RectangleCrop": { + "description": "Custom crop", + "properties": { + "type": { + "type": "string", + "enum": ["rectangle"], + "nullable": false, + "description": "The type of the crop." + }, + "rectangle": { + "properties": { + "bottom": { "type": "number", "format": "double" }, + "right": { "type": "number", "format": "double" }, + "left": { "type": "number", "format": "double" }, + "top": { "type": "number", "format": "double" } + }, + "required": ["bottom", "right", "left", "top"], + "type": "object" + } + }, + "required": ["type", "rectangle"], + "type": "object", + "additionalProperties": true + }, + "Crop": { "$ref": "#/components/schemas/RectangleCrop" }, + "ClipPresenterConfig": { + "properties": { "crop": { "$ref": "#/components/schemas/RectangleCrop" } }, + "type": "object", + "additionalProperties": true + }, + "ClipBackground": { + "properties": { + "color": { + "anyOf": [{ "type": "string" }, { "type": "boolean", "enum": [false] }], + "description": "Background color of the animated result, or false to use transparent background in-case of webm result format.", + "example": "#47ffff", + "pattern": "^#[a-fA-F0-9]{6}$" + } + }, + "type": "object", + "additionalProperties": true + }, + "UserData": { + "type": "string", + "minLength": 1, + "maxLength": 500, + "pattern": "^(?!\\s*$).+$" + }, + "SecuredUrl": { "type": "string", "pattern": "^(?:https):\\/\\/[\\S]+$" }, + "RemoteUrl": { + "type": "string", + "example": "https://path.to.directory/movie.mp4", + "pattern": "(https|s3):\\/\\/.+" + }, + "CreateClipRequest": { + "properties": { + "presenter_id": { + "type": "string", + "description": "a selection from the list or provided driver ids.", + "example": "amy-zjBKSuTooU" + }, + "driver_id": { + "type": "string", + "description": "a selection from the list or provided driver ids.\nIf not provided a driver video will be selected for you from the predefined drivers bank.", + "example": "a5PgMbeGQE", + "default": "a5PgMbeGQE" + }, + "script": { "$ref": "#/components/schemas/Script" }, + "config": { + "$ref": "#/components/schemas/ClipConfig", + "description": "Advanced configuration options." + }, + "created_by": { "type": "string", "description": "The user created the clip." }, + "presenter_config": { + "$ref": "#/components/schemas/ClipPresenterConfig", + "description": "Advanced presenter configuration options." + }, + "background": { + "$ref": "#/components/schemas/ClipBackground", + "description": "Background color of the clip result" + }, + "user_data": { + "$ref": "#/components/schemas/UserData", + "description": "Non-sensitive custom data that will be added to the clip response and webhook." + }, + "name": { "type": "string", "description": "The name of the clip" }, + "webhook": { + "$ref": "#/components/schemas/SecuredUrl", + "description": "A webhook URL for sending the payload including animate details.\nIn a case of empty value, the webhook will not be triggered.", + "example": "https://host.domain.tld/to/webhook" + }, + "result_url": { + "$ref": "#/components/schemas/RemoteUrl", + "description": "The URL of the clip video, if not provided use default destination.", + "example": "https://path.to.directory/" + } + }, + "required": ["presenter_id", "driver_id", "script"], + "type": "object", + "additionalProperties": true + }, + "Record_string.any_": { + "properties": {}, + "type": "object", + "description": "Construct a type with a set of properties K of type T" + }, + "GetClipResponse": { + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object" }, + "owner_id": { + "type": "string", + "description": "Unique identifier of the owner that submitted the clip" + }, + "audio_url": { + "$ref": "#/components/schemas/SecuredUrl", + "description": "The URL of the audio file which will be used by the actor", + "example": "https://path.to/audio.mp3" + }, + "created_at": { + "type": "string", + "description": "Clip creation time as iso-8601 string" + }, + "created_by": { "type": "string", "description": "The user id that created the clip" }, + "modified_at": { + "type": "string", + "description": "last modified time as iso-8601 string" + }, + "started_at": { "type": "string", "description": "Clip start time as iso-8601 string" }, + "completed_at": { + "type": "string", + "description": "Clip completion time as iso-8601 string" + }, + "config": { + "$ref": "#/components/schemas/ClipConfig", + "description": "The configuration that used to process the clip" + }, + "status": { + "$ref": "#/components/schemas/ClipStatus", + "description": "The status of the clip" + }, + "result_url": { "type": "string", "description": "The result url of the clip" }, + "presenter_id": { + "type": "string", + "description": "The identity of the actor that is used in the clip" + }, + "driver_id": { + "type": "string", + "description": "The identifier of the presenter's acting driver that is used in the clip" + }, + "metadata": { + "$ref": "#/components/schemas/Record_string.any_", + "description": "metadata that has been collected through the process" + }, + "webhook": { + "type": "string", + "description": "A webhook URL for sending the payload including animate details. In a case of empty value, the webhook will not be triggered" + }, + "name": { "type": "string", "description": "The name of the clip" } + }, + "required": ["id", "owner_id", "created_at", "modified_at", "status"], + "type": "object", + "additionalProperties": true + }, + "GetClipsResponse": { + "properties": { + "clips": { "items": { "$ref": "#/components/schemas/GetClipResponse" }, "type": "array" }, + "token": { "type": "string" } + }, + "required": ["clips"], + "type": "object", + "additionalProperties": true + }, + "UpdateClipFields": { + "properties": { "name": { "type": "string" } }, + "type": "object", + "additionalProperties": true + } + }, + "securitySchemes": { + "basic": { "type": "http", "scheme": "basic" }, + "bearer": { "type": "http", "scheme": "bearer" } + } + }, + "info": { + "title": "clips", + "version": "1.1.0", + "description": "Clips", + "license": { "name": "ISC" }, + "contact": { "name": "D-ID" } + }, + "openapi": "3.0.0", + "paths": { + "/clips/actors": { + "get": { + "operationId": "GetActors", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetClipsActorsResponse" } + } + } + } + }, + "description": "Get clips actors", + "summary": "Get actors list", + "security": [{ "optional": [] }], + "parameters": [ + { + "description": "pagination - the amount of actors to return", + "in": "query", + "name": "limit", + "required": false, + "schema": { "default": 100, "format": "double", "type": "number" } + }, + { + "description": "pagination - the pagination token from the previous response, default = 100", + "in": "query", + "name": "token", + "required": false, + "schema": { "type": "string" } + } + ] + } + }, + "/clips/actors/{id}/drivers": { + "get": { + "operationId": "GetActorDrivers", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetClipsDriversResponse" } + } + } + } + }, + "description": "Get clips actor drivers", + "summary": "Get driver list of an actor", + "security": [{ "optional": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } }, + { + "description": "pagination - the amount of actors to return", + "in": "query", + "name": "limit", + "required": false, + "schema": { "default": 100, "format": "double", "type": "number" } + }, + { + "description": "pagination - the pagination token from the previous response, default = 100", + "in": "query", + "name": "token", + "required": false, + "schema": { "type": "string" } + } + ] + } + }, + "/clips": { + "post": { + "operationId": "Create", + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateClipResponse" }, + "examples": { + "Example 1": { + "value": { + "id": "clp_s4d-SZd2xs", + "object": "clip", + "created_at": "2020-09-03T13:56:54.995", + "status": "created" + } + } + } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "BadRequestError", "description": "invalid actor url" } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "402": { + "description": "InsufficientCreditsError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "InsufficientCreditsError", + "description": "not enough credits" + } + } + } + } + } + }, + "403": { + "description": "PermissionError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "PermissionError", + "description": "user has no permission for stitch" + } + } + } + } + } + }, + "451": { + "description": "ImageModerationError | CelebrityRecognizedError | TextModerationError | AudioModerationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "ImageModerationError", + "description": "Automatic content moderation - contact support if you would like to submit for manual review" + } + } + } + } + } + } + }, + "description": "Create a clip", + "summary": "Create a clip", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/CreateClipRequest" } } + } + } + }, + "get": { + "operationId": "GetMany", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/GetClipsResponse" } } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + } + }, + "description": "Get clips", + "summary": "Get clips", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { + "description": "pagination - the amount of clips to return", + "in": "query", + "name": "limit", + "required": false, + "schema": { "default": 100, "format": "double", "type": "number" } + }, + { + "description": "pagination - the pagination token from the previous response, default = 100", + "in": "query", + "name": "token", + "required": false, + "schema": { "type": "string" } + } + ] + } + }, + "/clips/{id}": { + "get": { + "operationId": "GetById", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/GetClipResponse" } } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { "value": { "kind": "NotFoundError", "description": "not found" } } + } + } + } + } + }, + "description": "Get a specific clip", + "summary": "Get a specific clip", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } + ] + }, + "delete": { + "operationId": "Delete", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/GetClipResponse" } } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { "value": { "kind": "NotFoundError", "description": "not found" } } + } + } + } + }, + "409": { + "description": "NotReadyError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "NotReadyError", + "description": "clip is in process, try again later" + } + } + } + } + } + } + }, + "description": "Delete a specific clip", + "summary": "Delete a specific clip", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } + ] + } + } + }, + "servers": [{ "url": "https://api.d-id.com/" }] +} diff --git a/server/node-service/src/plugins/twilio/did.spec/credits.json b/server/node-service/src/plugins/twilio/did.spec/credits.json new file mode 100644 index 000000000..f997d277d --- /dev/null +++ b/server/node-service/src/plugins/twilio/did.spec/credits.json @@ -0,0 +1,162 @@ +{ + "components": { + "examples": {}, + "headers": {}, + "parameters": {}, + "requestBodies": {}, + "responses": {}, + "schemas": { + "CreditsItem": { + "properties": { + "owner_id": { "type": "string" }, + "created_at": { "type": "string" }, + "modified_at": { "type": "string" }, + "remaining": { "type": "number", "format": "double" }, + "total": { "type": "number", "format": "double" }, + "expire_at": { "type": "string" }, + "valid_from": { "type": "string" }, + "product_id": { "type": "string" }, + "product_billing_interval": { "type": "string", "enum": ["month", "year"] } + }, + "required": ["owner_id", "created_at", "modified_at", "remaining", "total", "expire_at"], + "type": "object", + "additionalProperties": false + }, + "UserCredits": { + "properties": { + "credits": { "items": { "$ref": "#/components/schemas/CreditsItem" }, "type": "array" }, + "remaining": { "type": "number", "format": "double" }, + "total": { "type": "number", "format": "double" } + }, + "required": ["credits", "remaining", "total"], + "type": "object", + "additionalProperties": false + }, + "JsonError": { + "properties": { + "kind": { "type": "string" }, + "description": { "type": "string" }, + "details": {} + }, + "required": ["kind", "description"], + "type": "object", + "additionalProperties": false + } + }, + "securitySchemes": { + "basic": { "type": "http", "scheme": "basic" }, + "bearer": { "type": "http", "scheme": "bearer" } + } + }, + "info": { + "title": "credits", + "version": "1.1.0", + "description": "information regarding your D-ID API credits", + "license": { "name": "ISC" }, + "contact": { "name": "D-ID" } + }, + "openapi": "3.0.0", + "paths": { + "/credits": { + "get": { + "operationId": "Get", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/UserCredits" }, + "examples": { + "Example 1": { + "value": { + "credits": [ + { + "owner_id": "52907745-7672-470e-a803-a2f8feb52944", + "remaining": 10000, + "total": 10000, + "expire_at": "2022-11-01T08:04:43.587Z" + } + ], + "remaining": 10000, + "total": 10000 + } + } + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "NotFoundError", + "description": "not credits found for user" + } + } + } + } + } + } + }, + "description": "Gets the remaning credits object for the authenticated user", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [] + } + }, + "/credits/all": { + "get": { + "operationId": "GetAll", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/UserCredits" }, + "examples": { + "Example 1": { + "value": { + "credits": [ + { + "owner_id": "52907745-7672-470e-a803-a2f8feb52944", + "remaining": 10000, + "total": 10000, + "expire_at": "2022-11-01T08:04:43.587Z" + } + ], + "remaining": 10000, + "total": 10000 + } + } + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "NotFoundError", + "description": "not credits found for user" + } + } + } + } + } + } + }, + "description": "Gets the remaning credits object for the authenticated user, including inactive credits", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [] + } + } + }, + "servers": [{ "url": "https://api.d-id.com/" }] +} diff --git a/server/node-service/src/plugins/twilio/did.spec/images.json b/server/node-service/src/plugins/twilio/did.spec/images.json new file mode 100644 index 000000000..3089fcf95 --- /dev/null +++ b/server/node-service/src/plugins/twilio/did.spec/images.json @@ -0,0 +1,247 @@ +{ + "components": { + "examples": {}, + "headers": {}, + "parameters": {}, + "requestBodies": {}, + "responses": {}, + "schemas": { + "OverlapKind": { "enum": ["NO", "PARTIAL", "YES", "UNKNOWN"], "type": "string" }, + "Rect": { + "properties": { + "top": { "type": "number", "format": "double" }, + "left": { "type": "number", "format": "double" }, + "bottom": { "type": "number", "format": "double" }, + "right": { "type": "number", "format": "double" } + }, + "required": ["top", "left", "bottom", "right"], + "type": "object", + "additionalProperties": false + }, + "FaceSquare": { + "properties": { + "size": { + "type": "integer", + "format": "int32", + "description": "Size of the square side", + "minimum": 0 + }, + "top_left": { + "items": { "type": "integer", "format": "int32" }, + "type": "array", + "description": "Top left location of the face in the frame - can be negative", + "minItems": 2, + "maxItems": 2 + }, + "overlap": { "$ref": "#/components/schemas/OverlapKind" }, + "face_id": { "type": "string" }, + "detect_confidence": { "type": "number", "format": "double" }, + "detection": { "$ref": "#/components/schemas/Rect" } + }, + "required": ["size", "top_left"], + "type": "object", + "additionalProperties": false + }, + "UploadImageResponseDto": { + "properties": { + "url": { + "type": "string", + "description": "The temporary URL of the image.\nThis URL should be provided when creating an animation via the /animations endpoint." + }, + "id": { + "type": "string", + "description": "A unique identifier which represents this image operation" + }, + "faces": { "items": { "$ref": "#/components/schemas/FaceSquare" }, "type": "array" } + }, + "required": ["url"], + "type": "object", + "additionalProperties": false + }, + "JsonError": { + "properties": { + "kind": { "type": "string" }, + "description": { "type": "string" }, + "details": {} + }, + "required": ["kind", "description"], + "type": "object", + "additionalProperties": false + } + }, + "securitySchemes": { + "basic": { "type": "http", "scheme": "basic" }, + "bearer": { "type": "http", "scheme": "bearer" } + } + }, + "info": { + "title": "images", + "version": "1.3.0", + "description": "Images", + "license": { "name": "ISC" }, + "contact": { "name": "D-ID" } + }, + "openapi": "3.0.0", + "paths": { + "/images": { + "post": { + "operationId": "Upload an image", + "responses": { + "201": { + "description": "The image has been uploaded successfully", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/UploadImageResponseDto" }, + "examples": { "Example 1": { "value": { "id": "tn8607bms7" } } } + } + } + }, + "400": { + "description": "BadRequestError | InvalidFileSizeError | InvalidImageResolutionError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "BadRequestError", "description": "invalid image" } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "402": { + "description": "InsufficientCreditsError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "InsufficientCreditsError", + "description": "not enough credits" + } + } + } + } + } + }, + "415": { + "description": "UnsupportedMimeTypeError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "UnsupportedMimeTypeError", + "description": "The provided mime type is not supported", + "details": { "received": "image/png", "supported": ["image/jpeg"] } + } + } + } + } + } + }, + "451": { + "description": "ImageModerationError | CelebrityRecognizedError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "ImageModerationError", + "description": "Automatic content moderation - contact support if you would like to submit for manual review" + } + } + } + } + } + } + }, + "description": "Upload an image to a temporary storage before creating an animation.\nThe image is uploaded using `multipart/form-data`; the filename directive is optional and if provided should contain up to 50 valid characters long.\n\nValid characters: a-z A-Z 0-9 . _ -\nSupported mime types: image/jpeg,image/png\nStorage duration: 24-48H", + "summary": "Upload image", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [], + "requestBody": { + "required": false, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "image": { + "type": "string", + "format": "binary", + "description": "The binary data of the image" + }, + "detect_faces": { "type": "string" }, + "metadata": { "type": "string" }, + "source_url": { "type": "string" }, + "result_url": { + "type": "string", + "description": "A URL to upload the image to. If provided, the image will be uploaded to this URL instead of the default bucket. Should be an https presigned URL. S3 presigned urls should have the putObject permission with content type matching the uploaded image." + } + } + } + } + } + } + } + }, + "/images/{id}": { + "delete": { + "operationId": "Delete", + "responses": { + "204": { "description": "OK" }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "NotFoundError", "description": "image not found" } + } + } + } + } + } + }, + "description": "Delete an image", + "summary": "Delete image", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } + ] + } + } + }, + "servers": [{ "url": "https://api.d-id.com/" }] +} diff --git a/server/node-service/src/plugins/twilio/did.spec/settings.json b/server/node-service/src/plugins/twilio/did.spec/settings.json new file mode 100644 index 000000000..664127182 --- /dev/null +++ b/server/node-service/src/plugins/twilio/did.spec/settings.json @@ -0,0 +1,130 @@ +{ + "components": { + "examples": {}, + "headers": {}, + "parameters": {}, + "requestBodies": {}, + "responses": {}, + "schemas": { + "JsonError": { + "properties": { + "kind": { "type": "string" }, + "description": { "type": "string" }, + "details": {} + }, + "required": ["kind", "description"], + "type": "object", + "additionalProperties": false + } + }, + "securitySchemes": { + "basic": { "type": "http", "scheme": "basic" }, + "bearer": { "type": "http", "scheme": "bearer" } + } + }, + "info": { + "title": "settings", + "version": "0.0.1", + "description": "user related settings", + "license": { "name": "ISC" }, + "contact": { "name": "D-ID" } + }, + "openapi": "3.0.0", + "paths": { + "/settings/logo": { + "post": { + "operationId": "UpdateLogo", + "responses": { + "201": { "description": "The image has been uploaded successfully" }, + "400": { + "description": "BadRequestError | InvalidFileSizeError | InvalidImageResolutionError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "BadRequestError", "description": "invalid image" } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + }, + "415": { + "description": "The image being uploaded is not supported", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "UnsupportedMimeTypeError", + "description": "The provided mime type is not supported", + "details": { "received": "image/gif" } + } + } + } + } + } + } + }, + "description": "Updates user logo", + "summary": "Updates user logo", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "logo": { "type": "string", "format": "binary" }, + "top": { "type": "string" }, + "left": { "type": "string" } + }, + "required": ["logo", "top", "left"] + } + } + } + } + }, + "delete": { + "operationId": "DeleteLogo", + "responses": { + "204": { "description": "The image has been deleted successfully" }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "AuthorizationError", "description": "user unauthenticated" } + } + } + } + } + } + }, + "description": "Deletes user logo", + "summary": "Deletes user logo", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [] + } + } + }, + "servers": [{ "url": "https://api.d-id.com/" }] +} diff --git a/server/node-service/src/plugins/twilio/did.spec/talks.json b/server/node-service/src/plugins/twilio/did.spec/talks.json new file mode 100644 index 000000000..e66f73b6e --- /dev/null +++ b/server/node-service/src/plugins/twilio/did.spec/talks.json @@ -0,0 +1,1159 @@ +{ + "components": { + "examples": {}, + "headers": {}, + "parameters": {}, + "requestBodies": {}, + "responses": {}, + "schemas": { + "TalkStatus": { + "enum": ["created", "done", "error", "started", "rejected"], + "type": "string" + }, + "CreateTalkResponseDto": { + "properties": { + "id": { "type": "string", "description": "The ID of the talk" }, + "object": { "type": "string", "description": "An identifier of this talk" }, + "created_by": { + "type": "string", + "description": "The user id of the user that created the talk" + }, + "created_at": { + "type": "string", + "description": "Talk creation time as iso-8601 string" + }, + "status": { + "$ref": "#/components/schemas/TalkStatus", + "description": "The status of the talk" + } + }, + "required": ["id", "object", "created_by", "created_at", "status"], + "type": "object", + "additionalProperties": true + }, + "JsonError": { + "properties": { + "kind": { "type": "string" }, + "description": { "type": "string" }, + "details": {} + }, + "required": ["kind", "description"], + "type": "object", + "additionalProperties": true + }, + "ImagePath": { + "type": "string", + "example": "https://path.to.directory/image.jpg", + "pattern": "(https|s3):.*\\.(?:jpg|jpeg|png|JPG|JPEG|PNG)" + }, + "DriverUrl": { "type": "string", "pattern": "(https|s3|bank):\\/\\/.+" }, + "VoiceConfigMicrosoft": { + "properties": { + "style": { + "type": "string", + "description": "The style of the voice.\nAvailable styles change between voices." + }, + "rate": { + "type": "string", + "description": "The speed of the voice.\nThe value is relative to 1, 0.5 being half speed, 2 being twice as fast, etc.\nAnother option is a constant value from x-slow/slow/medium/fast/x-fast.", + "example": "0.5" + }, + "pitch": { + "type": "string", + "description": "The pitch of the voice.\nValue could be an absolute value in Hz (including units), a relative value in Hz or st(semitones)\nor a constant value from x-low/low/medium/high/x-high.", + "example": "+2st" + } + }, + "type": "object", + "additionalProperties": true + }, + "Microsoft_tts_provider": { + "description": "AzureMicrosoft provider details, contains the provider type and requested voice id and style", + "properties": { + "type": { "type": "string", "enum": ["microsoft"], "nullable": false }, + "voice_id": { + "type": "string", + "description": "The voice_id from the list of available voices.\nFor full list of voice_ids: https://d-id.readme.io/reference/microsoft-azure", + "example": "Jenny", + "default": "Jenny" + }, + "voice_config": { + "$ref": "#/components/schemas/VoiceConfigMicrosoft", + "description": "Voice customization options" + } + }, + "required": ["type", "voice_id"], + "type": "object", + "additionalProperties": true + }, + "VoiceIdsAmazon": { + "type": "string", + "enum": [ + "Amy", + "Aria", + "Ayanda", + "Bianca", + "Brian", + "Camila", + "Emma", + "Gabrielle", + "Ivy", + "Joanna", + "Joey", + "Justin", + "Kendra", + "Kevin", + "Kimberly", + "Lea", + "Lucia", + "Lupe", + "Matthew", + "Olivia", + "Salli", + "Seoyeon", + "Takumi", + "Vicki" + ], + "nullable": false + }, + "Amazon_tts_provider": { + "description": "Amazon provider details, contains the provider type and requested voice id", + "properties": { + "type": { "type": "string", "enum": ["amazon"], "nullable": false }, + "voice_id": { + "$ref": "#/components/schemas/VoiceIdsAmazon", + "description": "The voice_id from the list of available voices.\nFor full list of voice_ids: https://d-id.readme.io/reference/text-to-speech-providers", + "example": "Joanna" + } + }, + "required": ["type", "voice_id"], + "type": "object", + "additionalProperties": true + }, + "TextToSpeechProviders": { + "anyOf": [ + { "$ref": "#/components/schemas/Microsoft_tts_provider" }, + { "$ref": "#/components/schemas/Amazon_tts_provider" } + ] + }, + "TextScript": { + "description": "Text script contains an input text to be spoken by the actor", + "properties": { + "type": { + "type": "string", + "enum": ["text"], + "nullable": false, + "description": "The type of the script." + }, + "provider": { + "$ref": "#/components/schemas/TextToSpeechProviders", + "description": "text-to-speech provider from list of supported providers. default is microsoft tts" + }, + "input": { + "type": "string", + "description": "The input text that will be synthesized to an audio file.\nNote that each provider has its own limitations on the text length.", + "example": "This is an example text", + "maxLength": 40000, + "minLength": 3 + }, + "ssml": { + "type": "boolean", + "description": "Is the text provided in ssml form.", + "default": "false" + } + }, + "required": ["type", "input"], + "type": "object", + "additionalProperties": true + }, + "AudioUrl": { + "type": "string", + "example": "https://path.to/audio.mp3", + "pattern": "^(https|s3):\\/\\/[\\S]+\\.(?:m4a|M4A|flac|FLAC|mp3|MP3|mp4|MP4|wav|WAV)([?#][\\w.\\/=&#%-]+)?$" + }, + "ScriptType": { "type": "string", "enum": ["text", "audio"] }, + "AudioScript": { + "description": "Audio script contains an audio file url to be spoken by the actor", + "properties": { + "type": { + "type": "string", + "enum": ["audio"], + "nullable": false, + "description": "The type of the script." + }, + "audio_url": { + "$ref": "#/components/schemas/AudioUrl", + "description": "The URL of the audio file which will be used by the actor.\nFile size is limit to 15MB." + } + }, + "required": ["type", "audio_url"], + "type": "object", + "additionalProperties": true + }, + "Script": { + "anyOf": [ + { "$ref": "#/components/schemas/TextScript" }, + { "$ref": "#/components/schemas/AudioScript" } + ] + }, + "Logo": { + "properties": { + "url": { + "type": "string", + "description": "https url to an ARGB jpg/png image, a default logo is used otherwise", + "pattern": "((https|s3):\\/\\/.+)" + }, + "position": { + "items": { "type": "integer", "format": "int32" }, + "type": "array", + "description": "position of the logo in pixels from the top left corner (w,h) negative values are subtracted from last pixel", + "example": [0, 500], + "minItems": 2, + "maxItems": 2 + } + }, + "required": ["url", "position"], + "type": "object", + "additionalProperties": true + }, + "ResultFormat": { "type": "string", "enum": ["mp4", "gif", "mov"] }, + "TalksConfig": { + "properties": { + "logo": { "anyOf": [{ "$ref": "#/components/schemas/Logo" }, { "type": "boolean" }] }, + "stitch": { + "type": "boolean", + "description": "Stitch back the animated result to the original image" + }, + "result_format": { + "$ref": "#/components/schemas/ResultFormat", + "description": "File format of the animated result" + }, + "fluent": { + "type": "boolean", + "description": "Interpolate between the last & first frames of the driver video\nWhen used together with `pad_audio` can create a seamless transition between videos of the same driver", + "default": "false" + }, + "pad_audio": { + "type": "number", + "format": "double", + "description": "Pad the audio with silence at the end (given in seconds)\nWill increase the video duration & the credits it consumes", + "default": "0.0", + "minimum": 0, + "maximum": 60 + } + }, + "type": "object", + "additionalProperties": true + }, + "UserData": { + "type": "string", + "minLength": 1, + "maxLength": 500, + "pattern": "^(?!\\s*$).+$" + }, + "SecuredUrl": { "type": "string", "pattern": "^(?:https):\\/\\/[\\S]+$" }, + "RemoteUrl": { + "type": "string", + "example": "https://path.to.directory/movie.mp4", + "pattern": "(https|s3):\\/\\/.+" + }, + "OverlapKind": { "enum": ["NO", "PARTIAL", "YES", "UNKNOWN"], "type": "string" }, + "Rect": { + "properties": { + "top": { "type": "number", "format": "double" }, + "left": { "type": "number", "format": "double" }, + "bottom": { "type": "number", "format": "double" }, + "right": { "type": "number", "format": "double" } + }, + "required": ["top", "left", "bottom", "right"], + "type": "object", + "additionalProperties": true + }, + "FaceSquareDto": { + "properties": { + "size": { + "type": "integer", + "format": "int32", + "description": "Size of the square side", + "minimum": 0 + }, + "top_left": { + "items": { "type": "integer", "format": "int32" }, + "type": "array", + "description": "Top left location of the face in the frame - can be negative", + "minItems": 2, + "maxItems": 2 + }, + "overlap": { "$ref": "#/components/schemas/OverlapKind" }, + "face_id": { "type": "string" }, + "detect_confidence": { "type": "number", "format": "double" }, + "detection": { "$ref": "#/components/schemas/Rect" } + }, + "required": ["size", "top_left"], + "type": "object", + "additionalProperties": true + }, + "CreateTalkRequest": { + "properties": { + "source_url": { + "$ref": "#/components/schemas/ImagePath", + "description": "The URL of the source image to be animated by the driver video, or a selection from the list of provided\nstudio actors." + }, + "driver_url": { + "$ref": "#/components/schemas/DriverUrl", + "description": "The URL of the driver video to drive the talk, or a selection from the list or provided drivers\nIf not provided a driver video will be selected for you from the predefined drivers bank", + "example": "bank://lively" + }, + "script": { "$ref": "#/components/schemas/Script" }, + "config": { + "$ref": "#/components/schemas/TalksConfig", + "description": "Advanced configuration options" + }, + "user_data": { + "$ref": "#/components/schemas/UserData", + "description": "Non-sensitive custom data that will be added to the talk response and webhook" + }, + "name": { "type": "string", "description": "The name of the talk video" }, + "webhook": { + "$ref": "#/components/schemas/SecuredUrl", + "description": "A webhook URL for sending the payload including animate details\nIn a case of empty value, the webhook will not be triggered", + "example": "https://host.domain.tld/to/webhook" + }, + "result_url": { + "$ref": "#/components/schemas/RemoteUrl", + "description": "The URL of the talk video, if not provided use default destination.", + "example": "https://path.to.directory/" + }, + "face": { + "$ref": "#/components/schemas/FaceSquareDto", + "description": "The face to animate - otherwise detects the face automatically", + "example": { "top_left": [0, 0], "size": 512 } + } + }, + "required": ["source_url", "script"], + "type": "object", + "additionalProperties": true + }, + "Record_string.any_": { + "properties": {}, + "type": "object", + "description": "Construct a type with a set of properties K of type T" + }, + "GetTalkDto": { + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object" }, + "user_id": { + "type": "string", + "description": "Unique identifier of the user that submitted the talk" + }, + "source_url": { + "type": "string", + "description": "The URL of the source image to be animated by the driver video. Image type of .jpg | .png" + }, + "driver_url": { + "type": "string", + "description": "The URL of the driver video to drive the talk, if not provided a driver video will be selected for you from the predefined DriversBank" + }, + "created_at": { + "type": "string", + "description": "Talk creation time as iso-8601 string" + }, + "created_by": { + "type": "string", + "description": "The user id of the user that created the talk" + }, + "audio_url": { + "$ref": "#/components/schemas/SecuredUrl", + "description": "The URL of the audio file which will be used by the actor", + "example": "https://path.to/audio.mp3" + }, + "started_at": { "type": "string", "description": "Talk start time as iso-8601 string" }, + "modified_at": { + "type": "string", + "description": "last modified time as iso-8601 string" + }, + "status": { + "$ref": "#/components/schemas/TalkStatus", + "description": "The status of the talk" + }, + "result_url": { "type": "string", "description": "s3 uri to the resulting video" }, + "metadata": { + "$ref": "#/components/schemas/Record_string.any_", + "description": "metadata that has been collected through the process" + }, + "webhook": { + "type": "string", + "description": "A webhook URL for sending the payload including animate details. In a case of empty value, the webhook will not be triggered" + }, + "config": { + "$ref": "#/components/schemas/TalksConfig", + "description": "The configuration that used to process the talk" + } + }, + "required": ["id", "user_id", "source_url", "created_at", "modified_at", "status"], + "type": "object", + "additionalProperties": true + }, + "GetTalksDto": { + "properties": { + "talks": { "items": { "$ref": "#/components/schemas/GetTalkDto" }, "type": "array" }, + "token": { "type": "string" } + }, + "required": ["talks"], + "type": "object", + "additionalProperties": true + }, + "UpdateTalkFields": { + "properties": { "name": { "type": "string" } }, + "type": "object", + "additionalProperties": true + }, + "Jsep": { + "properties": { + "type": { "type": "string", "enum": ["offer", "answer"] }, + "sdp": { "type": "string" } + }, + "required": ["type", "sdp"], + "type": "object", + "additionalProperties": true + }, + "IceServer": { + "properties": { + "urls": { + "anyOf": [{ "items": { "type": "string" }, "type": "array" }, { "type": "string" }] + }, + "username": { "type": "string" }, + "credential": { "type": "string" } + }, + "required": ["urls"], + "type": "object", + "additionalProperties": true + }, + "CreateStreamResponseDto": { + "properties": { + "session_id": { "type": "string" }, + "streamId": { "type": "string", "description": "Id of the stream", "deprecated": true }, + "id": { "type": "string", "description": "Id of the stream" }, + "jsep": { + "$ref": "#/components/schemas/Jsep", + "description": "Jsep offer object used to create a peer connection" + }, + "iceServers": { + "items": { "$ref": "#/components/schemas/IceServer" }, + "type": "array", + "description": "ICE servers used to create a peer connection", + "deprecated": true + }, + "ice_servers": { + "items": { "$ref": "#/components/schemas/IceServer" }, + "type": "array", + "description": "ICE servers used to create a peer connection" + } + }, + "required": ["streamId", "id", "jsep", "iceServers", "ice_servers"], + "type": "object", + "additionalProperties": true + }, + "IceCandidate": { + "properties": { + "candidate": { "type": "string" }, + "sdpMid": { "type": "string" }, + "sdpMLineIndex": { "type": "number", "format": "double" } + }, + "required": ["candidate", "sdpMid", "sdpMLineIndex"], + "type": "object", + "additionalProperties": true + }, + "Pick_TalksConfig.Exclude_keyofTalksConfig.logo__": { + "properties": { + "stitch": { + "type": "boolean", + "description": "Stitch back the animated result to the original image" + }, + "result_format": { + "$ref": "#/components/schemas/ResultFormat", + "description": "File format of the animated result" + }, + "fluent": { + "type": "boolean", + "description": "Interpolate between the last & first frames of the driver video\nWhen used together with `pad_audio` can create a seamless transition between videos of the same driver", + "default": "false" + }, + "pad_audio": { + "type": "number", + "format": "double", + "description": "Pad the audio with silence at the end (given in seconds)\nWill increase the video duration & the credits it consumes", + "default": "0.0", + "minimum": 0, + "maximum": 60 + } + }, + "type": "object", + "description": "From T, pick a set of properties whose keys are in the union K" + }, + "Omit_TalksConfig.logo_": { + "$ref": "#/components/schemas/Pick_TalksConfig.Exclude_keyofTalksConfig.logo__", + "description": "Construct a type with the properties of T except for those in type K." + }, + "CreateTalkStreamRequest": { + "properties": { + "session_id": { "type": "string" }, + "driver_url": { + "$ref": "#/components/schemas/DriverUrl", + "description": "The URL of the driver video to drive the talk, or a selection from the list or provided drivers.\nIf not provided a driver video will be selected for you from the predefined drivers bank.", + "example": "bank://lively" + }, + "script": { "$ref": "#/components/schemas/Script" }, + "config": { + "$ref": "#/components/schemas/Omit_TalksConfig.logo_", + "description": "Advanced configuration options." + }, + "user_data": { + "$ref": "#/components/schemas/UserData", + "description": "Non-sensitive custom data that will be added to the talk response and webhook." + }, + "name": { "type": "string", "description": "The name of the talk video" } + }, + "required": ["script"], + "type": "object", + "additionalProperties": true + } + }, + "securitySchemes": { + "basic": { "type": "http", "scheme": "basic" }, + "bearer": { "type": "http", "scheme": "bearer" } + } + }, + "info": { + "title": "talks", + "version": "1.1.0", + "description": "Talks", + "license": { "name": "ISC" }, + "contact": { "name": "D-ID" } + }, + "openapi": "3.0.0", + "paths": { + "/talks": { + "post": { + "operationId": "Create", + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateTalkResponseDto" }, + "examples": { + "Example 1": { + "value": { + "id": "tlk_AbCs-xcz", + "object": "talk", + "created_at": "2020-09-03T13:56:54.995", + "created_by": "123", + "status": "created" + } + } + } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "BadRequestError", + "description": "invalid actor url" + } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "AuthorizationError", + "description": "user unauthenticated" + } + } + } + } + } + }, + "402": { + "description": "InsufficientCreditsError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "InsufficientCreditsError", + "description": "not enough credits" + } + } + } + } + } + }, + "403": { + "description": "PermissionError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "PermissionError", + "description": "user has no permission for stitch" + } + } + } + } + } + }, + "451": { + "description": "ImageModerationError | CelebrityRecognizedError | TextModerationError | AudioModerationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "ImageModerationError", + "description": "Automatic content moderation - contact support if you would like to submit for manual review" + } + } + } + } + } + } + }, + "description": "Create a talk", + "summary": "Create a talk", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/CreateTalkRequest" } } + } + } + }, + "get": { + "operationId": "GetMany", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/GetTalksDto" } } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "AuthorizationError", + "description": "user unauthenticated" + } + } + } + } + } + } + }, + "description": "Get talks", + "summary": "Get talks", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { + "description": "pagination - the amount of talks to return", + "in": "query", + "name": "limit", + "required": false, + "schema": { "default": 100, "format": "double", "type": "number" } + }, + { + "description": "pagination - the pagination token from the previous response, default = 100", + "in": "query", + "name": "token", + "required": false, + "schema": { "type": "string" } + } + ] + } + }, + "/talks/{id}": { + "get": { + "operationId": "GetById", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/GetTalkDto" } } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "AuthorizationError", + "description": "user unauthenticated" + } + } + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "NotFoundError", "description": "not found" } + } + } + } + } + } + }, + "description": "Get a specific talk", + "summary": "Get a specific talk", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } + ] + }, + "delete": { + "operationId": "Delete", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/GetTalkDto" } } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "AuthorizationError", + "description": "user unauthenticated" + } + } + } + } + } + }, + "404": { + "description": "NotFoundError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { "kind": "NotFoundError", "description": "not found" } + } + } + } + } + }, + "409": { + "description": "NotReadyError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "NotReadyError", + "description": "talk is in process, try again later" + } + } + } + } + } + } + }, + "description": "Delete a specific talk", + "summary": "Delete a specific talk", + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } } + ] + } + }, + "/talks/streams": { + "post": { + "operationId": "CreateStream", + "responses": { + "201": { + "description": "Created stream", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateStreamResponseDto" } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "AuthorizationError", + "description": "user unauthenticated" + } + } + } + } + } + } + }, + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "face": { "$ref": "#/components/schemas/FaceSquareDto" }, + "source_url": { "type": "string" } + }, + "required": ["source_url"], + "type": "object" + } + } + } + } + } + }, + "/talks/streams/{id}/sdp": { + "post": { + "operationId": "StartConnection", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "properties": { "status": { "type": "string" } }, + "required": ["status"], + "type": "object" + } + } + } + }, + "400": { + "description": "MissingSessionCookie", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "MissingSessionCookie", + "description": "missing session cookie" + } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "AuthorizationError", + "description": "user unauthenticated" + } + } + } + } + } + } + }, + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } }, + { + "in": "header", + "name": "cookie", + "required": false, + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "session_id": { "type": "string" }, + "answer": { "$ref": "#/components/schemas/Jsep" } + }, + "required": ["answer"], + "type": "object" + } + } + } + } + } + }, + "/talks/streams/{id}/ice": { + "post": { + "operationId": "AddIceCandidate", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "properties": { "status": { "type": "string" } }, + "required": ["status"], + "type": "object" + } + } + } + }, + "400": { + "description": "MissingSessionCookie", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "MissingSessionCookie", + "description": "missing session cookie" + } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "AuthorizationError", + "description": "user unauthenticated" + } + } + } + } + } + } + }, + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } }, + { + "in": "header", + "name": "cookie", + "required": false, + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "anyOf": [ + { "$ref": "#/components/schemas/IceCandidate" }, + { "properties": {}, "type": "object" } + ] + }, + { + "properties": { "session_id": { "type": "string" } }, + "type": "object" + } + ] + } + } + } + } + } + }, + "/talks/streams/{id}": { + "post": { + "operationId": "CreateTalkStream", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "properties": { + "duration": { "type": "number", "format": "double" }, + "session_id": { "type": "string" }, + "status": { "type": "string" } + }, + "required": ["duration", "session_id", "status"], + "type": "object" + } + } + } + }, + "400": { + "description": "MissingSessionCookie", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "MissingSessionCookie", + "description": "missing session cookie" + } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "AuthorizationError", + "description": "user unauthenticated" + } + } + } + } + } + } + }, + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } }, + { + "in": "header", + "name": "cookie", + "required": false, + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateTalkStreamRequest" } + } + } + } + }, + "delete": { + "operationId": "DeleteStream", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "properties": { "status": { "type": "string" } }, + "required": ["status"], + "type": "object" + } + } + } + }, + "400": { + "description": "MissingSessionCookie", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "MissingSessionCookie", + "description": "missing session cookie" + } + } + } + } + } + }, + "401": { + "description": "AuthorizationError", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/JsonError" }, + "examples": { + "Example 1": { + "value": { + "kind": "AuthorizationError", + "description": "user unauthenticated" + } + } + } + } + } + } + }, + "security": [{ "basic": [] }, { "bearer": [] }], + "parameters": [ + { "in": "path", "name": "id", "required": true, "schema": { "type": "string" } }, + { + "in": "header", + "name": "cookie", + "required": false, + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { "session_id": { "type": "string" } }, + "type": "object" + } + } + } + } + } + } + }, + "servers": [{ "url": "https://api.d-id.com/" }] +} diff --git a/server/node-service/src/plugins/twilio/index.ts b/server/node-service/src/plugins/twilio/index.ts index 27bc3fcb9..a134293aa 100644 --- a/server/node-service/src/plugins/twilio/index.ts +++ b/server/node-service/src/plugins/twilio/index.ts @@ -1,38 +1,18 @@ -import { readYaml } from "../../common/util"; +import { dirToSpecList, specsToOptions, version2spec } from "../../common/util"; import _ from "lodash"; -import fs from "fs"; import path from "path"; import { OpenAPI } from "openapi-types"; import { ConfigToType, DataSourcePlugin, QueryConfig } from "lowcoder-sdk/dataSource"; import { runOpenApi } from "../openApi"; -import { MultiOpenApiSpecItem, parseMultiOpenApi, ParseOpenApiOptions } from "../openApi/parse"; -import { appendTags } from "../openApi/util"; +import { parseMultiOpenApi, ParseOpenApiOptions } from "../openApi/parse"; -function genTagFromFileName(name: string) { - const fileName = name.replace(/\.yaml|twilio_/g, ""); - const parts = fileName.split("_"); - return parts.reduce((a, b) => { - if (/v\d+/.test(b)) { - return `${a}(${b})`; - } - return a + _.upperFirst(b); - }, ""); -} - -const specList: MultiOpenApiSpecItem[] = []; -const start = performance.now(); -const specFiles = fs.readdirSync(path.join(__dirname, "./twilio.spec")); -specFiles.forEach((specFile) => { - const spec = readYaml(path.join(__dirname, "./twilio.spec", specFile)); - const tag = genTagFromFileName(specFile); - appendTags(spec, tag); - specList.push({ - id: tag, - spec, - }); -}); -logger.info("twilio spec list loaded, duration: %d ms", performance.now() - start); +const specs = { + "v1.0": dirToSpecList(path.join(__dirname, "./twilio.spec.v1")), + "v2.0": dirToSpecList(path.join(__dirname, "./twilio.spec.v2")), + "v3.0": dirToSpecList(path.join(__dirname, "./twilio.spec.v3")), + "v2010": dirToSpecList(path.join(__dirname, "./twilio.spec.v2010")), +} const dataSourceConfig = { type: "dataSource", @@ -49,6 +29,14 @@ const dataSourceConfig = { label: "Secret", placeholder: "", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -58,7 +46,7 @@ const parseOptions: ParseOpenApiOptions = { }, }; -let queryConfig: QueryConfig; +let queryConfig: any = {}; type DataSourceConfigType = ConfigToType; @@ -69,10 +57,10 @@ const twilioPlugin: DataSourcePlugin = { category: "Messaging", dataSourceConfig, - queryConfig: async () => { - if (!queryConfig) { - const { actions, categories } = await parseMultiOpenApi(specList, parseOptions); - queryConfig = { + queryConfig: async (data) => { + if (!queryConfig[data.specVersion as keyof typeof queryConfig]) { + const { actions, categories } = await parseMultiOpenApi(version2spec(specs, data.specVersion), parseOptions); + queryConfig[data.specVersion as keyof typeof queryConfig] = { type: "query", label: "Action", categories: { @@ -82,7 +70,7 @@ const twilioPlugin: DataSourcePlugin = { actions, }; } - return queryConfig; + return queryConfig[data.specVersion as keyof typeof queryConfig]; }, run: function (actionData, dataSourceConfig): Promise { @@ -90,8 +78,9 @@ const twilioPlugin: DataSourcePlugin = { url: "", serverURL: "", dynamicParamsConfig: dataSourceConfig, + specVersion: dataSourceConfig.specVersion, }; - return runOpenApi(actionData, runApiDsConfig, specList); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion)); }, }; diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_accounts_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_accounts_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_accounts_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_accounts_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_autopilot_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_autopilot_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_autopilot_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_autopilot_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_bulkexports_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_bulkexports_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_bulkexports_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_bulkexports_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_chat_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_chat_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_chat_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_chat_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_content_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_content_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_content_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_content_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_conversations_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_conversations_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_conversations_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_conversations_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_events_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_events_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_events_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_events_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_flex_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_flex_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_flex_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_flex_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_frontline_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_frontline_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_frontline_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_frontline_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_insights_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_insights_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_insights_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_insights_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_ip_messaging_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_ip_messaging_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_ip_messaging_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_ip_messaging_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_lookups_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_lookups_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_lookups_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_lookups_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_media_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_media_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_media_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_media_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_messaging_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_messaging_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_messaging_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_messaging_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_microvisor_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_microvisor_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_microvisor_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_microvisor_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_monitor_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_monitor_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_monitor_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_monitor_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_notify_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_notify_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_notify_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_notify_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_oauth_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_oauth_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_oauth_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_oauth_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_preview.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_preview.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_preview.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_preview.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_pricing_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_pricing_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_pricing_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_pricing_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_proxy_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_proxy_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_proxy_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_proxy_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_serverless_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_serverless_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_serverless_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_serverless_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_studio_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_studio_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_studio_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_studio_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_supersim_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_supersim_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_supersim_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_supersim_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_sync_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_sync_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_sync_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_sync_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_taskrouter_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_taskrouter_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_taskrouter_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_taskrouter_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_trunking_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_trunking_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_trunking_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_trunking_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_trusthub_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_trusthub_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_trusthub_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_trusthub_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_video_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_video_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_video_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_video_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_voice_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_voice_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_voice_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_voice_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_wireless_v1.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_wireless_v1.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_wireless_v1.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v1/twilio_wireless_v1.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_chat_v2.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_chat_v2.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_chat_v2.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_chat_v2.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_flex_v2.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_flex_v2.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_flex_v2.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_flex_v2.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_ip_messaging_v2.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_ip_messaging_v2.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_ip_messaging_v2.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_ip_messaging_v2.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_lookups_v2.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_lookups_v2.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_lookups_v2.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_lookups_v2.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_numbers_v2.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_numbers_v2.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_numbers_v2.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_numbers_v2.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_pricing_v2.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_pricing_v2.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_pricing_v2.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_pricing_v2.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_routes_v2.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_routes_v2.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_routes_v2.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_routes_v2.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_studio_v2.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_studio_v2.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_studio_v2.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_studio_v2.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_verify_v2.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_verify_v2.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_verify_v2.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v2/twilio_verify_v2.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_api_v2010.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v2010/twilio_api_v2010.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_api_v2010.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v2010/twilio_api_v2010.yaml diff --git a/server/node-service/src/plugins/twilio/twilio.spec/twilio_chat_v3.yaml b/server/node-service/src/plugins/twilio/twilio.spec.v3/twilio_chat_v3.yaml similarity index 100% rename from server/node-service/src/plugins/twilio/twilio.spec/twilio_chat_v3.yaml rename to server/node-service/src/plugins/twilio/twilio.spec.v3/twilio_chat_v3.yaml diff --git a/server/node-service/src/plugins/woocommerce/index.ts b/server/node-service/src/plugins/woocommerce/index.ts index fde86d352..98b55c9c6 100644 --- a/server/node-service/src/plugins/woocommerce/index.ts +++ b/server/node-service/src/plugins/woocommerce/index.ts @@ -5,6 +5,10 @@ import { ConfigToType, DataSourcePlugin } from "lowcoder-sdk/dataSource"; import { runOpenApi } from "../openApi"; import { defaultParseOpenApiOptions, parseOpenApi, ParseOpenApiOptions } from "../openApi/parse"; import spec from "./woocommerce-spec.json"; +import { specsToOptions, version2spec } from "../../common/util"; +const specs = { + "v1.0": spec, +} export function prepareServerUrl(url: string) { if (/\/wc\/v[12]$/.test(url)) { @@ -45,6 +49,14 @@ const dataSourceConfig = { tooltip: "", placeholder: "", }, + { + label: "Spec Version", + key: "specVersion", + type: "select", + tooltip: "Version of the spec file.", + placeholder: "v1.0", + options: specsToOptions(specs) + }, ], } as const; @@ -65,8 +77,8 @@ const wooCommercePlugin: DataSourcePlugin = { icon: "woocommerce.svg", category: "eCommerce", dataSourceConfig, - queryConfig: async () => { - const { actions, categories } = await parseOpenApi(spec as OpenAPI.Document, parseOptions); + queryConfig: async (data) => { + const { actions, categories } = await parseOpenApi(version2spec(specs, data.specVersion) as OpenAPI.Document, parseOptions); return { type: "query", label: "Operation", @@ -83,8 +95,9 @@ const wooCommercePlugin: DataSourcePlugin = { url: "", serverURL: prepareServerUrl(serverURL), dynamicParamsConfig: otherDataSourceConfig, + specVersion: dataSourceConfig.specVersion }; - return runOpenApi(actionData, runApiDsConfig, spec as unknown as OpenAPIV2.Document); + return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion) as unknown as OpenAPIV2.Document); }, };