Merge branch 'master' of github.com:budibase/budibase into test-oracle
This commit is contained in:
commit
84020be98e
|
@ -1,206 +0,0 @@
|
||||||
// @ts-ignore
|
|
||||||
import fs from "fs"
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
module FetchMock {
|
|
||||||
// @ts-ignore
|
|
||||||
const fetch = jest.requireActual("node-fetch")
|
|
||||||
let failCount = 0
|
|
||||||
let mockSearch = false
|
|
||||||
|
|
||||||
const func = async (url: any, opts: any) => {
|
|
||||||
const { host, pathname } = new URL(url)
|
|
||||||
function json(body: any, status = 200) {
|
|
||||||
return {
|
|
||||||
status,
|
|
||||||
headers: {
|
|
||||||
raw: () => {
|
|
||||||
return { "content-type": ["application/json"] }
|
|
||||||
},
|
|
||||||
get: (name: string) => {
|
|
||||||
if (name.toLowerCase() === "content-type") {
|
|
||||||
return ["application/json"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
json: async () => {
|
|
||||||
//x-www-form-encoded body is a URLSearchParams
|
|
||||||
//The call to stringify it leaves it blank
|
|
||||||
if (body?.opts?.body instanceof URLSearchParams) {
|
|
||||||
const paramArray = Array.from(body.opts.body.entries())
|
|
||||||
body.opts.body = paramArray.reduce((acc: any, pair: any) => {
|
|
||||||
acc[pair[0]] = pair[1]
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
return body
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pathname.includes("/api/global")) {
|
|
||||||
const user = {
|
|
||||||
email: "test@example.com",
|
|
||||||
_id: "us_test@example.com",
|
|
||||||
status: "active",
|
|
||||||
roles: {},
|
|
||||||
builder: {
|
|
||||||
global: false,
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
global: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return pathname.endsWith("/users") && opts.method === "GET"
|
|
||||||
? json([user])
|
|
||||||
: json(user)
|
|
||||||
}
|
|
||||||
// mocked data based on url
|
|
||||||
else if (pathname.includes("api/apps")) {
|
|
||||||
return json({
|
|
||||||
app1: {
|
|
||||||
url: "/app1",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else if (host.includes("example.com")) {
|
|
||||||
return json({
|
|
||||||
body: opts.body,
|
|
||||||
url,
|
|
||||||
method: opts.method,
|
|
||||||
})
|
|
||||||
} else if (host.includes("invalid.com")) {
|
|
||||||
return json(
|
|
||||||
{
|
|
||||||
invalid: true,
|
|
||||||
},
|
|
||||||
404
|
|
||||||
)
|
|
||||||
} else if (mockSearch && pathname.includes("_search")) {
|
|
||||||
const body = opts.body
|
|
||||||
const parts = body.split("tableId:")
|
|
||||||
let tableId
|
|
||||||
if (parts && parts[1]) {
|
|
||||||
tableId = parts[1].split('"')[0]
|
|
||||||
}
|
|
||||||
return json({
|
|
||||||
rows: [
|
|
||||||
{
|
|
||||||
doc: {
|
|
||||||
_id: "test",
|
|
||||||
tableId: tableId,
|
|
||||||
query: opts.body,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
bookmark: "test",
|
|
||||||
})
|
|
||||||
} else if (host.includes("google.com")) {
|
|
||||||
return json({
|
|
||||||
url,
|
|
||||||
opts,
|
|
||||||
value:
|
|
||||||
'<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en-GB"></html>',
|
|
||||||
})
|
|
||||||
} else if (
|
|
||||||
url === "https://api.github.com/repos/my-repo/budibase-comment-box"
|
|
||||||
) {
|
|
||||||
return Promise.resolve({
|
|
||||||
json: () => {
|
|
||||||
return {
|
|
||||||
name: "budibase-comment-box",
|
|
||||||
releases_url:
|
|
||||||
"https://api.github.com/repos/my-repo/budibase-comment-box{/id}",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else if (
|
|
||||||
url === "https://api.github.com/repos/my-repo/budibase-comment-box/latest"
|
|
||||||
) {
|
|
||||||
return Promise.resolve({
|
|
||||||
json: () => {
|
|
||||||
return {
|
|
||||||
assets: [
|
|
||||||
{
|
|
||||||
content_type: "application/gzip",
|
|
||||||
browser_download_url:
|
|
||||||
"https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else if (
|
|
||||||
url ===
|
|
||||||
"https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz"
|
|
||||||
) {
|
|
||||||
return Promise.resolve({
|
|
||||||
body: fs.createReadStream(
|
|
||||||
"src/api/routes/tests/data/comment-box-1.0.2.tar.gz"
|
|
||||||
),
|
|
||||||
ok: true,
|
|
||||||
})
|
|
||||||
} else if (url === "https://www.npmjs.com/package/budibase-component") {
|
|
||||||
return Promise.resolve({
|
|
||||||
status: 200,
|
|
||||||
json: () => {
|
|
||||||
return {
|
|
||||||
name: "budibase-component",
|
|
||||||
"dist-tags": {
|
|
||||||
latest: "1.0.0",
|
|
||||||
},
|
|
||||||
versions: {
|
|
||||||
"1.0.0": {
|
|
||||||
dist: {
|
|
||||||
tarball:
|
|
||||||
"https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.2.tgz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else if (
|
|
||||||
url ===
|
|
||||||
"https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.2.tgz"
|
|
||||||
) {
|
|
||||||
return Promise.resolve({
|
|
||||||
body: fs.createReadStream(
|
|
||||||
"src/api/routes/tests/data/budibase-component-1.0.2.tgz"
|
|
||||||
),
|
|
||||||
ok: true,
|
|
||||||
})
|
|
||||||
} else if (
|
|
||||||
url === "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz"
|
|
||||||
) {
|
|
||||||
return Promise.resolve({
|
|
||||||
body: fs.createReadStream(
|
|
||||||
"src/api/routes/tests/data/comment-box-1.0.2.tar.gz"
|
|
||||||
),
|
|
||||||
ok: true,
|
|
||||||
})
|
|
||||||
} else if (url === "https://www.googleapis.com/oauth2/v4/token") {
|
|
||||||
// any valid response
|
|
||||||
return json({})
|
|
||||||
} else if (host.includes("failonce.com")) {
|
|
||||||
failCount++
|
|
||||||
if (failCount === 1) {
|
|
||||||
return json({ message: "error" }, 500)
|
|
||||||
} else {
|
|
||||||
return json({
|
|
||||||
fails: failCount - 1,
|
|
||||||
url,
|
|
||||||
opts,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fetch(url, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func.Headers = fetch.Headers
|
|
||||||
|
|
||||||
func.mockSearch = () => {
|
|
||||||
mockSearch = true
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = func
|
|
||||||
}
|
|
|
@ -1,6 +1,13 @@
|
||||||
import { npmUpload, urlUpload, githubUpload } from "./uploaders"
|
import { npmUpload, urlUpload, githubUpload } from "./uploaders"
|
||||||
import { plugins as pluginCore } from "@budibase/backend-core"
|
import { plugins as pluginCore } from "@budibase/backend-core"
|
||||||
import { PluginType, FileType, PluginSource } from "@budibase/types"
|
import {
|
||||||
|
PluginType,
|
||||||
|
FileType,
|
||||||
|
PluginSource,
|
||||||
|
Ctx,
|
||||||
|
CreatePluginRequest,
|
||||||
|
CreatePluginResponse,
|
||||||
|
} from "@budibase/types"
|
||||||
import env from "../../../environment"
|
import env from "../../../environment"
|
||||||
import { clientAppSocket } from "../../../websockets"
|
import { clientAppSocket } from "../../../websockets"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
|
@ -29,7 +36,9 @@ export async function upload(ctx: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(ctx: any) {
|
export async function create(
|
||||||
|
ctx: Ctx<CreatePluginRequest, CreatePluginResponse>
|
||||||
|
) {
|
||||||
const { source, url, headers, githubToken } = ctx.request.body
|
const { source, url, headers, githubToken } = ctx.request.body
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -75,14 +84,9 @@ export async function create(ctx: any) {
|
||||||
const doc = await pro.plugins.storePlugin(metadata, directory, source)
|
const doc = await pro.plugins.storePlugin(metadata, directory, source)
|
||||||
|
|
||||||
clientAppSocket?.emit("plugins-update", { name, hash: doc.hash })
|
clientAppSocket?.emit("plugins-update", { name, hash: doc.hash })
|
||||||
ctx.body = {
|
|
||||||
message: "Plugin uploaded successfully",
|
|
||||||
plugins: [doc],
|
|
||||||
}
|
|
||||||
ctx.body = { plugin: doc }
|
ctx.body = { plugin: doc }
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const errMsg = err?.message ? err?.message : err
|
const errMsg = err?.message ? err?.message : err
|
||||||
|
|
||||||
ctx.throw(400, `Failed to import plugin: ${errMsg}`)
|
ctx.throw(400, `Failed to import plugin: ${errMsg}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { type App } from "@budibase/types"
|
||||||
import tk from "timekeeper"
|
import tk from "timekeeper"
|
||||||
import * as uuid from "uuid"
|
import * as uuid from "uuid"
|
||||||
import { structures } from "@budibase/backend-core/tests"
|
import { structures } from "@budibase/backend-core/tests"
|
||||||
|
import nock from "nock"
|
||||||
|
|
||||||
describe("/applications", () => {
|
describe("/applications", () => {
|
||||||
let config = setup.getConfig()
|
let config = setup.getConfig()
|
||||||
|
@ -35,6 +36,7 @@ describe("/applications", () => {
|
||||||
throw new Error("Failed to publish app")
|
throw new Error("Failed to publish app")
|
||||||
}
|
}
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
|
nock.cleanAll()
|
||||||
})
|
})
|
||||||
|
|
||||||
// These need to go first for the app totals to make sense
|
// These need to go first for the app totals to make sense
|
||||||
|
@ -324,18 +326,33 @@ describe("/applications", () => {
|
||||||
|
|
||||||
describe("delete", () => {
|
describe("delete", () => {
|
||||||
it("should delete published app and dev apps with dev app ID", async () => {
|
it("should delete published app and dev apps with dev app ID", async () => {
|
||||||
|
const prodAppId = app.appId.replace("_dev", "")
|
||||||
|
nock("http://localhost:10000")
|
||||||
|
.delete(`/api/global/roles/${prodAppId}`)
|
||||||
|
.reply(200, {})
|
||||||
|
|
||||||
await config.api.application.delete(app.appId)
|
await config.api.application.delete(app.appId)
|
||||||
expect(events.app.deleted).toHaveBeenCalledTimes(1)
|
expect(events.app.deleted).toHaveBeenCalledTimes(1)
|
||||||
expect(events.app.unpublished).toHaveBeenCalledTimes(1)
|
expect(events.app.unpublished).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should delete published app and dev app with prod app ID", async () => {
|
it("should delete published app and dev app with prod app ID", async () => {
|
||||||
await config.api.application.delete(app.appId.replace("_dev", ""))
|
const prodAppId = app.appId.replace("_dev", "")
|
||||||
|
nock("http://localhost:10000")
|
||||||
|
.delete(`/api/global/roles/${prodAppId}`)
|
||||||
|
.reply(200, {})
|
||||||
|
|
||||||
|
await config.api.application.delete(prodAppId)
|
||||||
expect(events.app.deleted).toHaveBeenCalledTimes(1)
|
expect(events.app.deleted).toHaveBeenCalledTimes(1)
|
||||||
expect(events.app.unpublished).toHaveBeenCalledTimes(1)
|
expect(events.app.unpublished).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to delete an app after SQS_SEARCH_ENABLE has been set but app hasn't been migrated", async () => {
|
it("should be able to delete an app after SQS_SEARCH_ENABLE has been set but app hasn't been migrated", async () => {
|
||||||
|
const prodAppId = app.appId.replace("_dev", "")
|
||||||
|
nock("http://localhost:10000")
|
||||||
|
.delete(`/api/global/roles/${prodAppId}`)
|
||||||
|
.reply(200, {})
|
||||||
|
|
||||||
await config.withCoreEnv({ SQS_SEARCH_ENABLE: "true" }, async () => {
|
await config.withCoreEnv({ SQS_SEARCH_ENABLE: "true" }, async () => {
|
||||||
await config.api.application.delete(app.appId)
|
await config.api.application.delete(app.appId)
|
||||||
})
|
})
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
|
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
|
||||||
import { tableForDatasource } from "../../../tests/utilities/structures"
|
import { tableForDatasource } from "../../../tests/utilities/structures"
|
||||||
|
import nock from "nock"
|
||||||
|
|
||||||
describe("/datasources", () => {
|
describe("/datasources", () => {
|
||||||
const config = setup.getConfig()
|
const config = setup.getConfig()
|
||||||
|
@ -37,6 +38,7 @@ describe("/datasources", () => {
|
||||||
config: {},
|
config: {},
|
||||||
})
|
})
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
|
nock.cleanAll()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
|
@ -71,6 +73,12 @@ describe("/datasources", () => {
|
||||||
|
|
||||||
describe("dynamic variables", () => {
|
describe("dynamic variables", () => {
|
||||||
it("should invalidate changed or removed variables", async () => {
|
it("should invalidate changed or removed variables", async () => {
|
||||||
|
nock("http://www.example.com/")
|
||||||
|
.get("/")
|
||||||
|
.reply(200, [{ value: "test" }])
|
||||||
|
.get("/?test=test")
|
||||||
|
.reply(200, [{ value: 1 }])
|
||||||
|
|
||||||
let datasource = await config.api.datasource.create({
|
let datasource = await config.api.datasource.create({
|
||||||
type: "datasource",
|
type: "datasource",
|
||||||
name: "Rest",
|
name: "Rest",
|
||||||
|
@ -81,7 +89,7 @@ describe("/datasources", () => {
|
||||||
const query = await config.api.query.save({
|
const query = await config.api.query.save({
|
||||||
datasourceId: datasource._id!,
|
datasourceId: datasource._id!,
|
||||||
fields: {
|
fields: {
|
||||||
path: "www.google.com",
|
path: "www.example.com",
|
||||||
},
|
},
|
||||||
parameters: [],
|
parameters: [],
|
||||||
transformer: null,
|
transformer: null,
|
||||||
|
|
|
@ -15,6 +15,8 @@ jest.mock("@budibase/backend-core", () => {
|
||||||
|
|
||||||
import { events, objectStore } from "@budibase/backend-core"
|
import { events, objectStore } from "@budibase/backend-core"
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
|
import nock from "nock"
|
||||||
|
import { PluginSource } from "@budibase/types"
|
||||||
|
|
||||||
const mockUploadDirectory = objectStore.uploadDirectory as jest.Mock
|
const mockUploadDirectory = objectStore.uploadDirectory as jest.Mock
|
||||||
const mockDeleteFolder = objectStore.deleteFolder as jest.Mock
|
const mockDeleteFolder = objectStore.deleteFolder as jest.Mock
|
||||||
|
@ -28,6 +30,7 @@ describe("/plugins", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await config.init()
|
await config.init()
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
|
nock.cleanAll()
|
||||||
})
|
})
|
||||||
|
|
||||||
const createPlugin = async (status?: number) => {
|
const createPlugin = async (status?: number) => {
|
||||||
|
@ -112,67 +115,108 @@ describe("/plugins", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("github", () => {
|
describe("github", () => {
|
||||||
const createGithubPlugin = async (status?: number, url?: string) => {
|
beforeEach(async () => {
|
||||||
return await request
|
nock("https://api.github.com")
|
||||||
.post(`/api/plugin`)
|
.get("/repos/my-repo/budibase-comment-box")
|
||||||
.send({
|
.reply(200, {
|
||||||
source: "Github",
|
name: "budibase-comment-box",
|
||||||
url,
|
releases_url:
|
||||||
githubToken: "token",
|
"https://api.github.com/repos/my-repo/budibase-comment-box{/id}",
|
||||||
})
|
})
|
||||||
.set(config.defaultHeaders())
|
.get("/repos/my-repo/budibase-comment-box/latest")
|
||||||
.expect("Content-Type", /json/)
|
.reply(200, {
|
||||||
.expect(status ? status : 200)
|
assets: [
|
||||||
}
|
{
|
||||||
it("should be able to create a plugin from github", async () => {
|
content_type: "application/gzip",
|
||||||
const res = await createGithubPlugin(
|
browser_download_url:
|
||||||
200,
|
"https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz",
|
||||||
"https://github.com/my-repo/budibase-comment-box.git"
|
},
|
||||||
)
|
],
|
||||||
expect(res.body).toBeDefined()
|
})
|
||||||
expect(res.body.plugin).toBeDefined()
|
|
||||||
expect(res.body.plugin._id).toEqual("plg_comment-box")
|
nock("https://github.com")
|
||||||
|
.get(
|
||||||
|
"/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz"
|
||||||
|
)
|
||||||
|
.replyWithFile(
|
||||||
|
200,
|
||||||
|
"src/api/routes/tests/data/comment-box-1.0.2.tar.gz"
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should be able to create a plugin from github", async () => {
|
||||||
|
const { plugin } = await config.api.plugin.create({
|
||||||
|
source: PluginSource.GITHUB,
|
||||||
|
url: "https://github.com/my-repo/budibase-comment-box.git",
|
||||||
|
githubToken: "token",
|
||||||
|
})
|
||||||
|
expect(plugin._id).toEqual("plg_comment-box")
|
||||||
|
})
|
||||||
|
|
||||||
it("should fail if the url is not from github", async () => {
|
it("should fail if the url is not from github", async () => {
|
||||||
const res = await createGithubPlugin(
|
await config.api.plugin.create(
|
||||||
400,
|
{
|
||||||
"https://notgithub.com/my-repo/budibase-comment-box"
|
source: PluginSource.GITHUB,
|
||||||
)
|
url: "https://notgithub.com/my-repo/budibase-comment-box",
|
||||||
expect(res.body.message).toEqual(
|
githubToken: "token",
|
||||||
"Failed to import plugin: The plugin origin must be from Github"
|
},
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
body: {
|
||||||
|
message:
|
||||||
|
"Failed to import plugin: The plugin origin must be from Github",
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe("npm", () => {
|
describe("npm", () => {
|
||||||
it("should be able to create a plugin from npm", async () => {
|
it("should be able to create a plugin from npm", async () => {
|
||||||
const res = await request
|
nock("https://registry.npmjs.org")
|
||||||
.post(`/api/plugin`)
|
.get("/budibase-component")
|
||||||
.send({
|
.reply(200, {
|
||||||
source: "NPM",
|
name: "budibase-component",
|
||||||
url: "https://www.npmjs.com/package/budibase-component",
|
"dist-tags": {
|
||||||
|
latest: "1.0.0",
|
||||||
|
},
|
||||||
|
versions: {
|
||||||
|
"1.0.0": {
|
||||||
|
dist: {
|
||||||
|
tarball:
|
||||||
|
"https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.1.tgz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.set(config.defaultHeaders())
|
.get("/budibase-component/-/budibase-component-1.0.1.tgz")
|
||||||
.expect("Content-Type", /json/)
|
.replyWithFile(
|
||||||
.expect(200)
|
200,
|
||||||
expect(res.body).toBeDefined()
|
"src/api/routes/tests/data/budibase-component-1.0.1.tgz"
|
||||||
expect(res.body.plugin._id).toEqual("plg_budibase-component")
|
)
|
||||||
|
|
||||||
|
const { plugin } = await config.api.plugin.create({
|
||||||
|
source: PluginSource.NPM,
|
||||||
|
url: "https://www.npmjs.com/package/budibase-component",
|
||||||
|
})
|
||||||
|
expect(plugin._id).toEqual("plg_budibase-component")
|
||||||
expect(events.plugin.imported).toHaveBeenCalled()
|
expect(events.plugin.imported).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("url", () => {
|
describe("url", () => {
|
||||||
it("should be able to create a plugin from a URL", async () => {
|
it("should be able to create a plugin from a URL", async () => {
|
||||||
const res = await request
|
nock("https://www.someurl.com")
|
||||||
.post(`/api/plugin`)
|
.get("/comment-box/comment-box-1.0.2.tar.gz")
|
||||||
.send({
|
.replyWithFile(
|
||||||
source: "URL",
|
200,
|
||||||
url: "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz",
|
"src/api/routes/tests/data/comment-box-1.0.2.tar.gz"
|
||||||
})
|
)
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
const { plugin } = await config.api.plugin.create({
|
||||||
.expect(200)
|
source: PluginSource.URL,
|
||||||
expect(res.body).toBeDefined()
|
url: "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz",
|
||||||
expect(res.body.plugin._id).toEqual("plg_comment-box")
|
})
|
||||||
|
expect(plugin._id).toEqual("plg_comment-box")
|
||||||
expect(events.plugin.imported).toHaveBeenCalledTimes(1)
|
expect(events.plugin.imported).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,8 +5,6 @@ import { getCachedVariable } from "../../../../threads/utils"
|
||||||
import nock from "nock"
|
import nock from "nock"
|
||||||
import { generator } from "@budibase/backend-core/tests"
|
import { generator } from "@budibase/backend-core/tests"
|
||||||
|
|
||||||
jest.unmock("node-fetch")
|
|
||||||
|
|
||||||
describe("rest", () => {
|
describe("rest", () => {
|
||||||
let config: TestConfiguration
|
let config: TestConfiguration
|
||||||
let datasource: Datasource
|
let datasource: Datasource
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
const setup = require("./utilities")
|
|
||||||
const fetch = require("node-fetch")
|
|
||||||
|
|
||||||
jest.mock("node-fetch")
|
|
||||||
|
|
||||||
describe("test the outgoing webhook action", () => {
|
|
||||||
let inputs
|
|
||||||
let config = setup.getConfig()
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await config.init()
|
|
||||||
inputs = {
|
|
||||||
username: "joe_bloggs",
|
|
||||||
url: "http://www.example.com",
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
|
||||||
|
|
||||||
it("should be able to run the action", async () => {
|
|
||||||
const res = await setup.runStep(setup.actions.discord.stepId, inputs)
|
|
||||||
expect(res.response.url).toEqual("http://www.example.com")
|
|
||||||
expect(res.response.method).toEqual("post")
|
|
||||||
expect(res.success).toEqual(true)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { getConfig, afterAll as _afterAll, runStep, actions } from "./utilities"
|
||||||
|
import nock from "nock"
|
||||||
|
|
||||||
|
describe("test the outgoing webhook action", () => {
|
||||||
|
let config = getConfig()
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(_afterAll)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to run the action", async () => {
|
||||||
|
nock("http://www.example.com/").post("/").reply(200, { foo: "bar" })
|
||||||
|
const res = await runStep(actions.discord.stepId, {
|
||||||
|
url: "http://www.example.com",
|
||||||
|
username: "joe_bloggs",
|
||||||
|
})
|
||||||
|
expect(res.response.foo).toEqual("bar")
|
||||||
|
expect(res.success).toEqual(true)
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,4 +1,5 @@
|
||||||
import { getConfig, afterAll, runStep, actions } from "./utilities"
|
import { getConfig, afterAll, runStep, actions } from "./utilities"
|
||||||
|
import nock from "nock"
|
||||||
|
|
||||||
describe("test the outgoing webhook action", () => {
|
describe("test the outgoing webhook action", () => {
|
||||||
let config = getConfig()
|
let config = getConfig()
|
||||||
|
@ -9,42 +10,45 @@ describe("test the outgoing webhook action", () => {
|
||||||
|
|
||||||
afterAll()
|
afterAll()
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
|
||||||
it("should be able to run the action", async () => {
|
it("should be able to run the action", async () => {
|
||||||
|
nock("http://www.example.com/").post("/").reply(200, { foo: "bar" })
|
||||||
const res = await runStep(actions.integromat.stepId, {
|
const res = await runStep(actions.integromat.stepId, {
|
||||||
value1: "test",
|
|
||||||
url: "http://www.example.com",
|
url: "http://www.example.com",
|
||||||
})
|
})
|
||||||
expect(res.response.url).toEqual("http://www.example.com")
|
expect(res.response.foo).toEqual("bar")
|
||||||
expect(res.response.method).toEqual("post")
|
|
||||||
expect(res.success).toEqual(true)
|
expect(res.success).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should add the payload props when a JSON string is provided", async () => {
|
it("should add the payload props when a JSON string is provided", async () => {
|
||||||
const payload = `{"value1":1,"value2":2,"value3":3,"value4":4,"value5":5,"name":"Adam","age":9}`
|
const payload = {
|
||||||
|
value1: 1,
|
||||||
|
value2: 2,
|
||||||
|
value3: 3,
|
||||||
|
value4: 4,
|
||||||
|
value5: 5,
|
||||||
|
name: "Adam",
|
||||||
|
age: 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
nock("http://www.example.com/")
|
||||||
|
.post("/", payload)
|
||||||
|
.reply(200, { foo: "bar" })
|
||||||
|
|
||||||
const res = await runStep(actions.integromat.stepId, {
|
const res = await runStep(actions.integromat.stepId, {
|
||||||
value1: "ONE",
|
body: { value: JSON.stringify(payload) },
|
||||||
value2: "TWO",
|
|
||||||
value3: "THREE",
|
|
||||||
value4: "FOUR",
|
|
||||||
value5: "FIVE",
|
|
||||||
body: {
|
|
||||||
value: payload,
|
|
||||||
},
|
|
||||||
url: "http://www.example.com",
|
url: "http://www.example.com",
|
||||||
})
|
})
|
||||||
expect(res.response.url).toEqual("http://www.example.com")
|
expect(res.response.foo).toEqual("bar")
|
||||||
expect(res.response.method).toEqual("post")
|
|
||||||
expect(res.response.body).toEqual(payload)
|
|
||||||
expect(res.success).toEqual(true)
|
expect(res.success).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return a 400 if the JSON payload string is malformed", async () => {
|
it("should return a 400 if the JSON payload string is malformed", async () => {
|
||||||
const payload = `{ value1 1 }`
|
|
||||||
const res = await runStep(actions.integromat.stepId, {
|
const res = await runStep(actions.integromat.stepId, {
|
||||||
value1: "ONE",
|
body: { value: "{ invalid json }" },
|
||||||
body: {
|
|
||||||
value: payload,
|
|
||||||
},
|
|
||||||
url: "http://www.example.com",
|
url: "http://www.example.com",
|
||||||
})
|
})
|
||||||
expect(res.httpStatus).toEqual(400)
|
expect(res.httpStatus).toEqual(400)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { getConfig, afterAll, runStep, actions } from "./utilities"
|
import { getConfig, afterAll, runStep, actions } from "./utilities"
|
||||||
|
import nock from "nock"
|
||||||
|
|
||||||
describe("test the outgoing webhook action", () => {
|
describe("test the outgoing webhook action", () => {
|
||||||
let config = getConfig()
|
let config = getConfig()
|
||||||
|
@ -9,31 +10,33 @@ describe("test the outgoing webhook action", () => {
|
||||||
|
|
||||||
afterAll()
|
afterAll()
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
|
||||||
it("should be able to run the action and default to 'get'", async () => {
|
it("should be able to run the action and default to 'get'", async () => {
|
||||||
|
nock("http://www.example.com/").get("/").reply(200, { foo: "bar" })
|
||||||
const res = await runStep(actions.n8n.stepId, {
|
const res = await runStep(actions.n8n.stepId, {
|
||||||
url: "http://www.example.com",
|
url: "http://www.example.com",
|
||||||
body: {
|
body: {
|
||||||
test: "IGNORE_ME",
|
test: "IGNORE_ME",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(res.response.url).toEqual("http://www.example.com")
|
expect(res.response.foo).toEqual("bar")
|
||||||
expect(res.response.method).toEqual("GET")
|
|
||||||
expect(res.response.body).toBeUndefined()
|
|
||||||
expect(res.success).toEqual(true)
|
expect(res.success).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should add the payload props when a JSON string is provided", async () => {
|
it("should add the payload props when a JSON string is provided", async () => {
|
||||||
const payload = `{ "name": "Adam", "age": 9 }`
|
nock("http://www.example.com/")
|
||||||
|
.post("/", { name: "Adam", age: 9 })
|
||||||
|
.reply(200)
|
||||||
const res = await runStep(actions.n8n.stepId, {
|
const res = await runStep(actions.n8n.stepId, {
|
||||||
body: {
|
body: {
|
||||||
value: payload,
|
value: JSON.stringify({ name: "Adam", age: 9 }),
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "http://www.example.com",
|
url: "http://www.example.com",
|
||||||
})
|
})
|
||||||
expect(res.response.url).toEqual("http://www.example.com")
|
|
||||||
expect(res.response.method).toEqual("POST")
|
|
||||||
expect(res.response.body).toEqual(`{"name":"Adam","age":9}`)
|
|
||||||
expect(res.success).toEqual(true)
|
expect(res.success).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -53,6 +56,9 @@ describe("test the outgoing webhook action", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not append the body if the method is HEAD", async () => {
|
it("should not append the body if the method is HEAD", async () => {
|
||||||
|
nock("http://www.example.com/")
|
||||||
|
.head("/", body => body === "")
|
||||||
|
.reply(200)
|
||||||
const res = await runStep(actions.n8n.stepId, {
|
const res = await runStep(actions.n8n.stepId, {
|
||||||
url: "http://www.example.com",
|
url: "http://www.example.com",
|
||||||
method: "HEAD",
|
method: "HEAD",
|
||||||
|
@ -60,9 +66,6 @@ describe("test the outgoing webhook action", () => {
|
||||||
test: "IGNORE_ME",
|
test: "IGNORE_ME",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(res.response.url).toEqual("http://www.example.com")
|
|
||||||
expect(res.response.method).toEqual("HEAD")
|
|
||||||
expect(res.response.body).toBeUndefined()
|
|
||||||
expect(res.success).toEqual(true)
|
expect(res.success).toEqual(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
const setup = require("./utilities")
|
|
||||||
const fetch = require("node-fetch")
|
|
||||||
|
|
||||||
jest.mock("node-fetch")
|
|
||||||
|
|
||||||
describe("test the outgoing webhook action", () => {
|
|
||||||
let inputs
|
|
||||||
let config = setup.getConfig()
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await config.init()
|
|
||||||
inputs = {
|
|
||||||
requestMethod: "POST",
|
|
||||||
url: "www.example.com",
|
|
||||||
requestBody: JSON.stringify({
|
|
||||||
a: 1,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
|
||||||
|
|
||||||
it("should be able to run the action", async () => {
|
|
||||||
const res = await setup.runStep(
|
|
||||||
setup.actions.OUTGOING_WEBHOOK.stepId,
|
|
||||||
inputs
|
|
||||||
)
|
|
||||||
expect(res.success).toEqual(true)
|
|
||||||
expect(res.response.url).toEqual("http://www.example.com")
|
|
||||||
expect(res.response.method).toEqual("POST")
|
|
||||||
expect(JSON.parse(res.response.body).a).toEqual(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return an error if something goes wrong in fetch", async () => {
|
|
||||||
const res = await setup.runStep(setup.actions.OUTGOING_WEBHOOK.stepId, {
|
|
||||||
requestMethod: "GET",
|
|
||||||
url: "www.invalid.com",
|
|
||||||
})
|
|
||||||
expect(res.success).toEqual(false)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { getConfig, afterAll as _afterAll, runStep, actions } from "./utilities"
|
||||||
|
import nock from "nock"
|
||||||
|
|
||||||
|
describe("test the outgoing webhook action", () => {
|
||||||
|
const config = getConfig()
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(_afterAll)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to run the action", async () => {
|
||||||
|
nock("http://www.example.com")
|
||||||
|
.post("/", { a: 1 })
|
||||||
|
.reply(200, { foo: "bar" })
|
||||||
|
const res = await runStep(actions.OUTGOING_WEBHOOK.stepId, {
|
||||||
|
requestMethod: "POST",
|
||||||
|
url: "www.example.com",
|
||||||
|
requestBody: JSON.stringify({ a: 1 }),
|
||||||
|
})
|
||||||
|
expect(res.success).toEqual(true)
|
||||||
|
expect(res.response.foo).toEqual("bar")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return an error if something goes wrong in fetch", async () => {
|
||||||
|
const res = await runStep(actions.OUTGOING_WEBHOOK.stepId, {
|
||||||
|
requestMethod: "GET",
|
||||||
|
url: "www.invalid.com",
|
||||||
|
})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,4 +1,5 @@
|
||||||
import { getConfig, afterAll, runStep, actions } from "./utilities"
|
import { getConfig, afterAll, runStep, actions } from "./utilities"
|
||||||
|
import nock from "nock"
|
||||||
|
|
||||||
describe("test the outgoing webhook action", () => {
|
describe("test the outgoing webhook action", () => {
|
||||||
let config = getConfig()
|
let config = getConfig()
|
||||||
|
@ -9,44 +10,45 @@ describe("test the outgoing webhook action", () => {
|
||||||
|
|
||||||
afterAll()
|
afterAll()
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
|
||||||
it("should be able to run the action", async () => {
|
it("should be able to run the action", async () => {
|
||||||
|
nock("http://www.example.com/").post("/").reply(200, { foo: "bar" })
|
||||||
const res = await runStep(actions.zapier.stepId, {
|
const res = await runStep(actions.zapier.stepId, {
|
||||||
value1: "test",
|
|
||||||
url: "http://www.example.com",
|
url: "http://www.example.com",
|
||||||
})
|
})
|
||||||
expect(res.response.url).toEqual("http://www.example.com")
|
expect(res.response.foo).toEqual("bar")
|
||||||
expect(res.response.method).toEqual("post")
|
|
||||||
expect(res.success).toEqual(true)
|
expect(res.success).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should add the payload props when a JSON string is provided", async () => {
|
it("should add the payload props when a JSON string is provided", async () => {
|
||||||
const payload = `{ "value1": 1, "value2": 2, "value3": 3, "value4": 4, "value5": 5, "name": "Adam", "age": 9 }`
|
const payload = {
|
||||||
|
value1: 1,
|
||||||
|
value2: 2,
|
||||||
|
value3: 3,
|
||||||
|
value4: 4,
|
||||||
|
value5: 5,
|
||||||
|
name: "Adam",
|
||||||
|
age: 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
nock("http://www.example.com/")
|
||||||
|
.post("/", { ...payload, platform: "budibase" })
|
||||||
|
.reply(200, { foo: "bar" })
|
||||||
|
|
||||||
const res = await runStep(actions.zapier.stepId, {
|
const res = await runStep(actions.zapier.stepId, {
|
||||||
value1: "ONE",
|
body: { value: JSON.stringify(payload) },
|
||||||
value2: "TWO",
|
|
||||||
value3: "THREE",
|
|
||||||
value4: "FOUR",
|
|
||||||
value5: "FIVE",
|
|
||||||
body: {
|
|
||||||
value: payload,
|
|
||||||
},
|
|
||||||
url: "http://www.example.com",
|
url: "http://www.example.com",
|
||||||
})
|
})
|
||||||
expect(res.response.url).toEqual("http://www.example.com")
|
expect(res.response.foo).toEqual("bar")
|
||||||
expect(res.response.method).toEqual("post")
|
|
||||||
expect(res.response.body).toEqual(
|
|
||||||
`{"platform":"budibase","value1":1,"value2":2,"value3":3,"value4":4,"value5":5,"name":"Adam","age":9}`
|
|
||||||
)
|
|
||||||
expect(res.success).toEqual(true)
|
expect(res.success).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return a 400 if the JSON payload string is malformed", async () => {
|
it("should return a 400 if the JSON payload string is malformed", async () => {
|
||||||
const payload = `{ value1 1 }`
|
|
||||||
const res = await runStep(actions.zapier.stepId, {
|
const res = await runStep(actions.zapier.stepId, {
|
||||||
value1: "ONE",
|
body: { value: "{ invalid json }" },
|
||||||
body: {
|
|
||||||
value: payload,
|
|
||||||
},
|
|
||||||
url: "http://www.example.com",
|
url: "http://www.example.com",
|
||||||
})
|
})
|
||||||
expect(res.httpStatus).toEqual(400)
|
expect(res.httpStatus).toEqual(400)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
BodyType,
|
||||||
DatasourceFieldType,
|
DatasourceFieldType,
|
||||||
HttpMethod,
|
HttpMethod,
|
||||||
Integration,
|
Integration,
|
||||||
|
@ -15,7 +16,7 @@ import {
|
||||||
import get from "lodash/get"
|
import get from "lodash/get"
|
||||||
import * as https from "https"
|
import * as https from "https"
|
||||||
import qs from "querystring"
|
import qs from "querystring"
|
||||||
import type { Response } from "node-fetch"
|
import type { Response, RequestInit } from "node-fetch"
|
||||||
import fetch from "node-fetch"
|
import fetch from "node-fetch"
|
||||||
import { formatBytes } from "../utilities"
|
import { formatBytes } from "../utilities"
|
||||||
import { performance } from "perf_hooks"
|
import { performance } from "perf_hooks"
|
||||||
|
@ -28,15 +29,6 @@ import path from "path"
|
||||||
import { Builder as XmlBuilder } from "xml2js"
|
import { Builder as XmlBuilder } from "xml2js"
|
||||||
import { getAttachmentHeaders } from "./utils/restUtils"
|
import { getAttachmentHeaders } from "./utils/restUtils"
|
||||||
|
|
||||||
enum BodyType {
|
|
||||||
NONE = "none",
|
|
||||||
FORM_DATA = "form",
|
|
||||||
XML = "xml",
|
|
||||||
ENCODED = "encoded",
|
|
||||||
JSON = "json",
|
|
||||||
TEXT = "text",
|
|
||||||
}
|
|
||||||
|
|
||||||
const coreFields = {
|
const coreFields = {
|
||||||
path: {
|
path: {
|
||||||
type: DatasourceFieldType.STRING,
|
type: DatasourceFieldType.STRING,
|
||||||
|
@ -127,7 +119,23 @@ const SCHEMA: Integration = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
class RestIntegration implements IntegrationBase {
|
interface ParsedResponse {
|
||||||
|
data: any
|
||||||
|
info: {
|
||||||
|
code: number
|
||||||
|
size: string
|
||||||
|
time: string
|
||||||
|
}
|
||||||
|
extra?: {
|
||||||
|
raw: string | undefined
|
||||||
|
headers: Record<string, string[] | string>
|
||||||
|
}
|
||||||
|
pagination?: {
|
||||||
|
cursor: any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RestIntegration implements IntegrationBase {
|
||||||
private config: RestConfig
|
private config: RestConfig
|
||||||
private headers: {
|
private headers: {
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
|
@ -138,7 +146,10 @@ class RestIntegration implements IntegrationBase {
|
||||||
this.config = config
|
this.config = config
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseResponse(response: Response, pagination: PaginationConfig | null) {
|
async parseResponse(
|
||||||
|
response: Response,
|
||||||
|
pagination?: PaginationConfig
|
||||||
|
): Promise<ParsedResponse> {
|
||||||
let data: any[] | string | undefined,
|
let data: any[] | string | undefined,
|
||||||
raw: string | undefined,
|
raw: string | undefined,
|
||||||
headers: Record<string, string[] | string> = {},
|
headers: Record<string, string[] | string> = {},
|
||||||
|
@ -235,8 +246,8 @@ class RestIntegration implements IntegrationBase {
|
||||||
getUrl(
|
getUrl(
|
||||||
path: string,
|
path: string,
|
||||||
queryString: string,
|
queryString: string,
|
||||||
pagination: PaginationConfig | null,
|
pagination?: PaginationConfig,
|
||||||
paginationValues: PaginationValues | null
|
paginationValues?: PaginationValues
|
||||||
): string {
|
): string {
|
||||||
// Add pagination params to query string if required
|
// Add pagination params to query string if required
|
||||||
if (pagination?.location === "query" && paginationValues) {
|
if (pagination?.location === "query" && paginationValues) {
|
||||||
|
@ -279,10 +290,10 @@ class RestIntegration implements IntegrationBase {
|
||||||
addBody(
|
addBody(
|
||||||
bodyType: string,
|
bodyType: string,
|
||||||
body: string | any,
|
body: string | any,
|
||||||
input: any,
|
input: RequestInit,
|
||||||
pagination: PaginationConfig | null,
|
pagination?: PaginationConfig,
|
||||||
paginationValues: PaginationValues | null
|
paginationValues?: PaginationValues
|
||||||
) {
|
): RequestInit {
|
||||||
if (!input.headers) {
|
if (!input.headers) {
|
||||||
input.headers = {}
|
input.headers = {}
|
||||||
}
|
}
|
||||||
|
@ -345,6 +356,7 @@ class RestIntegration implements IntegrationBase {
|
||||||
string = new XmlBuilder().buildObject(object)
|
string = new XmlBuilder().buildObject(object)
|
||||||
}
|
}
|
||||||
input.body = string
|
input.body = string
|
||||||
|
// @ts-ignore
|
||||||
input.headers["Content-Type"] = "application/xml"
|
input.headers["Content-Type"] = "application/xml"
|
||||||
break
|
break
|
||||||
case BodyType.JSON:
|
case BodyType.JSON:
|
||||||
|
@ -356,13 +368,14 @@ class RestIntegration implements IntegrationBase {
|
||||||
object[key] = value
|
object[key] = value
|
||||||
})
|
})
|
||||||
input.body = JSON.stringify(object)
|
input.body = JSON.stringify(object)
|
||||||
|
// @ts-ignore
|
||||||
input.headers["Content-Type"] = "application/json"
|
input.headers["Content-Type"] = "application/json"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
|
|
||||||
getAuthHeaders(authConfigId: string): { [key: string]: any } {
|
getAuthHeaders(authConfigId?: string): { [key: string]: any } {
|
||||||
let headers: any = {}
|
let headers: any = {}
|
||||||
|
|
||||||
if (this.config.authConfigs && authConfigId) {
|
if (this.config.authConfigs && authConfigId) {
|
||||||
|
@ -398,7 +411,7 @@ class RestIntegration implements IntegrationBase {
|
||||||
headers = {},
|
headers = {},
|
||||||
method = HttpMethod.GET,
|
method = HttpMethod.GET,
|
||||||
disabledHeaders,
|
disabledHeaders,
|
||||||
bodyType,
|
bodyType = BodyType.NONE,
|
||||||
requestBody,
|
requestBody,
|
||||||
authConfigId,
|
authConfigId,
|
||||||
pagination,
|
pagination,
|
||||||
|
@ -407,7 +420,7 @@ class RestIntegration implements IntegrationBase {
|
||||||
const authHeaders = this.getAuthHeaders(authConfigId)
|
const authHeaders = this.getAuthHeaders(authConfigId)
|
||||||
|
|
||||||
this.headers = {
|
this.headers = {
|
||||||
...this.config.defaultHeaders,
|
...(this.config.defaultHeaders || {}),
|
||||||
...headers,
|
...headers,
|
||||||
...authHeaders,
|
...authHeaders,
|
||||||
}
|
}
|
||||||
|
@ -420,7 +433,7 @@ class RestIntegration implements IntegrationBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let input: any = { method, headers: this.headers }
|
let input: RequestInit = { method, headers: this.headers }
|
||||||
input = this.addBody(
|
input = this.addBody(
|
||||||
bodyType,
|
bodyType,
|
||||||
requestBody,
|
requestBody,
|
||||||
|
@ -437,7 +450,12 @@ class RestIntegration implements IntegrationBase {
|
||||||
|
|
||||||
// Deprecated by rejectUnauthorized
|
// Deprecated by rejectUnauthorized
|
||||||
if (this.config.legacyHttpParser) {
|
if (this.config.legacyHttpParser) {
|
||||||
|
// NOTE(samwho): it seems like this code doesn't actually work because it requires
|
||||||
|
// node-fetch >=3, and we're not on that because upgrading to it produces errors to
|
||||||
|
// do with ESM that are above my pay grade.
|
||||||
|
|
||||||
// https://github.com/nodejs/node/issues/43798
|
// https://github.com/nodejs/node/issues/43798
|
||||||
|
// @ts-ignore
|
||||||
input.extraHttpOptions = { insecureHTTPParser: true }
|
input.extraHttpOptions = { insecureHTTPParser: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { GoogleSpreadsheetWorksheet } from "google-spreadsheet"
|
import type { GoogleSpreadsheetWorksheet } from "google-spreadsheet"
|
||||||
|
import nock from "nock"
|
||||||
|
|
||||||
jest.mock("google-auth-library")
|
jest.mock("google-auth-library")
|
||||||
const { OAuth2Client } = require("google-auth-library")
|
const { OAuth2Client } = require("google-auth-library")
|
||||||
|
@ -62,6 +63,13 @@ describe("Google Sheets Integration", () => {
|
||||||
await config.init()
|
await config.init()
|
||||||
|
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
nock.cleanAll()
|
||||||
|
nock("https://www.googleapis.com/").post("/oauth2/v4/token").reply(200, {
|
||||||
|
grant_type: "client_credentials",
|
||||||
|
client_id: "your-client-id",
|
||||||
|
client_secret: "your-client-secret",
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function createBasicTable(name: string, columns: string[]): Table {
|
function createBasicTable(name: string, columns: string[]): Table {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,7 @@
|
||||||
import TestConfiguration from "../../tests/utilities/TestConfiguration"
|
import TestConfiguration from "../../tests/utilities/TestConfiguration"
|
||||||
import { startup } from "../index"
|
import { startup } from "../index"
|
||||||
import { users, utils, tenancy } from "@budibase/backend-core"
|
import { users, utils, tenancy } from "@budibase/backend-core"
|
||||||
|
import nock from "nock"
|
||||||
|
|
||||||
describe("check BB_ADMIN environment variables", () => {
|
describe("check BB_ADMIN environment variables", () => {
|
||||||
const config = new TestConfiguration()
|
const config = new TestConfiguration()
|
||||||
|
@ -8,7 +9,17 @@ describe("check BB_ADMIN environment variables", () => {
|
||||||
await config.init()
|
await config.init()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
|
||||||
it("should be able to create a user with the BB_ADMIN environment variables", async () => {
|
it("should be able to create a user with the BB_ADMIN environment variables", async () => {
|
||||||
|
nock("http://localhost:10000")
|
||||||
|
.get("/api/global/configs/checklist")
|
||||||
|
.reply(200, {})
|
||||||
|
.get("/api/global/self/api_key")
|
||||||
|
.reply(200, {})
|
||||||
|
|
||||||
const EMAIL = "budibase@budibase.com",
|
const EMAIL = "budibase@budibase.com",
|
||||||
PASSWORD = "budibase"
|
PASSWORD = "budibase"
|
||||||
await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => {
|
await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { env as coreEnv, timers } from "@budibase/backend-core"
|
import { env as coreEnv, timers } from "@budibase/backend-core"
|
||||||
import { testContainerUtils } from "@budibase/backend-core/tests"
|
import { testContainerUtils } from "@budibase/backend-core/tests"
|
||||||
|
import nock from "nock"
|
||||||
|
|
||||||
if (!process.env.CI) {
|
if (!process.env.CI) {
|
||||||
// set a longer timeout in dev for debugging 100 seconds
|
// set a longer timeout in dev for debugging 100 seconds
|
||||||
|
@ -9,6 +10,15 @@ if (!process.env.CI) {
|
||||||
jest.setTimeout(30 * 1000)
|
jest.setTimeout(30 * 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nock.disableNetConnect()
|
||||||
|
nock.enableNetConnect(host => {
|
||||||
|
return (
|
||||||
|
host.includes("localhost") ||
|
||||||
|
host.includes("127.0.0.1") ||
|
||||||
|
host.includes("::1")
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
testContainerUtils.setupEnv(env, coreEnv)
|
testContainerUtils.setupEnv(env, coreEnv)
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { RoleAPI } from "./role"
|
||||||
import { TemplateAPI } from "./template"
|
import { TemplateAPI } from "./template"
|
||||||
import { RowActionAPI } from "./rowAction"
|
import { RowActionAPI } from "./rowAction"
|
||||||
import { AutomationAPI } from "./automation"
|
import { AutomationAPI } from "./automation"
|
||||||
|
import { PluginAPI } from "./plugin"
|
||||||
|
|
||||||
export default class API {
|
export default class API {
|
||||||
table: TableAPI
|
table: TableAPI
|
||||||
|
@ -33,6 +34,7 @@ export default class API {
|
||||||
templates: TemplateAPI
|
templates: TemplateAPI
|
||||||
rowAction: RowActionAPI
|
rowAction: RowActionAPI
|
||||||
automation: AutomationAPI
|
automation: AutomationAPI
|
||||||
|
plugin: PluginAPI
|
||||||
|
|
||||||
constructor(config: TestConfiguration) {
|
constructor(config: TestConfiguration) {
|
||||||
this.table = new TableAPI(config)
|
this.table = new TableAPI(config)
|
||||||
|
@ -51,5 +53,6 @@ export default class API {
|
||||||
this.templates = new TemplateAPI(config)
|
this.templates = new TemplateAPI(config)
|
||||||
this.rowAction = new RowActionAPI(config)
|
this.rowAction = new RowActionAPI(config)
|
||||||
this.automation = new AutomationAPI(config)
|
this.automation = new AutomationAPI(config)
|
||||||
|
this.plugin = new PluginAPI(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { Expectations, TestAPI } from "./base"
|
||||||
|
import { CreatePluginRequest, CreatePluginResponse } from "@budibase/types"
|
||||||
|
|
||||||
|
export class PluginAPI extends TestAPI {
|
||||||
|
create = async (body: CreatePluginRequest, expectations?: Expectations) => {
|
||||||
|
return await this._post<CreatePluginResponse>(`/api/plugin`, {
|
||||||
|
body,
|
||||||
|
expectations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,3 +15,4 @@ export * from "./automation"
|
||||||
export * from "./layout"
|
export * from "./layout"
|
||||||
export * from "./query"
|
export * from "./query"
|
||||||
export * from "./role"
|
export * from "./role"
|
||||||
|
export * from "./plugins"
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { PluginSource } from "../../documents"
|
||||||
|
|
||||||
|
export interface CreatePluginRequest {
|
||||||
|
source: PluginSource
|
||||||
|
url: string
|
||||||
|
githubToken?: string
|
||||||
|
headers?: { [key: string]: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatePluginResponse {
|
||||||
|
plugin: any
|
||||||
|
}
|
|
@ -45,15 +45,15 @@ export interface DynamicVariable {
|
||||||
|
|
||||||
export interface RestConfig {
|
export interface RestConfig {
|
||||||
url: string
|
url: string
|
||||||
rejectUnauthorized: boolean
|
rejectUnauthorized?: boolean
|
||||||
downloadImages?: boolean
|
downloadImages?: boolean
|
||||||
defaultHeaders: {
|
defaultHeaders?: {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
legacyHttpParser: boolean
|
legacyHttpParser?: boolean
|
||||||
authConfigs: RestAuthConfig[]
|
authConfigs?: RestAuthConfig[]
|
||||||
staticVariables: {
|
staticVariables?: {
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
}
|
}
|
||||||
dynamicVariables: DynamicVariable[]
|
dynamicVariables?: DynamicVariable[]
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,31 +36,39 @@ export interface QueryResponse {
|
||||||
pagination: any
|
pagination: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum BodyType {
|
||||||
|
NONE = "none",
|
||||||
|
FORM_DATA = "form",
|
||||||
|
XML = "xml",
|
||||||
|
ENCODED = "encoded",
|
||||||
|
JSON = "json",
|
||||||
|
TEXT = "text",
|
||||||
|
}
|
||||||
|
|
||||||
export interface RestQueryFields {
|
export interface RestQueryFields {
|
||||||
path: string
|
path?: string
|
||||||
queryString?: string
|
queryString?: string
|
||||||
headers: { [key: string]: any }
|
headers?: { [key: string]: any }
|
||||||
disabledHeaders: { [key: string]: any }
|
disabledHeaders?: { [key: string]: any }
|
||||||
requestBody: any
|
requestBody?: any
|
||||||
bodyType: string
|
bodyType?: BodyType
|
||||||
json: object
|
method?: string
|
||||||
method: string
|
authConfigId?: string
|
||||||
authConfigId: string
|
pagination?: PaginationConfig
|
||||||
pagination: PaginationConfig | null
|
paginationValues?: PaginationValues
|
||||||
paginationValues: PaginationValues | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaginationConfig {
|
export interface PaginationConfig {
|
||||||
type: string
|
type: string
|
||||||
location: string
|
location: string
|
||||||
pageParam: string
|
pageParam: string
|
||||||
sizeParam: string | null
|
sizeParam?: string
|
||||||
responseParam: string | null
|
responseParam?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaginationValues {
|
export interface PaginationValues {
|
||||||
page: string | number | null
|
page?: string | number
|
||||||
limit: number | null
|
limit?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum HttpMethod {
|
export enum HttpMethod {
|
||||||
|
|
Loading…
Reference in New Issue