diff --git a/packages/backend-core/src/logging/index.ts b/packages/backend-core/src/logging/index.ts index 276a8d627c..b229f47dea 100644 --- a/packages/backend-core/src/logging/index.ts +++ b/packages/backend-core/src/logging/index.ts @@ -1,5 +1,5 @@ export * as correlation from "./correlation/correlation" -export { default as logger } from "./pino/logger" +export { logger, disableLogger } from "./pino/logger" export * from "./alerts" // turn off or on context logging i.e. tenantId, appId etc diff --git a/packages/backend-core/src/logging/pino/logger.ts b/packages/backend-core/src/logging/pino/logger.ts index c82876f49a..dd4b505d30 100644 --- a/packages/backend-core/src/logging/pino/logger.ts +++ b/packages/backend-core/src/logging/pino/logger.ts @@ -5,6 +5,17 @@ import * as correlation from "../correlation" import { IdentityType } from "@budibase/types" import { LOG_CONTEXT } from "../index" +// CORE LOGGERS - for disabling + +const BUILT_INS = { + log: console.log, + error: console.error, + info: console.info, + warn: console.warn, + trace: console.trace, + debug: console.debug, +} + // LOGGER const pinoOptions: LoggerOptions = { @@ -31,6 +42,15 @@ if (env.isDev()) { export const logger = pino(pinoOptions) +export function disableLogger() { + console.log = BUILT_INS.log + console.error = BUILT_INS.error + console.info = BUILT_INS.info + console.warn = BUILT_INS.warn + console.trace = BUILT_INS.trace + console.debug = BUILT_INS.debug +} + // CONSOLE OVERRIDES interface MergingObject { @@ -166,5 +186,3 @@ const getIdentity = () => { } return identity } - -export default logger diff --git a/packages/backend-core/src/logging/pino/middleware.ts b/packages/backend-core/src/logging/pino/middleware.ts index e9d37ab692..569420c5f2 100644 --- a/packages/backend-core/src/logging/pino/middleware.ts +++ b/packages/backend-core/src/logging/pino/middleware.ts @@ -1,5 +1,5 @@ import env from "../../environment" -import logger from "./logger" +import { logger } from "./logger" import { IncomingMessage } from "http" const pino = require("koa-pino-logger") import { Options } from "pino-http" diff --git a/packages/backend-core/src/plugin/tests/validation.spec.ts b/packages/backend-core/src/plugin/tests/validation.spec.ts new file mode 100644 index 0000000000..0fea009645 --- /dev/null +++ b/packages/backend-core/src/plugin/tests/validation.spec.ts @@ -0,0 +1,83 @@ +import { validate } from "../utils" +import fetch from "node-fetch" +import { PluginType } from "@budibase/types" + +const repoUrl = + "https://raw.githubusercontent.com/Budibase/budibase-skeleton/master" +const automationLink = `${repoUrl}/automation/schema.json.hbs` +const componentLink = `${repoUrl}/component/schema.json.hbs` +const datasourceLink = `${repoUrl}/datasource/schema.json.hbs` + +async function getSchema(link: string) { + const response = await fetch(link) + if (response.status > 300) { + return + } + const text = await response.text() + return JSON.parse(text) +} + +async function runTest(opts: { link?: string; schema?: any }) { + let error + try { + let schema = opts.schema + if (opts.link) { + schema = await getSchema(opts.link) + } + validate(schema) + } catch (err) { + error = err + } + return error +} + +describe("it should be able to validate an automation schema", () => { + it("should return automation skeleton schema is valid", async () => { + const error = await runTest({ link: automationLink }) + expect(error).toBeUndefined() + }) + + it("should fail given invalid automation schema", async () => { + const error = await runTest({ + schema: { + type: PluginType.AUTOMATION, + schema: {}, + }, + }) + expect(error).toBeDefined() + }) +}) + +describe("it should be able to validate a component schema", () => { + it("should return component skeleton schema is valid", async () => { + const error = await runTest({ link: componentLink }) + expect(error).toBeUndefined() + }) + + it("should fail given invalid component schema", async () => { + const error = await runTest({ + schema: { + type: PluginType.COMPONENT, + schema: {}, + }, + }) + expect(error).toBeDefined() + }) +}) + +describe("it should be able to validate a datasource schema", () => { + it("should return datasource skeleton schema is valid", async () => { + const error = await runTest({ link: datasourceLink }) + expect(error).toBeUndefined() + }) + + it("should fail given invalid datasource schema", async () => { + const error = await runTest({ + schema: { + type: PluginType.DATASOURCE, + schema: {}, + }, + }) + expect(error).toBeDefined() + }) +}) diff --git a/packages/backend-core/src/plugin/utils.ts b/packages/backend-core/src/plugin/utils.ts index 7b62248bb5..f73ded0659 100644 --- a/packages/backend-core/src/plugin/utils.ts +++ b/packages/backend-core/src/plugin/utils.ts @@ -1,4 +1,12 @@ -import { DatasourceFieldType, QueryType, PluginType } from "@budibase/types" +import { + DatasourceFieldType, + QueryType, + PluginType, + AutomationStepType, + AutomationStepIdArray, + AutomationIOType, + AutomationCustomIOType, +} from "@budibase/types" import joi from "joi" const DATASOURCE_TYPES = [ @@ -19,7 +27,7 @@ function runJoi(validator: joi.Schema, schema: any) { function validateComponent(schema: any) { const validator = joi.object({ - type: joi.string().allow("component").required(), + type: joi.string().allow(PluginType.COMPONENT).required(), metadata: joi.object().unknown(true).required(), hash: joi.string().optional(), version: joi.string().optional(), @@ -53,7 +61,7 @@ function validateDatasource(schema: any) { .required() const validator = joi.object({ - type: joi.string().allow("datasource").required(), + type: joi.string().allow(PluginType.DATASOURCE).required(), metadata: joi.object().unknown(true).required(), hash: joi.string().optional(), version: joi.string().optional(), @@ -82,6 +90,55 @@ function validateDatasource(schema: any) { runJoi(validator, schema) } +function validateAutomation(schema: any) { + const basePropsValidator = joi.object().pattern(joi.string(), { + type: joi + .string() + .allow(...Object.values(AutomationIOType)) + .required(), + customType: joi.string().allow(...Object.values(AutomationCustomIOType)), + title: joi.string(), + description: joi.string(), + enum: joi.array().items(joi.string()), + pretty: joi.array().items(joi.string()), + }) + const stepSchemaValidator = joi + .object({ + properties: basePropsValidator, + required: joi.array().items(joi.string()), + }) + .concat(basePropsValidator) + .required() + const validator = joi.object({ + type: joi.string().allow(PluginType.AUTOMATION).required(), + metadata: joi.object().unknown(true).required(), + hash: joi.string().optional(), + version: joi.string().optional(), + schema: joi.object({ + name: joi.string().required(), + tagline: joi.string().required(), + icon: joi.string().required(), + description: joi.string().required(), + type: joi + .string() + .allow(AutomationStepType.ACTION, AutomationStepType.LOGIC) + .required(), + stepId: joi + .string() + .disallow(...AutomationStepIdArray) + .required(), + inputs: joi.object().optional(), + schema: joi + .object({ + inputs: stepSchemaValidator, + outputs: stepSchemaValidator, + }) + .required(), + }), + }) + runJoi(validator, schema) +} + export function validate(schema: any) { switch (schema?.type) { case PluginType.COMPONENT: @@ -90,6 +147,9 @@ export function validate(schema: any) { case PluginType.DATASOURCE: validateDatasource(schema) break + case PluginType.AUTOMATION: + validateAutomation(schema) + break default: throw new Error(`Unknown plugin type - check schema.json: ${schema.type}`) } diff --git a/packages/backend-core/tests/core/utilities/mocks/fetch.ts b/packages/backend-core/tests/core/utilities/mocks/fetch.ts index 287280067a..f7447d2c47 100644 --- a/packages/backend-core/tests/core/utilities/mocks/fetch.ts +++ b/packages/backend-core/tests/core/utilities/mocks/fetch.ts @@ -1,7 +1,7 @@ const mockFetch = jest.fn((url: any, opts: any) => { const fetch = jest.requireActual("node-fetch") const env = jest.requireActual("../../../../src/environment").default - if (url.includes(env.COUCH_DB_URL)) { + if (url.includes(env.COUCH_DB_URL) || url.includes("raw.github")) { return fetch(url, opts) } return undefined diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte index f30b49eb39..3a85c7ed56 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte @@ -26,7 +26,7 @@ const external = actions.reduce((acc, elm) => { const [k, v] = elm - if (!v.internal) { + if (!v.internal && !v.custom) { acc[k] = v } return acc @@ -41,6 +41,15 @@ return acc }, {}) + const plugins = actions.reduce((acc, elm) => { + const [k, v] = elm + if (v.custom) { + acc[k] = v + } + return acc + }, {}) + console.log(plugins) + const selectAction = action => { actionVal = action selectedAction = action.name @@ -116,6 +125,26 @@ {/each} + + {#if Object.keys(plugins).length} + + Plugins +
+ {#each Object.entries(plugins) as [idx, action]} +
selectAction(action)} + > +
+ + {action.name} +
+
+ {/each} +
+
+ {/if}