Merge branch 'develop' of github.com:Budibase/budibase into feature/portal-usage
This commit is contained in:
commit
e61efc5474
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.0.159-alpha.2",
|
"version": "1.0.164-alpha.3",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-json": "^4.0.2",
|
"@rollup/plugin-json": "^4.0.2",
|
||||||
|
"@types/mongodb": "3.6.3",
|
||||||
|
"@typescript-eslint/parser": "4.28.0",
|
||||||
"babel-eslint": "^10.0.3",
|
"babel-eslint": "^10.0.3",
|
||||||
"eslint": "^7.28.0",
|
"eslint": "^7.28.0",
|
||||||
"eslint-plugin-cypress": "^2.11.3",
|
"eslint-plugin-cypress": "^2.11.3",
|
||||||
|
@ -16,7 +18,6 @@
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup-plugin-replace": "^2.2.0",
|
"rollup-plugin-replace": "^2.2.0",
|
||||||
"svelte": "^3.38.2",
|
"svelte": "^3.38.2",
|
||||||
"@typescript-eslint/parser": "4.28.0",
|
|
||||||
"typescript": "4.5.5"
|
"typescript": "4.5.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/backend-core",
|
"name": "@budibase/backend-core",
|
||||||
"version": "1.0.159-alpha.2",
|
"version": "1.0.164-alpha.3",
|
||||||
"description": "Budibase backend core libraries used in server and worker",
|
"description": "Budibase backend core libraries used in server and worker",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
|
|
|
@ -1,61 +1,194 @@
|
||||||
|
require("../../tests/utilities/dbConfig");
|
||||||
const {
|
const {
|
||||||
generateAppID,
|
generateAppID,
|
||||||
getDevelopmentAppID,
|
getDevelopmentAppID,
|
||||||
getProdAppID,
|
getProdAppID,
|
||||||
isDevAppID,
|
isDevAppID,
|
||||||
isProdAppID,
|
isProdAppID,
|
||||||
|
getPlatformUrl,
|
||||||
|
getScopedConfig
|
||||||
} = require("../utils")
|
} = require("../utils")
|
||||||
|
const tenancy = require("../../tenancy");
|
||||||
|
const { Configs, DEFAULT_TENANT_ID } = require("../../constants");
|
||||||
|
const env = require("../../environment")
|
||||||
|
|
||||||
function getID() {
|
describe("utils", () => {
|
||||||
const appId = generateAppID()
|
describe("app ID manipulation", () => {
|
||||||
const split = appId.split("_")
|
|
||||||
const uuid = split[split.length - 1]
|
function getID() {
|
||||||
const devAppId = `app_dev_${uuid}`
|
const appId = generateAppID()
|
||||||
return { appId, devAppId, split, uuid }
|
const split = appId.split("_")
|
||||||
|
const uuid = split[split.length - 1]
|
||||||
|
const devAppId = `app_dev_${uuid}`
|
||||||
|
return { appId, devAppId, split, uuid }
|
||||||
|
}
|
||||||
|
|
||||||
|
it("should be able to generate a new app ID", () => {
|
||||||
|
expect(generateAppID().startsWith("app_")).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to convert a production app ID to development", () => {
|
||||||
|
const { appId, uuid } = getID()
|
||||||
|
expect(getDevelopmentAppID(appId)).toEqual(`app_dev_${uuid}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to convert a development app ID to development", () => {
|
||||||
|
const { devAppId, uuid } = getID()
|
||||||
|
expect(getDevelopmentAppID(devAppId)).toEqual(`app_dev_${uuid}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to convert a development ID to a production", () => {
|
||||||
|
const { devAppId, uuid } = getID()
|
||||||
|
expect(getProdAppID(devAppId)).toEqual(`app_${uuid}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to convert a production ID to production", () => {
|
||||||
|
const { appId, uuid } = getID()
|
||||||
|
expect(getProdAppID(appId)).toEqual(`app_${uuid}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to confirm dev app ID is development", () => {
|
||||||
|
const { devAppId } = getID()
|
||||||
|
expect(isDevAppID(devAppId)).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to confirm prod app ID is not development", () => {
|
||||||
|
const { appId } = getID()
|
||||||
|
expect(isDevAppID(appId)).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to confirm prod app ID is prod", () => {
|
||||||
|
const { appId } = getID()
|
||||||
|
expect(isProdAppID(appId)).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to confirm dev app ID is not prod", () => {
|
||||||
|
const { devAppId } = getID()
|
||||||
|
expect(isProdAppID(devAppId)).toEqual(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const DB_URL = "http://dburl.com"
|
||||||
|
const DEFAULT_URL = "http://localhost:10000"
|
||||||
|
const ENV_URL = "http://env.com"
|
||||||
|
|
||||||
|
const setDbPlatformUrl = async () => {
|
||||||
|
const db = tenancy.getGlobalDB()
|
||||||
|
db.put({
|
||||||
|
_id: "config_settings",
|
||||||
|
type: Configs.SETTINGS,
|
||||||
|
config: {
|
||||||
|
platformUrl: DB_URL
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("app ID manipulation", () => {
|
const clearSettingsConfig = async () => {
|
||||||
it("should be able to generate a new app ID", () => {
|
await tenancy.doInTenant(DEFAULT_TENANT_ID, async () => {
|
||||||
expect(generateAppID().startsWith("app_")).toEqual(true)
|
const db = tenancy.getGlobalDB()
|
||||||
|
try {
|
||||||
|
const config = await db.get("config_settings")
|
||||||
|
await db.remove("config_settings", config._rev)
|
||||||
|
} catch (e) {
|
||||||
|
if (e.status !== 404) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("getPlatformUrl", () => {
|
||||||
|
describe("self host", () => {
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
env._set("SELF_HOST", 1)
|
||||||
|
await clearSettingsConfig()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("gets the default url", async () => {
|
||||||
|
await tenancy.doInTenant(null, async () => {
|
||||||
|
const url = await getPlatformUrl()
|
||||||
|
expect(url).toBe(DEFAULT_URL)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("gets the platform url from the environment", async () => {
|
||||||
|
await tenancy.doInTenant(null, async () => {
|
||||||
|
env._set("PLATFORM_URL", ENV_URL)
|
||||||
|
const url = await getPlatformUrl()
|
||||||
|
expect(url).toBe(ENV_URL)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("gets the platform url from the database", async () => {
|
||||||
|
await tenancy.doInTenant(null, async () => {
|
||||||
|
await setDbPlatformUrl()
|
||||||
|
const url = await getPlatformUrl()
|
||||||
|
expect(url).toBe(DB_URL)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to convert a production app ID to development", () => {
|
|
||||||
const { appId, uuid } = getID()
|
|
||||||
expect(getDevelopmentAppID(appId)).toEqual(`app_dev_${uuid}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to convert a development app ID to development", () => {
|
describe("cloud", () => {
|
||||||
const { devAppId, uuid } = getID()
|
const TENANT_AWARE_URL = "http://default.env.com"
|
||||||
expect(getDevelopmentAppID(devAppId)).toEqual(`app_dev_${uuid}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to convert a development ID to a production", () => {
|
beforeEach(async () => {
|
||||||
const { devAppId, uuid } = getID()
|
env._set("SELF_HOSTED", 0)
|
||||||
expect(getProdAppID(devAppId)).toEqual(`app_${uuid}`)
|
env._set("MULTI_TENANCY", 1)
|
||||||
})
|
env._set("PLATFORM_URL", ENV_URL)
|
||||||
|
await clearSettingsConfig()
|
||||||
|
})
|
||||||
|
|
||||||
it("should be able to convert a production ID to production", () => {
|
it("gets the platform url from the environment without tenancy", async () => {
|
||||||
const { appId, uuid } = getID()
|
await tenancy.doInTenant(DEFAULT_TENANT_ID, async () => {
|
||||||
expect(getProdAppID(appId)).toEqual(`app_${uuid}`)
|
const url = await getPlatformUrl({ tenantAware: false })
|
||||||
})
|
expect(url).toBe(ENV_URL)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("should be able to confirm dev app ID is development", () => {
|
it("gets the platform url from the environment with tenancy", async () => {
|
||||||
const { devAppId } = getID()
|
await tenancy.doInTenant(DEFAULT_TENANT_ID, async () => {
|
||||||
expect(isDevAppID(devAppId)).toEqual(true)
|
const url = await getPlatformUrl()
|
||||||
})
|
expect(url).toBe(TENANT_AWARE_URL)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("should be able to confirm prod app ID is not development", () => {
|
it("never gets the platform url from the database", async () => {
|
||||||
const { appId } = getID()
|
await tenancy.doInTenant(DEFAULT_TENANT_ID, async () => {
|
||||||
expect(isDevAppID(appId)).toEqual(false)
|
await setDbPlatformUrl()
|
||||||
|
const url = await getPlatformUrl()
|
||||||
|
expect(url).toBe(TENANT_AWARE_URL)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("should be able to confirm prod app ID is prod", () => {
|
describe("getScopedConfig", () => {
|
||||||
const { appId } = getID()
|
describe("settings config", () => {
|
||||||
expect(isProdAppID(appId)).toEqual(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to confirm dev app ID is not prod", () => {
|
beforeEach(async () => {
|
||||||
const { devAppId } = getID()
|
env._set("SELF_HOSTED", 1)
|
||||||
expect(isProdAppID(devAppId)).toEqual(false)
|
env._set("PLATFORM_URL", "")
|
||||||
|
await clearSettingsConfig()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns the platform url with an existing config", async () => {
|
||||||
|
await tenancy.doInTenant(DEFAULT_TENANT_ID, async () => {
|
||||||
|
await setDbPlatformUrl()
|
||||||
|
const db = tenancy.getGlobalDB()
|
||||||
|
const config = await getScopedConfig(db, { type: Configs.SETTINGS })
|
||||||
|
expect(config.platformUrl).toBe(DB_URL)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns the platform url without an existing config", async () => {
|
||||||
|
await tenancy.doInTenant(DEFAULT_TENANT_ID, async () => {
|
||||||
|
const db = tenancy.getGlobalDB()
|
||||||
|
const config = await getScopedConfig(db, { type: Configs.SETTINGS })
|
||||||
|
expect(config.platformUrl).toBe(DEFAULT_URL)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,7 +9,7 @@ const {
|
||||||
APP_PREFIX,
|
APP_PREFIX,
|
||||||
APP_DEV,
|
APP_DEV,
|
||||||
} = require("./constants")
|
} = require("./constants")
|
||||||
const { getTenantId, getGlobalDBName } = require("../tenancy")
|
const { getTenantId, getGlobalDBName, getGlobalDB } = require("../tenancy")
|
||||||
const fetch = require("node-fetch")
|
const fetch = require("node-fetch")
|
||||||
const { doWithDB, allDbs } = require("./index")
|
const { doWithDB, allDbs } = require("./index")
|
||||||
const { getCouchInfo } = require("./pouch")
|
const { getCouchInfo } = require("./pouch")
|
||||||
|
@ -392,9 +392,7 @@ const getScopedFullConfig = async function (db, { type, user, workspace }) {
|
||||||
// always provide the platform URL
|
// always provide the platform URL
|
||||||
if (type === Configs.SETTINGS) {
|
if (type === Configs.SETTINGS) {
|
||||||
if (scopedConfig && scopedConfig.doc) {
|
if (scopedConfig && scopedConfig.doc) {
|
||||||
scopedConfig.doc.config.platformUrl = await getPlatformUrl(
|
scopedConfig.doc.config.platformUrl = await getPlatformUrl()
|
||||||
scopedConfig.doc.config
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
scopedConfig = {
|
scopedConfig = {
|
||||||
doc: {
|
doc: {
|
||||||
|
@ -409,19 +407,30 @@ const getScopedFullConfig = async function (db, { type, user, workspace }) {
|
||||||
return scopedConfig && scopedConfig.doc
|
return scopedConfig && scopedConfig.doc
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPlatformUrl = async settings => {
|
const getPlatformUrl = async (opts = { tenantAware: true }) => {
|
||||||
let platformUrl = env.PLATFORM_URL || "http://localhost:10000"
|
let platformUrl = env.PLATFORM_URL || "http://localhost:10000"
|
||||||
|
|
||||||
if (!env.SELF_HOSTED && env.MULTI_TENANCY) {
|
if (!env.SELF_HOSTED && env.MULTI_TENANCY && opts.tenantAware) {
|
||||||
// cloud and multi tenant - add the tenant to the default platform url
|
// cloud and multi tenant - add the tenant to the default platform url
|
||||||
const tenantId = getTenantId()
|
const tenantId = getTenantId()
|
||||||
if (!platformUrl.includes("localhost:")) {
|
if (!platformUrl.includes("localhost:")) {
|
||||||
platformUrl = platformUrl.replace("://", `://${tenantId}.`)
|
platformUrl = platformUrl.replace("://", `://${tenantId}.`)
|
||||||
}
|
}
|
||||||
} else {
|
} else if (env.SELF_HOSTED) {
|
||||||
|
const db = getGlobalDB()
|
||||||
|
// get the doc directly instead of with getScopedConfig to prevent loop
|
||||||
|
let settings
|
||||||
|
try {
|
||||||
|
settings = await db.get(generateConfigID({ type: Configs.SETTINGS }))
|
||||||
|
} catch (e) {
|
||||||
|
if (e.status !== 404) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// self hosted - check for platform url override
|
// self hosted - check for platform url override
|
||||||
if (settings && settings.platformUrl) {
|
if (settings && settings.config && settings.config.platformUrl) {
|
||||||
platformUrl = settings.platformUrl
|
platformUrl = settings.config.platformUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,20 +21,12 @@ async function fetchGoogleCreds() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function platformUrl() {
|
|
||||||
const db = getGlobalDB()
|
|
||||||
const publicConfig = await getScopedConfig(db, {
|
|
||||||
type: Configs.SETTINGS,
|
|
||||||
})
|
|
||||||
return getPlatformUrl(publicConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function preAuth(passport, ctx, next) {
|
async function preAuth(passport, ctx, next) {
|
||||||
// get the relevant config
|
// get the relevant config
|
||||||
const googleConfig = await fetchGoogleCreds()
|
const googleConfig = await fetchGoogleCreds()
|
||||||
const platUrl = await platformUrl()
|
const platformUrl = await getPlatformUrl({ tenantAware: false })
|
||||||
|
|
||||||
let callbackUrl = `${platUrl}/api/global/auth/datasource/google/callback`
|
let callbackUrl = `${platformUrl}/api/global/auth/datasource/google/callback`
|
||||||
const strategy = await google.strategyFactory(googleConfig, callbackUrl)
|
const strategy = await google.strategyFactory(googleConfig, callbackUrl)
|
||||||
|
|
||||||
if (!ctx.query.appId || !ctx.query.datasourceId) {
|
if (!ctx.query.appId || !ctx.query.datasourceId) {
|
||||||
|
@ -51,9 +43,9 @@ async function preAuth(passport, ctx, next) {
|
||||||
async function postAuth(passport, ctx, next) {
|
async function postAuth(passport, ctx, next) {
|
||||||
// get the relevant config
|
// get the relevant config
|
||||||
const config = await fetchGoogleCreds()
|
const config = await fetchGoogleCreds()
|
||||||
const platUrl = await platformUrl()
|
const platformUrl = await getPlatformUrl({ tenantAware: false })
|
||||||
|
|
||||||
let callbackUrl = `${platUrl}/api/global/auth/datasource/google/callback`
|
let callbackUrl = `${platformUrl}/api/global/auth/datasource/google/callback`
|
||||||
const strategy = await google.strategyFactory(
|
const strategy = await google.strategyFactory(
|
||||||
config,
|
config,
|
||||||
callbackUrl,
|
callbackUrl,
|
||||||
|
|
|
@ -71,7 +71,7 @@ describe("oidc", () => {
|
||||||
|
|
||||||
describe("authenticate", () => {
|
describe("authenticate", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks()
|
||||||
});
|
});
|
||||||
|
|
||||||
// mock third party common authentication
|
// mock third party common authentication
|
||||||
|
@ -80,10 +80,10 @@ describe("oidc", () => {
|
||||||
|
|
||||||
// mock the passport callback
|
// mock the passport callback
|
||||||
const mockDone = jest.fn()
|
const mockDone = jest.fn()
|
||||||
|
const mockSaveUserFn = jest.fn()
|
||||||
|
|
||||||
async function doAuthenticate() {
|
async function doAuthenticate() {
|
||||||
const oidc = require("../oidc")
|
const oidc = require("../oidc")
|
||||||
const mockSaveUserFn = jest.fn()
|
|
||||||
const authenticate = await oidc.buildVerifyFn(mockSaveUserFn)
|
const authenticate = await oidc.buildVerifyFn(mockSaveUserFn)
|
||||||
|
|
||||||
await authenticate(
|
await authenticate(
|
||||||
|
@ -105,11 +105,13 @@ describe("oidc", () => {
|
||||||
expect(authenticateThirdParty).toHaveBeenCalledWith(
|
expect(authenticateThirdParty).toHaveBeenCalledWith(
|
||||||
user,
|
user,
|
||||||
false,
|
false,
|
||||||
mockDone)
|
mockDone,
|
||||||
|
mockSaveUserFn,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
it("delegates authentication to third party common", async () => {
|
it("delegates authentication to third party common", async () => {
|
||||||
doTest()
|
await doTest()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("uses JWT email to get email", async () => {
|
it("uses JWT email to get email", async () => {
|
||||||
|
@ -118,7 +120,7 @@ describe("oidc", () => {
|
||||||
email : "mock@budibase.com"
|
email : "mock@budibase.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
doTest()
|
await doTest()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("uses JWT username to get email", async () => {
|
it("uses JWT username to get email", async () => {
|
||||||
|
@ -127,7 +129,7 @@ describe("oidc", () => {
|
||||||
preferred_username : "mock@budibase.com"
|
preferred_username : "mock@budibase.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
doTest()
|
await doTest()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("uses JWT invalid username to get email", async () => {
|
it("uses JWT invalid username to get email", async () => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/bbui",
|
"name": "@budibase/bbui",
|
||||||
"description": "A UI solution used in the different Budibase projects.",
|
"description": "A UI solution used in the different Budibase projects.",
|
||||||
"version": "1.0.159-alpha.2",
|
"version": "1.0.164-alpha.3",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"module": "dist/bbui.es.js",
|
"module": "dist/bbui.es.js",
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
||||||
"@budibase/string-templates": "^1.0.159-alpha.2",
|
"@budibase/string-templates": "^1.0.164-alpha.3",
|
||||||
"@spectrum-css/actionbutton": "^1.0.1",
|
"@spectrum-css/actionbutton": "^1.0.1",
|
||||||
"@spectrum-css/actiongroup": "^1.0.1",
|
"@spectrum-css/actiongroup": "^1.0.1",
|
||||||
"@spectrum-css/avatar": "^3.0.2",
|
"@spectrum-css/avatar": "^3.0.2",
|
||||||
|
|
|
@ -58,6 +58,11 @@
|
||||||
if (timeOnly) {
|
if (timeOnly) {
|
||||||
newValue = `2000-01-01T${newValue.split("T")[1]}`
|
newValue = `2000-01-01T${newValue.split("T")[1]}`
|
||||||
}
|
}
|
||||||
|
// date only, offset for timezone so always right date
|
||||||
|
else if (!enableTime) {
|
||||||
|
const offset = dates[0].getTimezoneOffset() * 60000
|
||||||
|
newValue = new Date(dates[0].getTime() - offset).toISOString()
|
||||||
|
}
|
||||||
dispatch("change", newValue)
|
dispatch("change", newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import "@spectrum-css/textfield/dist/index-vars.css"
|
import "@spectrum-css/textfield/dist/index-vars.css"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher, onMount } from "svelte"
|
||||||
|
|
||||||
export let value = null
|
export let value = null
|
||||||
export let placeholder = null
|
export let placeholder = null
|
||||||
|
@ -13,8 +13,11 @@
|
||||||
export let quiet = false
|
export let quiet = false
|
||||||
export let dataCy
|
export let dataCy
|
||||||
export let align
|
export let align
|
||||||
|
export let autofocus = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
let field
|
||||||
let focus = false
|
let focus = false
|
||||||
|
|
||||||
const updateValue = newValue => {
|
const updateValue = newValue => {
|
||||||
|
@ -58,6 +61,11 @@
|
||||||
updateValue(event.target.value)
|
updateValue(event.target.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
focus = autofocus
|
||||||
|
if (focus) field.focus()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -77,6 +85,7 @@
|
||||||
</svg>
|
</svg>
|
||||||
{/if}
|
{/if}
|
||||||
<input
|
<input
|
||||||
|
bind:this={field}
|
||||||
{disabled}
|
{disabled}
|
||||||
{readonly}
|
{readonly}
|
||||||
{id}
|
{id}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
export let updateOnChange = true
|
export let updateOnChange = true
|
||||||
export let quiet = false
|
export let quiet = false
|
||||||
export let dataCy
|
export let dataCy
|
||||||
|
export let autofocus
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{type}
|
{type}
|
||||||
{quiet}
|
{quiet}
|
||||||
|
{autofocus}
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
on:click
|
on:click
|
||||||
on:input
|
on:input
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,4 +1,5 @@
|
||||||
import filterTests from '../support/filterTests'
|
import filterTests from '../support/filterTests'
|
||||||
|
const interact = require('../support/interact')
|
||||||
|
|
||||||
filterTests(['smoke', 'all'], () => {
|
filterTests(['smoke', 'all'], () => {
|
||||||
context("Create an Application", () => {
|
context("Create an Application", () => {
|
||||||
|
@ -10,14 +11,14 @@ filterTests(['smoke', 'all'], () => {
|
||||||
|
|
||||||
if (!(Cypress.env("TEST_ENV"))) {
|
if (!(Cypress.env("TEST_ENV"))) {
|
||||||
it("should show the new user UI/UX", () => {
|
it("should show the new user UI/UX", () => {
|
||||||
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/create`) //added /portal/apps/create
|
||||||
cy.get(`[data-cy="create-app-btn"]`).contains('Start from scratch').should("exist")
|
cy.get(interact.CREATE_APP_BUTTON).contains('Start from scratch').should("exist")
|
||||||
cy.get(`[data-cy="import-app-btn"]`).should("exist")
|
cy.get(interact.CREATE_APP_BUTTON).should("exist")
|
||||||
|
|
||||||
cy.get(".template-category-filters").should("exist")
|
cy.get(interact.TEMPLATE_CATEGORY_FILTER).should("exist")
|
||||||
cy.get(".template-categories").should("exist")
|
cy.get(interact.TEMPLATE_CATEGORY).should("exist")
|
||||||
|
|
||||||
cy.get(".appTable").should("not.exist")
|
cy.get(interact.APP_TABLE).should("not.exist")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,21 +30,21 @@ filterTests(['smoke', 'all'], () => {
|
||||||
.its("body")
|
.its("body")
|
||||||
.then(val => {
|
.then(val => {
|
||||||
if (val.length > 0) {
|
if (val.length > 0) {
|
||||||
cy.get(".spectrum-Button").contains("Templates").click({force: true})
|
cy.get(interact.SPECTRUM_BUTTON_TEMPLATE).contains("Templates").click({force: true})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
cy.get(".template-category-filters").should("exist")
|
cy.get(interact.TEMPLATE_CATEGORY_FILTER).should("exist")
|
||||||
cy.get(".template-categories").should("exist")
|
cy.get(interact.TEMPLATE_CATEGORY).should("exist")
|
||||||
|
|
||||||
cy.get(".template-category").its('length').should('be.gt', 1)
|
cy.get(interact.TEMPLATE_CATEGORY_ACTIONGROUP).its('length').should('be.gt', 1)
|
||||||
cy.get(".template-category-filters .spectrum-ActionButton").its('length').should('be.gt', 2)
|
cy.get(interact.TEMPLATE_CATEGORY_FILTER_ACTIONBUTTON).its('length').should('be.gt', 2)
|
||||||
|
|
||||||
cy.get(".template-category-filters .spectrum-ActionButton").eq(1).click()
|
cy.get(interact.TEMPLATE_CATEGORY_FILTER_ACTIONBUTTON).eq(1).click()
|
||||||
cy.get(".template-category").should('have.length', 1)
|
cy.get(interact.TEMPLATE_CATEGORY_ACTIONGROUP).should('have.length', 1)
|
||||||
|
|
||||||
cy.get(".template-category-filters .spectrum-ActionButton").eq(0).click()
|
cy.get(interact.TEMPLATE_CATEGORY_FILTER_ACTIONBUTTON).eq(0).click()
|
||||||
cy.get(".template-category").its('length').should('be.gt', 1)
|
cy.get(interact.TEMPLATE_CATEGORY_ACTIONGROUP).its('length').should('be.gt', 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should enforce a valid url before submission", () => {
|
it("should enforce a valid url before submission", () => {
|
||||||
|
@ -51,37 +52,40 @@ filterTests(['smoke', 'all'], () => {
|
||||||
cy.wait(500)
|
cy.wait(500)
|
||||||
|
|
||||||
// Start create app process. If apps already exist, click second button
|
// Start create app process. If apps already exist, click second button
|
||||||
cy.get(`[data-cy="create-app-btn"]`).click({ force: true })
|
cy.get(interact.CREATE_APP_BUTTON).click({ force: true })
|
||||||
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
||||||
.its("body")
|
.its("body")
|
||||||
.then(val => {
|
.then(val => {
|
||||||
if (val.length > 0) {
|
if (val.length > 0) {
|
||||||
cy.get(`[data-cy="create-app-btn"]`).click({ force: true })
|
cy.get(interact.CREATE_APP_BUTTON).click({ force: true })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const appName = "Cypress Tests"
|
const appName = "Cypress Tests"
|
||||||
cy.get(".spectrum-Modal").within(() => {
|
cy.get(interact.SPECTRUM_MODAL).within(() => {
|
||||||
|
|
||||||
|
cy.get(interact.APP_NAME_INPUT).eq(0).should('have.focus')
|
||||||
|
|
||||||
//Auto fill
|
//Auto fill
|
||||||
cy.get("input").eq(0).type(appName).should("have.value", appName).blur()
|
cy.get(interact.APP_NAME_INPUT).eq(0).clear()
|
||||||
cy.get("input").eq(1).should("have.value", "/cypress-tests")
|
cy.get(interact.APP_NAME_INPUT).eq(0).type(appName).should("have.value", appName).blur()
|
||||||
cy.get(".spectrum-ButtonGroup").contains("Create app").should('not.be.disabled')
|
cy.get(interact.APP_NAME_INPUT).eq(1).should("have.value", "/cypress-tests")
|
||||||
|
cy.get(interact.SPECTRUM_BUTTON_GROUP).contains("Create app").should('not.be.disabled')
|
||||||
|
|
||||||
//Empty the app url - disabled create
|
//Empty the app url - disabled create
|
||||||
cy.get("input").eq(1).clear().blur()
|
cy.get(interact.APP_NAME_INPUT).eq(1).clear().blur()
|
||||||
cy.get(".spectrum-ButtonGroup").contains("Create app").should('be.disabled')
|
cy.get(interact.SPECTRUM_BUTTON_GROUP).contains("Create app").should('be.disabled')
|
||||||
|
|
||||||
//Invalid url
|
//Invalid url
|
||||||
cy.get("input").eq(1).type("/new app-url").blur()
|
cy.get(interact.APP_NAME_INPUT).eq(1).type("/new app-url").blur()
|
||||||
cy.get(".spectrum-ButtonGroup").contains("Create app").should('be.disabled')
|
cy.get(interact.SPECTRUM_BUTTON_GROUP).contains("Create app").should('be.disabled')
|
||||||
|
|
||||||
//Specifically alter the url
|
//Specifically alter the url
|
||||||
cy.get("input").eq(1).clear()
|
cy.get(interact.APP_NAME_INPUT).eq(1).clear()
|
||||||
cy.get("input").eq(1).type("another-app-name").blur()
|
cy.get(interact.APP_NAME_INPUT).eq(1).type("another-app-name").blur()
|
||||||
cy.get("input").eq(1).should("have.value", "/another-app-name")
|
cy.get(interact.APP_NAME_INPUT).eq(1).should("have.value", "/another-app-name")
|
||||||
cy.get("input").eq(0).should("have.value", appName)
|
cy.get(interact.APP_NAME_INPUT).eq(0).should("have.value", appName)
|
||||||
cy.get(".spectrum-ButtonGroup").contains("Create app").should('not.be.disabled')
|
cy.get(interact.SPECTRUM_BUTTON_GROUP).contains("Create app").should('not.be.disabled')
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -97,6 +101,77 @@ filterTests(['smoke', 'all'], () => {
|
||||||
cy.deleteApp(appName)
|
cy.deleteApp(appName)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should create the first application from scratch with a default name", () => {
|
||||||
|
cy.createApp()
|
||||||
|
|
||||||
|
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||||
|
cy.wait(1000)
|
||||||
|
|
||||||
|
cy.applicationInAppTable("My app")
|
||||||
|
cy.deleteApp("My app")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create the first application from scratch, using the users first name as the default app name", () => {
|
||||||
|
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||||
|
|
||||||
|
cy.updateUserInformation("Ted", "Userman")
|
||||||
|
|
||||||
|
cy.createApp()
|
||||||
|
|
||||||
|
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||||
|
cy.wait(1000)
|
||||||
|
|
||||||
|
cy.applicationInAppTable("Teds app")
|
||||||
|
cy.deleteApp("Teds app")
|
||||||
|
cy.wait(2000)
|
||||||
|
|
||||||
|
//Accomodate names that end in 'S'
|
||||||
|
cy.updateUserInformation("Chris", "Userman")
|
||||||
|
|
||||||
|
cy.createApp()
|
||||||
|
|
||||||
|
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||||
|
cy.wait(1000)
|
||||||
|
|
||||||
|
cy.applicationInAppTable("Chris app")
|
||||||
|
cy.deleteApp("Chris app")
|
||||||
|
cy.wait(2000)
|
||||||
|
|
||||||
|
cy.updateUserInformation("", "")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create an application from an export", () => {
|
||||||
|
const exportedApp = 'cypress/fixtures/exported-app.txt'
|
||||||
|
|
||||||
|
cy.importApp(exportedApp, "")
|
||||||
|
|
||||||
|
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||||
|
|
||||||
|
cy.applicationInAppTable("My app")
|
||||||
|
|
||||||
|
cy.get(".appTable .name").eq(0).click()
|
||||||
|
|
||||||
|
cy.deleteApp("My app")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create an application from an export, using the users first name as the default app name", () => {
|
||||||
|
const exportedApp = 'cypress/fixtures/exported-app.txt'
|
||||||
|
|
||||||
|
cy.updateUserInformation("Ted", "Userman")
|
||||||
|
|
||||||
|
cy.importApp(exportedApp, "")
|
||||||
|
|
||||||
|
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||||
|
|
||||||
|
cy.applicationInAppTable("Teds app")
|
||||||
|
|
||||||
|
cy.get(".appTable .name").eq(0).click()
|
||||||
|
|
||||||
|
cy.deleteApp("Teds app")
|
||||||
|
|
||||||
|
cy.updateUserInformation("", "")
|
||||||
|
})
|
||||||
|
|
||||||
it("should generate the first application from a template", () => {
|
it("should generate the first application from a template", () => {
|
||||||
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||||
cy.wait(500)
|
cy.wait(500)
|
||||||
|
@ -106,15 +181,15 @@ filterTests(['smoke', 'all'], () => {
|
||||||
.its("body")
|
.its("body")
|
||||||
.then(val => {
|
.then(val => {
|
||||||
if (val.length > 0) {
|
if (val.length > 0) {
|
||||||
cy.get(`[data-cy="create-app-btn"]`).click({ force: true })
|
cy.get(interact.CREATE_APP_BUTTON).click({ force: true })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
cy.get(".template-category-filters").should("exist")
|
cy.get(interact.TEMPLATE_CATEGORY_FILTER).should("exist")
|
||||||
cy.get(".template-categories").should("exist")
|
cy.get(interact.TEMPLATE_CATEGORY).should("exist")
|
||||||
|
|
||||||
// Select template
|
// Select template
|
||||||
cy.get('.template-category').eq(0).within(() => {
|
cy.get(interact.TEMPLATE_CATEGORY_ACTIONGROUP).eq(0).within(() => {
|
||||||
const card = cy.get('.template-card').eq(0).should("exist");
|
const card = cy.get('.template-card').eq(0).should("exist");
|
||||||
const cardOverlay = card.get('.template-thumbnail-action-overlay').should("exist")
|
const cardOverlay = card.get('.template-thumbnail-action-overlay').should("exist")
|
||||||
cardOverlay.invoke("show")
|
cardOverlay.invoke("show")
|
||||||
|
@ -128,8 +203,8 @@ filterTests(['smoke', 'all'], () => {
|
||||||
templateName.invoke('text')
|
templateName.invoke('text')
|
||||||
.then(templateNameText => {
|
.then(templateNameText => {
|
||||||
const templateNameParsed = "/"+templateNameText.toLowerCase().replace(/\s+/g, "-")
|
const templateNameParsed = "/"+templateNameText.toLowerCase().replace(/\s+/g, "-")
|
||||||
cy.get(".spectrum-Modal input").eq(0).should("have.value", templateNameText)
|
cy.get(interact.SPECTRUM_MODAL_INPUT).eq(0).should("have.value", templateNameText)
|
||||||
cy.get(".spectrum-Modal input").eq(1).should("have.value", templateNameParsed)
|
cy.get(interact.SPECTRUM_MODAL_INPUT).eq(1).should("have.value", templateNameParsed)
|
||||||
|
|
||||||
cy.get(".spectrum-Modal .spectrum-ButtonGroup").contains("Create app").click()
|
cy.get(".spectrum-Modal .spectrum-ButtonGroup").contains("Create app").click()
|
||||||
cy.wait(5000)
|
cy.wait(5000)
|
||||||
|
|
|
@ -4,6 +4,8 @@ filterTests(["smoke", "all"], () => {
|
||||||
context("Create a User and Assign Roles", () => {
|
context("Create a User and Assign Roles", () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.login()
|
cy.login()
|
||||||
|
cy.deleteApp("Cypress Tests")
|
||||||
|
cy.createApp("Cypress Tests")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create a user", () => {
|
it("should create a user", () => {
|
||||||
|
@ -52,7 +54,7 @@ filterTests(["smoke", "all"], () => {
|
||||||
cy.get(".spectrum-Table").contains("bbuser").click()
|
cy.get(".spectrum-Table").contains("bbuser").click()
|
||||||
cy.wait(1000)
|
cy.wait(1000)
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
cy.get(".spectrum-Table")
|
cy.get(".spectrum-Table", { timeout: 3000})
|
||||||
.eq(1)
|
.eq(1)
|
||||||
.find(".spectrum-Table-row")
|
.find(".spectrum-Table-row")
|
||||||
.eq(0)
|
.eq(0)
|
||||||
|
@ -79,6 +81,7 @@ filterTests(["smoke", "all"], () => {
|
||||||
.contains("Update role")
|
.contains("Update role")
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
})
|
})
|
||||||
|
cy.reload()
|
||||||
}
|
}
|
||||||
// Confirm roles exist within Configure roles table
|
// Confirm roles exist within Configure roles table
|
||||||
cy.wait(2000)
|
cy.wait(2000)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import filterTests from "../../../support/filterTests"
|
import filterTests from "../../../support/filterTests"
|
||||||
|
|
||||||
filterTests(["all"], () => {
|
filterTests(["all"], () => {
|
||||||
context("Job Application Functionality", () => {
|
context("Job Application Tracker Template Functionality", () => {
|
||||||
const templateName = "Job Application Tracker"
|
const templateName = "Job Application Tracker"
|
||||||
const templateNameParsed = templateName.toLowerCase().replace(/\s+/g, '-')
|
const templateNameParsed = templateName.toLowerCase().replace(/\s+/g, '-')
|
||||||
|
|
||||||
|
@ -14,15 +14,7 @@ filterTests(["all"], () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
cy.wait(2000)
|
cy.wait(2000)
|
||||||
|
cy.templateNavigation()
|
||||||
// Template navigation
|
|
||||||
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
|
||||||
.its("body")
|
|
||||||
.then(val => {
|
|
||||||
if (val.length > 0) {
|
|
||||||
cy.get(".spectrum-Button").contains("Templates").click({force: true})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create and publish app with Job Application Tracker template", () => {
|
it("should create and publish app with Job Application Tracker template", () => {
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
import filterTests from "../../../support/filterTests"
|
||||||
|
|
||||||
|
filterTests(["all"], () => {
|
||||||
|
context("IT Ticketing System Template Functionality", () => {
|
||||||
|
const templateName = "IT Ticketing System"
|
||||||
|
const templateNameParsed = templateName.toLowerCase().replace(/\s+/g, '-')
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
cy.login()
|
||||||
|
cy.deleteApp(templateName)
|
||||||
|
cy.visit(`${Cypress.config().baseUrl}/builder`, {
|
||||||
|
onBeforeLoad(win) {
|
||||||
|
cy.stub(win, 'open')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
cy.wait(2000)
|
||||||
|
cy.templateNavigation()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create and publish app with IT Ticketing System template", () => {
|
||||||
|
// Select IT Ticketing System template
|
||||||
|
cy.get(".template-thumbnail-text")
|
||||||
|
.contains(templateName).parentsUntil(".template-grid").within(() => {
|
||||||
|
cy.get(".spectrum-Button").contains("Use template").click({ force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Confirm URL matches template name
|
||||||
|
const appUrl = cy.get(".app-server")
|
||||||
|
appUrl.invoke('text').then(appUrlText => {
|
||||||
|
expect(appUrlText).to.equal(`${Cypress.config().baseUrl}/app/` + templateNameParsed)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create App
|
||||||
|
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||||
|
cy.get(".spectrum-Button").contains("Create app").click({ force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Publish App
|
||||||
|
cy.wait(2000) // Wait for app to generate
|
||||||
|
cy.get(".toprightnav").contains("Publish").click({ force: true })
|
||||||
|
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||||
|
cy.get(".spectrum-Button").contains("Publish").click({ force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Verify Published app
|
||||||
|
cy.wait(2000) // Wait for App to publish and modal to appear
|
||||||
|
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||||
|
cy.get(".spectrum-Button").contains("View App").click({ force: true })
|
||||||
|
cy.window().its('open').should('be.calledOnce')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
xit("should filter tickets by status", () => {
|
||||||
|
// Visit published app
|
||||||
|
cy.visit(`${Cypress.config().baseUrl}/app/` + templateNameParsed)
|
||||||
|
cy.wait(1000)
|
||||||
|
|
||||||
|
// Tickets section
|
||||||
|
cy.get(".links").contains("Tickets").click({ force: true })
|
||||||
|
cy.wait(1000)
|
||||||
|
|
||||||
|
// Filter by stage - Confirm table updates
|
||||||
|
cy.get(".spectrum-Picker").contains("Filter by status").click({ force: true })
|
||||||
|
cy.get(".spectrum-Menu").find('li').its('length').then(len => {
|
||||||
|
for (let i = 1; i < len; i++) {
|
||||||
|
cy.get(".spectrum-Menu-item").eq(i).click()
|
||||||
|
const stage = cy.get(".spectrum-Picker-label")
|
||||||
|
stage.invoke('text').then(stageText => {
|
||||||
|
if (stageText == "In progress" || stageText == "On hold" || stageText == "Triaged") {
|
||||||
|
cy.get(".placeholder").should('contain', 'No rows found')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cy.get(".spectrum-Table-row").should('contain', stageText)
|
||||||
|
}
|
||||||
|
cy.get(".spectrum-Picker").contains(stageText).click({ force: true })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -39,6 +39,71 @@ Cypress.Commands.add("closeModal", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add("importApp", (exportFilePath, name) => {
|
||||||
|
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||||
|
|
||||||
|
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
||||||
|
.its("body")
|
||||||
|
.then(val => {
|
||||||
|
if (val.length > 0) {
|
||||||
|
cy.get(`[data-cy="create-app-btn"]`).click({ force: true })
|
||||||
|
cy.wait(500)
|
||||||
|
}
|
||||||
|
cy.get(`[data-cy="import-app-btn"]`).click({ force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.get(".spectrum-Modal").within(() => {
|
||||||
|
cy.get("input").eq(1).should("have.focus")
|
||||||
|
|
||||||
|
cy.get(".spectrum-Dropzone").selectFile(exportFilePath, {
|
||||||
|
action: "drag-drop",
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.get(".gallery .filename").contains("exported-app.txt")
|
||||||
|
|
||||||
|
if (name && name != "") {
|
||||||
|
cy.get("input").eq(0).type(name).should("have.value", name).blur()
|
||||||
|
}
|
||||||
|
cy.get(".confirm-wrap button")
|
||||||
|
.should("not.be.disabled")
|
||||||
|
.click({ force: true })
|
||||||
|
cy.wait(5000)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add("updateUserInformation", (firstName, lastName) => {
|
||||||
|
cy.get(".user-dropdown .avatar > .icon").click({ force: true })
|
||||||
|
|
||||||
|
cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => {
|
||||||
|
cy.get("li[data-cy='user-info']").click({ force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.get(".spectrum-Modal.is-open").within(() => {
|
||||||
|
cy.get("[data-cy='user-first-name']").clear()
|
||||||
|
|
||||||
|
if (!firstName || firstName == "") {
|
||||||
|
cy.get("[data-cy='user-first-name']").invoke("val").should("be.empty")
|
||||||
|
} else {
|
||||||
|
cy.get("[data-cy='user-first-name']")
|
||||||
|
.type(firstName)
|
||||||
|
.should("have.value", firstName)
|
||||||
|
.blur()
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.get("[data-cy='user-last-name']").clear()
|
||||||
|
|
||||||
|
if (!lastName || lastName == "") {
|
||||||
|
cy.get("[data-cy='user-last-name']").invoke("val").should("be.empty")
|
||||||
|
} else {
|
||||||
|
cy.get("[data-cy='user-last-name']")
|
||||||
|
.type(lastName)
|
||||||
|
.should("have.value", lastName)
|
||||||
|
.blur()
|
||||||
|
}
|
||||||
|
cy.get("button").contains("Update information").click({ force: true })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("createApp", (name, addDefaultTable) => {
|
Cypress.Commands.add("createApp", (name, addDefaultTable) => {
|
||||||
const shouldCreateDefaultTable =
|
const shouldCreateDefaultTable =
|
||||||
typeof addDefaultTable != "boolean" ? true : addDefaultTable
|
typeof addDefaultTable != "boolean" ? true : addDefaultTable
|
||||||
|
@ -57,7 +122,11 @@ Cypress.Commands.add("createApp", (name, addDefaultTable) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
cy.get(".spectrum-Modal").within(() => {
|
cy.get(".spectrum-Modal").within(() => {
|
||||||
cy.get("input").eq(0).type(name).should("have.value", name).blur()
|
cy.get("input").eq(0).should("have.focus")
|
||||||
|
if (name && name != "") {
|
||||||
|
cy.get("input").eq(0).clear()
|
||||||
|
cy.get("input").eq(0).type(name).should("have.value", name).blur()
|
||||||
|
}
|
||||||
cy.get(".spectrum-ButtonGroup")
|
cy.get(".spectrum-ButtonGroup")
|
||||||
.contains("Create app")
|
.contains("Create app")
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
|
@ -455,7 +524,12 @@ Cypress.Commands.add("createAppFromScratch", appName => {
|
||||||
.contains("Start from scratch")
|
.contains("Start from scratch")
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
cy.get(".spectrum-Modal").within(() => {
|
cy.get(".spectrum-Modal").within(() => {
|
||||||
cy.get("input").eq(0).type(appName).should("have.value", appName).blur()
|
cy.get("input")
|
||||||
|
.eq(0)
|
||||||
|
.clear()
|
||||||
|
.type(appName)
|
||||||
|
.should("have.value", appName)
|
||||||
|
.blur()
|
||||||
cy.get(".spectrum-ButtonGroup").contains("Create app").click()
|
cy.get(".spectrum-ButtonGroup").contains("Create app").click()
|
||||||
cy.wait(10000)
|
cy.wait(10000)
|
||||||
})
|
})
|
||||||
|
@ -573,7 +647,8 @@ Cypress.Commands.add("addDatasourceConfig", (datasource, skipFetch) => {
|
||||||
cy.get(".spectrum-Button")
|
cy.get(".spectrum-Button")
|
||||||
.contains("Save and fetch tables")
|
.contains("Save and fetch tables")
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
cy.wait(3000)
|
// Check modal closes after datasource config & fetch
|
||||||
|
cy.get(".spectrum-Dialog-grid", { timeout: 7000 }).should("not.exist")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -595,3 +670,15 @@ Cypress.Commands.add("createRestQuery", (method, restUrl, queryPrettyName) => {
|
||||||
.should("contain", method)
|
.should("contain", method)
|
||||||
.and("contain", queryPrettyName)
|
.and("contain", queryPrettyName)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add("templateNavigation", () => {
|
||||||
|
// Navigates to templates section
|
||||||
|
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
||||||
|
.its("body")
|
||||||
|
.then(val => {
|
||||||
|
// Templates button needs clicked if apps already exist
|
||||||
|
if (val.length > 0) {
|
||||||
|
cy.get(".spectrum-Button").contains("Templates").click({ force: true })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
// createApp test
|
||||||
|
export const CREATE_APP_BUTTON = '[data-cy="create-app-btn"]'
|
||||||
|
export const TEMPLATE_CATEGORY_FILTER = ".template-category-filters"
|
||||||
|
export const TEMPLATE_CATEGORY = ".template-categories"
|
||||||
|
export const APP_TABLE = ".appTable"
|
||||||
|
export const SPECTRUM_BUTTON_TEMPLATE = ".spectrum-Button"
|
||||||
|
export const TEMPLATE_CATEGORY_ACTIONGROUP = ".template-category"
|
||||||
|
export const TEMPLATE_CATEGORY_FILTER_ACTIONBUTTON =
|
||||||
|
".template-category-filters .spectrum-ActionButton"
|
||||||
|
export const SPECTRUM_MODAL = ".spectrum-Modal"
|
||||||
|
export const APP_NAME_INPUT = "input" // we need to update this with atribute cy-data
|
||||||
|
export const SPECTRUM_BUTTON_GROUP = ".spectrum-ButtonGroup"
|
||||||
|
export const SPECTRUM_MODAL_INPUT = ".spectrum-Modal input"
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "1.0.159-alpha.2",
|
"version": "1.0.164-alpha.3",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -67,10 +67,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.159-alpha.2",
|
"@budibase/bbui": "^1.0.164-alpha.3",
|
||||||
"@budibase/client": "^1.0.159-alpha.2",
|
"@budibase/client": "^1.0.164-alpha.3",
|
||||||
"@budibase/frontend-core": "^1.0.159-alpha.2",
|
"@budibase/frontend-core": "^1.0.164-alpha.3",
|
||||||
"@budibase/string-templates": "^1.0.159-alpha.2",
|
"@budibase/string-templates": "^1.0.164-alpha.3",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@spectrum-css/page": "^3.0.1",
|
"@spectrum-css/page": "^3.0.1",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
|
|
|
@ -12,4 +12,4 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CopyInput {value} copyValue={fullWebhookURL(value)} />
|
<CopyInput value={fullWebhookURL(value)} />
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
export let label = null
|
export let label = null
|
||||||
export let value
|
export let value
|
||||||
export let copyValue
|
|
||||||
export let dataCy = null
|
export let dataCy = null
|
||||||
|
|
||||||
const copyToClipboard = val => {
|
const copyToClipboard = val => {
|
||||||
|
@ -19,7 +18,7 @@
|
||||||
|
|
||||||
<div data-cy={dataCy}>
|
<div data-cy={dataCy}>
|
||||||
<Input readonly {value} {label} />
|
<Input readonly {value} {label} />
|
||||||
<div class="icon" on:click={() => copyToClipboard(value || copyValue)}>
|
<div class="icon" on:click={() => copyToClipboard(value)}>
|
||||||
<Icon size="S" name="Copy" />
|
<Icon size="S" name="Copy" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -27,6 +27,14 @@
|
||||||
Personalise the platform by adding your first name and last name.
|
Personalise the platform by adding your first name and last name.
|
||||||
</Body>
|
</Body>
|
||||||
<Input disabled bind:value={$auth.user.email} label="Email" />
|
<Input disabled bind:value={$auth.user.email} label="Email" />
|
||||||
<Input bind:value={$values.firstName} label="First name" />
|
<Input
|
||||||
<Input bind:value={$values.lastName} label="Last name" />
|
bind:value={$values.firstName}
|
||||||
|
label="First name"
|
||||||
|
dataCy="user-first-name"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
bind:value={$values.lastName}
|
||||||
|
label="Last name"
|
||||||
|
dataCy="user-last-name"
|
||||||
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
|
@ -16,13 +16,26 @@
|
||||||
export let template
|
export let template
|
||||||
|
|
||||||
let creating = false
|
let creating = false
|
||||||
|
let defaultAppName
|
||||||
|
|
||||||
const values = writable({ name: "", url: null })
|
const values = writable({ name: "", url: null })
|
||||||
const validation = createValidationStore()
|
const validation = createValidationStore()
|
||||||
$: validation.check($values)
|
$: validation.check($values)
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
$values.name = resolveAppName(template, $values.name)
|
const lastChar = $auth.user?.firstName
|
||||||
|
? $auth.user?.firstName[$auth.user?.firstName.length - 1]
|
||||||
|
: null
|
||||||
|
|
||||||
|
defaultAppName =
|
||||||
|
lastChar && lastChar.toLowerCase() == "s"
|
||||||
|
? `${$auth.user?.firstName} app`
|
||||||
|
: `${$auth.user.firstName}s app`
|
||||||
|
|
||||||
|
$values.name = resolveAppName(
|
||||||
|
template,
|
||||||
|
!$auth.user?.firstName ? "My app" : defaultAppName
|
||||||
|
)
|
||||||
nameToUrl($values.name)
|
nameToUrl($values.name)
|
||||||
await setupValidation()
|
await setupValidation()
|
||||||
})
|
})
|
||||||
|
@ -44,7 +57,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolveAppName = (template, name) => {
|
const resolveAppName = (template, name) => {
|
||||||
if (template && !name) {
|
if (template && !template.fromFile) {
|
||||||
return template.name
|
return template.name
|
||||||
}
|
}
|
||||||
return name ? name.trim() : null
|
return name ? name.trim() : null
|
||||||
|
@ -83,7 +96,7 @@
|
||||||
}
|
}
|
||||||
data.append("useTemplate", template != null)
|
data.append("useTemplate", template != null)
|
||||||
if (template) {
|
if (template) {
|
||||||
data.append("templateName", template.name) //or here?
|
data.append("templateName", template.name)
|
||||||
data.append("templateKey", template.key)
|
data.append("templateKey", template.key)
|
||||||
data.append("templateFile", $values.file)
|
data.append("templateFile", $values.file)
|
||||||
}
|
}
|
||||||
|
@ -159,15 +172,14 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<Input
|
<Input
|
||||||
|
autofocus={true}
|
||||||
bind:value={$values.name}
|
bind:value={$values.name}
|
||||||
disabled={creating}
|
disabled={creating}
|
||||||
error={$validation.touched.name && $validation.errors.name}
|
error={$validation.touched.name && $validation.errors.name}
|
||||||
on:blur={() => ($validation.touched.name = true)}
|
on:blur={() => ($validation.touched.name = true)}
|
||||||
on:change={nameToUrl($values.name)}
|
on:change={nameToUrl($values.name)}
|
||||||
label="Name"
|
label="Name"
|
||||||
placeholder={$auth.user?.firstName
|
placeholder={defaultAppName}
|
||||||
? `${$auth.user.firstName}s app`
|
|
||||||
: "My app"}
|
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
<Input
|
<Input
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<img alt="logo" src={$organisation.logoUrl || Logo} />
|
<img alt="logo" src={$organisation.logoUrl || Logo} />
|
||||||
<ActionMenu align="right">
|
<ActionMenu align="right" dataCy="user-menu">
|
||||||
<div slot="control" class="avatar">
|
<div slot="control" class="avatar">
|
||||||
<Avatar
|
<Avatar
|
||||||
size="M"
|
size="M"
|
||||||
|
|
|
@ -169,7 +169,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-dropdown">
|
<div class="user-dropdown">
|
||||||
<ActionMenu align="right">
|
<ActionMenu align="right" dataCy="user-menu">
|
||||||
<div slot="control" class="avatar">
|
<div slot="control" class="avatar">
|
||||||
<Avatar
|
<Avatar
|
||||||
size="M"
|
size="M"
|
||||||
|
@ -178,7 +178,11 @@
|
||||||
/>
|
/>
|
||||||
<Icon size="XL" name="ChevronDown" />
|
<Icon size="XL" name="ChevronDown" />
|
||||||
</div>
|
</div>
|
||||||
<MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}>
|
<MenuItem
|
||||||
|
icon="UserEdit"
|
||||||
|
on:click={() => userInfoModal.show()}
|
||||||
|
dataCy={"user-info"}
|
||||||
|
>
|
||||||
Update user information
|
Update user information
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{#if $auth.isBuilder}
|
{#if $auth.isBuilder}
|
||||||
|
|
|
@ -68,6 +68,26 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteSmtp() {
|
||||||
|
// Delete the SMTP config
|
||||||
|
try {
|
||||||
|
await API.deleteConfig({
|
||||||
|
id: smtpConfig._id,
|
||||||
|
rev: smtpConfig._rev,
|
||||||
|
})
|
||||||
|
smtpConfig = {
|
||||||
|
config: {},
|
||||||
|
}
|
||||||
|
await admin.getChecklist()
|
||||||
|
notifications.success(`Settings cleared`)
|
||||||
|
analytics.captureEvent(Events.SMTP.SAVED)
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error(
|
||||||
|
`Failed to clear email settings, reason: ${error?.message || "Unknown"}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchSmtp() {
|
async function fetchSmtp() {
|
||||||
loading = true
|
loading = true
|
||||||
try {
|
try {
|
||||||
|
@ -156,8 +176,15 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</Layout>
|
</Layout>
|
||||||
<div>
|
<div class="spectrum-ButtonGroup spectrum-Settings-buttonGroup">
|
||||||
<Button cta on:click={saveSmtp}>Save</Button>
|
<Button cta on:click={saveSmtp}>Save</Button>
|
||||||
|
<Button
|
||||||
|
secondary
|
||||||
|
on:click={deleteSmtp}
|
||||||
|
disabled={!$admin.checklist.smtp.checked}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Divider size="S" />
|
<Divider size="S" />
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
|
@ -186,4 +213,8 @@
|
||||||
grid-gap: var(--spacing-l);
|
grid-gap: var(--spacing-l);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
.spectrum-Settings-buttonGroup {
|
||||||
|
gap: var(--spectrum-global-dimension-static-size-200);
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,7 +14,10 @@
|
||||||
let options = roles
|
let options = roles
|
||||||
.filter(role => role._id !== "PUBLIC")
|
.filter(role => role._id !== "PUBLIC")
|
||||||
.map(role => ({ value: role._id, label: role.name }))
|
.map(role => ({ value: role._id, label: role.name }))
|
||||||
options.push({ value: NO_ACCESS, label: "No Access" })
|
|
||||||
|
if (!user?.builder?.global) {
|
||||||
|
options.push({ value: NO_ACCESS, label: "No Access" })
|
||||||
|
}
|
||||||
let selectedRole = user?.roles?.[app?._id]
|
let selectedRole = user?.roles?.[app?._id]
|
||||||
|
|
||||||
async function updateUserRoles() {
|
async function updateUserRoles() {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "1.0.159-alpha.2",
|
"version": "1.0.164-alpha.3",
|
||||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "1.0.159-alpha.2",
|
"version": "1.0.164-alpha.3",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"module": "dist/budibase-client.js",
|
"module": "dist/budibase-client.js",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
|
@ -19,9 +19,9 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.159-alpha.2",
|
"@budibase/bbui": "^1.0.164-alpha.3",
|
||||||
"@budibase/frontend-core": "^1.0.159-alpha.2",
|
"@budibase/frontend-core": "^1.0.164-alpha.3",
|
||||||
"@budibase/string-templates": "^1.0.159-alpha.2",
|
"@budibase/string-templates": "^1.0.164-alpha.3",
|
||||||
"@spectrum-css/button": "^3.0.3",
|
"@spectrum-css/button": "^3.0.3",
|
||||||
"@spectrum-css/card": "^3.0.3",
|
"@spectrum-css/card": "^3.0.3",
|
||||||
"@spectrum-css/divider": "^1.0.3",
|
"@spectrum-css/divider": "^1.0.3",
|
||||||
|
|
|
@ -40,6 +40,15 @@ export const getOptions = (
|
||||||
|
|
||||||
// Extract custom options
|
// Extract custom options
|
||||||
if (optionsSource === "custom" && customOptions) {
|
if (optionsSource === "custom" && customOptions) {
|
||||||
|
customOptions.forEach(option => {
|
||||||
|
if (typeof option.value === "string") {
|
||||||
|
if (option.value.toLowerCase() === "true") {
|
||||||
|
option.value = true
|
||||||
|
} else if (option.value.toLowerCase() === "false") {
|
||||||
|
option.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
return customOptions
|
return customOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/frontend-core",
|
"name": "@budibase/frontend-core",
|
||||||
"version": "1.0.159-alpha.2",
|
"version": "1.0.164-alpha.3",
|
||||||
"description": "Budibase frontend core libraries used in builder and client",
|
"description": "Budibase frontend core libraries used in builder and client",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.159-alpha.2",
|
"@budibase/bbui": "^1.0.164-alpha.3",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"svelte": "^3.46.2"
|
"svelte": "^3.46.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,17 @@ export const buildConfigEndpoints = API => ({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a global config
|
||||||
|
* @param id the id of the config to delete
|
||||||
|
* @param rev the revision of the config to delete
|
||||||
|
*/
|
||||||
|
deleteConfig: async ({ id, rev }) => {
|
||||||
|
return await API.delete({
|
||||||
|
url: `/api/global/configs/${id}/${rev}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the config for a certain tenant.
|
* Gets the config for a certain tenant.
|
||||||
* @param tenantId the tenant ID to get the config for
|
* @param tenantId the tenant ID to get the config for
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.159-alpha.2",
|
"version": "1.0.164-alpha.3",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -69,10 +69,10 @@
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "^10.0.3",
|
"@apidevtools/swagger-parser": "^10.0.3",
|
||||||
"@budibase/backend-core": "^1.0.159-alpha.2",
|
"@budibase/backend-core": "^1.0.164-alpha.3",
|
||||||
"@budibase/client": "^1.0.159-alpha.2",
|
"@budibase/client": "^1.0.164-alpha.3",
|
||||||
"@budibase/pro": "1.0.159-alpha.2",
|
"@budibase/pro": "1.0.164-alpha.3",
|
||||||
"@budibase/string-templates": "^1.0.159-alpha.2",
|
"@budibase/string-templates": "^1.0.164-alpha.3",
|
||||||
"@bull-board/api": "^3.7.0",
|
"@bull-board/api": "^3.7.0",
|
||||||
"@bull-board/koa": "^3.7.0",
|
"@bull-board/koa": "^3.7.0",
|
||||||
"@elastic/elasticsearch": "7.10.0",
|
"@elastic/elasticsearch": "7.10.0",
|
||||||
|
@ -152,6 +152,7 @@
|
||||||
"@types/koa": "^2.13.3",
|
"@types/koa": "^2.13.3",
|
||||||
"@types/koa-router": "^7.4.2",
|
"@types/koa-router": "^7.4.2",
|
||||||
"@types/lodash": "4.14.180",
|
"@types/lodash": "4.14.180",
|
||||||
|
"@types/mongodb": "3.6.3",
|
||||||
"@types/node": "^15.12.4",
|
"@types/node": "^15.12.4",
|
||||||
"@types/oracledb": "^5.2.1",
|
"@types/oracledb": "^5.2.1",
|
||||||
"@types/redis": "^4.0.11",
|
"@types/redis": "^4.0.11",
|
||||||
|
|
|
@ -1,9 +1,66 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`viewBuilder Calculate and filter creates a view with the calculation statistics and filter schema 1`] = `
|
||||||
|
Object {
|
||||||
|
"map": "function (doc) {
|
||||||
|
if ((doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" && !(
|
||||||
|
doc[\\"myField\\"] === undefined ||
|
||||||
|
doc[\\"myField\\"] === null ||
|
||||||
|
doc[\\"myField\\"] === \\"\\" ||
|
||||||
|
(Array.isArray(doc[\\"myField\\"]) && doc[\\"myField\\"].length === 0)
|
||||||
|
)) && (doc[\\"age\\"] > 17)) {
|
||||||
|
emit(doc[\\"_id\\"], doc[\\"myField\\"]);
|
||||||
|
}
|
||||||
|
}",
|
||||||
|
"meta": Object {
|
||||||
|
"calculation": "stats",
|
||||||
|
"field": "myField",
|
||||||
|
"filters": Array [
|
||||||
|
Object {
|
||||||
|
"condition": "MT",
|
||||||
|
"key": "age",
|
||||||
|
"value": 17,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"groupBy": undefined,
|
||||||
|
"schema": Object {
|
||||||
|
"avg": Object {
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
"count": Object {
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
"field": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"max": Object {
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
"min": Object {
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
"sum": Object {
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
"sumsqr": Object {
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
|
||||||
|
},
|
||||||
|
"reduce": "_stats",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`viewBuilder Calculate creates a view with the calculation statistics schema 1`] = `
|
exports[`viewBuilder Calculate creates a view with the calculation statistics schema 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"map": "function (doc) {
|
"map": "function (doc) {
|
||||||
if (doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" ) {
|
if ((doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" && !(
|
||||||
|
doc[\\"myField\\"] === undefined ||
|
||||||
|
doc[\\"myField\\"] === null ||
|
||||||
|
doc[\\"myField\\"] === \\"\\" ||
|
||||||
|
(Array.isArray(doc[\\"myField\\"]) && doc[\\"myField\\"].length === 0)
|
||||||
|
)) ) {
|
||||||
emit(doc[\\"_id\\"], doc[\\"myField\\"]);
|
emit(doc[\\"_id\\"], doc[\\"myField\\"]);
|
||||||
}
|
}
|
||||||
}",
|
}",
|
||||||
|
|
|
@ -44,4 +44,22 @@ describe("viewBuilder", () => {
|
||||||
})).toMatchSnapshot()
|
})).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("Calculate and filter", () => {
|
||||||
|
it("creates a view with the calculation statistics and filter schema", () => {
|
||||||
|
expect(viewTemplate({
|
||||||
|
"name": "Calculate View",
|
||||||
|
"field": "myField",
|
||||||
|
"calculation": "stats",
|
||||||
|
"tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"value": 17,
|
||||||
|
"condition": "MT",
|
||||||
|
"key": "age",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
});
|
});
|
|
@ -7,6 +7,7 @@ const {
|
||||||
} = require("../../../db/utils")
|
} = require("../../../db/utils")
|
||||||
const env = require("../../../environment")
|
const env = require("../../../environment")
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
const viewBuilder = require("./viewBuilder")
|
||||||
|
|
||||||
exports.getView = async viewName => {
|
exports.getView = async viewName => {
|
||||||
const db = getAppDB()
|
const db = getAppDB()
|
||||||
|
@ -114,7 +115,8 @@ exports.deleteView = async viewName => {
|
||||||
exports.migrateToInMemoryView = async (db, viewName) => {
|
exports.migrateToInMemoryView = async (db, viewName) => {
|
||||||
// delete the view initially
|
// delete the view initially
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
const view = designDoc.views[viewName]
|
// run the view back through the view builder to update it
|
||||||
|
const view = viewBuilder(designDoc.views[viewName].meta)
|
||||||
delete designDoc.views[viewName]
|
delete designDoc.views[viewName]
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
await exports.saveView(db, null, viewName, view)
|
await exports.saveView(db, null, viewName, view)
|
||||||
|
@ -123,7 +125,7 @@ exports.migrateToInMemoryView = async (db, viewName) => {
|
||||||
exports.migrateToDesignView = async (db, viewName) => {
|
exports.migrateToDesignView = async (db, viewName) => {
|
||||||
let view = await db.get(generateMemoryViewID(viewName))
|
let view = await db.get(generateMemoryViewID(viewName))
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
designDoc.views[viewName] = view.view
|
designDoc.views[viewName] = viewBuilder(view.view.meta)
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
await db.remove(view._id, view._rev)
|
await db.remove(view._id, view._rev)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,12 @@ const TOKEN_MAP = {
|
||||||
OR: "||",
|
OR: "||",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CONDITIONS = {
|
||||||
|
EMPTY: "EMPTY",
|
||||||
|
NOT_EMPTY: "NOT_EMPTY",
|
||||||
|
CONTAINS: "CONTAINS",
|
||||||
|
}
|
||||||
|
|
||||||
const isEmptyExpression = key => {
|
const isEmptyExpression = key => {
|
||||||
return `(
|
return `(
|
||||||
doc["${key}"] === undefined ||
|
doc["${key}"] === undefined ||
|
||||||
|
@ -77,13 +83,13 @@ function parseFilterExpression(filters) {
|
||||||
expression.push(TOKEN_MAP[filter.conjunction])
|
expression.push(TOKEN_MAP[filter.conjunction])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.condition === "CONTAINS") {
|
if (filter.condition === CONDITIONS.CONTAINS) {
|
||||||
expression.push(
|
expression.push(
|
||||||
`doc["${filter.key}"].${TOKEN_MAP[filter.condition]}("${filter.value}")`
|
`doc["${filter.key}"].${TOKEN_MAP[filter.condition]}("${filter.value}")`
|
||||||
)
|
)
|
||||||
} else if (filter.condition === "EMPTY") {
|
} else if (filter.condition === CONDITIONS.EMPTY) {
|
||||||
expression.push(isEmptyExpression(filter.key))
|
expression.push(isEmptyExpression(filter.key))
|
||||||
} else if (filter.condition === "NOT_EMPTY") {
|
} else if (filter.condition === CONDITIONS.NOT_EMPTY) {
|
||||||
expression.push(`!${isEmptyExpression(filter.key)}`)
|
expression.push(`!${isEmptyExpression(filter.key)}`)
|
||||||
} else {
|
} else {
|
||||||
const value =
|
const value =
|
||||||
|
@ -125,22 +131,37 @@ function viewTemplate({ field, tableId, groupBy, filters = [], calculation }) {
|
||||||
if (filters && filters.length > 0 && filters[0].conjunction) {
|
if (filters && filters.length > 0 && filters[0].conjunction) {
|
||||||
delete filters[0].conjunction
|
delete filters[0].conjunction
|
||||||
}
|
}
|
||||||
const parsedFilters = parseFilterExpression(filters)
|
|
||||||
const filterExpression = parsedFilters ? `&& (${parsedFilters})` : ""
|
|
||||||
|
|
||||||
const emitExpression = parseEmitExpression(field, groupBy)
|
let schema = null,
|
||||||
|
statFilter = null
|
||||||
const reduction = field && calculation ? { reduce: `_${calculation}` } : {}
|
|
||||||
|
|
||||||
let schema = null
|
|
||||||
|
|
||||||
if (calculation) {
|
if (calculation) {
|
||||||
schema = {
|
schema = {
|
||||||
...(groupBy ? GROUP_PROPERTY : FIELD_PROPERTY),
|
...(groupBy ? GROUP_PROPERTY : FIELD_PROPERTY),
|
||||||
...SCHEMA_MAP[calculation],
|
...SCHEMA_MAP[calculation],
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
!filters.find(
|
||||||
|
filter =>
|
||||||
|
filter.key === field && filter.condition === CONDITIONS.NOT_EMPTY
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
statFilter = parseFilterExpression([
|
||||||
|
{ key: field, condition: CONDITIONS.NOT_EMPTY },
|
||||||
|
])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parsedFilters = parseFilterExpression(filters)
|
||||||
|
const filterExpression = parsedFilters ? `&& (${parsedFilters})` : ""
|
||||||
|
|
||||||
|
const emitExpression = parseEmitExpression(field, groupBy)
|
||||||
|
const tableExpression = `doc.tableId === "${tableId}"`
|
||||||
|
const coreExpression = statFilter
|
||||||
|
? `(${tableExpression} && ${statFilter})`
|
||||||
|
: tableExpression
|
||||||
|
const reduction = field && calculation ? { reduce: `_${calculation}` } : {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
meta: {
|
meta: {
|
||||||
field,
|
field,
|
||||||
|
@ -151,7 +172,7 @@ function viewTemplate({ field, tableId, groupBy, filters = [], calculation }) {
|
||||||
calculation,
|
calculation,
|
||||||
},
|
},
|
||||||
map: `function (doc) {
|
map: `function (doc) {
|
||||||
if (doc.tableId === "${tableId}" ${filterExpression}) {
|
if (${coreExpression} ${filterExpression}) {
|
||||||
${emitExpression}
|
${emitExpression}
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
|
|
|
@ -4,10 +4,18 @@ import {
|
||||||
QueryTypes,
|
QueryTypes,
|
||||||
} from "../definitions/datasource"
|
} from "../definitions/datasource"
|
||||||
import { IntegrationBase } from "./base/IntegrationBase"
|
import { IntegrationBase } from "./base/IntegrationBase"
|
||||||
|
import {
|
||||||
|
MongoClient,
|
||||||
|
ObjectID,
|
||||||
|
FilterQuery,
|
||||||
|
UpdateQuery,
|
||||||
|
FindOneAndUpdateOption,
|
||||||
|
UpdateOneOptions,
|
||||||
|
UpdateManyOptions,
|
||||||
|
CommonOptions,
|
||||||
|
} from "mongodb"
|
||||||
|
|
||||||
module MongoDBModule {
|
module MongoDBModule {
|
||||||
const { MongoClient } = require("mongodb")
|
|
||||||
|
|
||||||
interface MongoDBConfig {
|
interface MongoDBConfig {
|
||||||
connectionString: string
|
connectionString: string
|
||||||
db: string
|
db: string
|
||||||
|
@ -76,20 +84,76 @@ module MongoDBModule {
|
||||||
return this.client.connect()
|
return this.client.connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createObjectIds(json: any): object {
|
||||||
|
const self = this
|
||||||
|
function interpolateObjectIds(json: any) {
|
||||||
|
for (let field of Object.keys(json)) {
|
||||||
|
if (json[field] instanceof Object) {
|
||||||
|
json[field] = self.createObjectIds(json[field])
|
||||||
|
}
|
||||||
|
if (field === "_id" && typeof json[field] === "string") {
|
||||||
|
const id = json["_id"].match(
|
||||||
|
/(?<=objectid\(['"]).*(?=['"]\))/gi
|
||||||
|
)?.[0]
|
||||||
|
if (id) {
|
||||||
|
json["_id"] = ObjectID.createFromHexString(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(json)) {
|
||||||
|
for (let i = 0; i < json.length; i++) {
|
||||||
|
json[i] = interpolateObjectIds(json[i])
|
||||||
|
}
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
return interpolateObjectIds(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
parseQueryParams(params: string, mode: string) {
|
||||||
|
let queryParams = params.split(/(?<=(},)).*{/g)
|
||||||
|
let group1 = queryParams[0]
|
||||||
|
let group2 = queryParams[2]
|
||||||
|
let group3 = queryParams[4]
|
||||||
|
if (group1) {
|
||||||
|
group1 = JSON.parse(group1.replace(/,+$/, ""))
|
||||||
|
}
|
||||||
|
if (group2) {
|
||||||
|
group2 = JSON.parse("{" + group2.replace(/,+$/, ""))
|
||||||
|
}
|
||||||
|
if (group3) {
|
||||||
|
group3 = JSON.parse("{" + group3.replace(/,+$/, ""))
|
||||||
|
}
|
||||||
|
if (mode === "update") {
|
||||||
|
return {
|
||||||
|
filter: group1,
|
||||||
|
update: group2,
|
||||||
|
options: group3 ?? {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
filter: group1,
|
||||||
|
options: group2 ?? {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async create(query: { json: object; extra: { [key: string]: string } }) {
|
async create(query: { json: object; extra: { [key: string]: string } }) {
|
||||||
try {
|
try {
|
||||||
await this.connect()
|
await this.connect()
|
||||||
const db = this.client.db(this.config.db)
|
const db = this.client.db(this.config.db)
|
||||||
const collection = db.collection(query.extra.collection)
|
const collection = db.collection(query.extra.collection)
|
||||||
|
let json = this.createObjectIds(query.json)
|
||||||
|
|
||||||
// For mongodb we add an extra actionType to specify
|
// For mongodb we add an extra actionType to specify
|
||||||
// which method we want to call on the collection
|
// which method we want to call on the collection
|
||||||
switch (query.extra.actionTypes) {
|
switch (query.extra.actionTypes) {
|
||||||
case "insertOne": {
|
case "insertOne": {
|
||||||
return await collection.insertOne(query.json)
|
return await collection.insertOne(json)
|
||||||
}
|
}
|
||||||
case "insertMany": {
|
case "insertMany": {
|
||||||
return await collection.insertOne(query.json).toArray()
|
return await collection.insertMany(json)
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -110,22 +174,32 @@ module MongoDBModule {
|
||||||
await this.connect()
|
await this.connect()
|
||||||
const db = this.client.db(this.config.db)
|
const db = this.client.db(this.config.db)
|
||||||
const collection = db.collection(query.extra.collection)
|
const collection = db.collection(query.extra.collection)
|
||||||
|
let json = this.createObjectIds(query.json)
|
||||||
|
|
||||||
switch (query.extra.actionTypes) {
|
switch (query.extra.actionTypes) {
|
||||||
case "find": {
|
case "find": {
|
||||||
return await collection.find(query.json).toArray()
|
return await collection.find(json).toArray()
|
||||||
}
|
}
|
||||||
case "findOne": {
|
case "findOne": {
|
||||||
return await collection.findOne(query.json)
|
return await collection.findOne(json)
|
||||||
}
|
}
|
||||||
case "findOneAndUpdate": {
|
case "findOneAndUpdate": {
|
||||||
return await collection.findOneAndUpdate(query.json)
|
let findAndUpdateJson = json as {
|
||||||
|
filter: FilterQuery<any>
|
||||||
|
update: UpdateQuery<any>
|
||||||
|
options: FindOneAndUpdateOption<any>
|
||||||
|
}
|
||||||
|
return await collection.findOneAndUpdate(
|
||||||
|
findAndUpdateJson.filter,
|
||||||
|
findAndUpdateJson.update,
|
||||||
|
findAndUpdateJson.options
|
||||||
|
)
|
||||||
}
|
}
|
||||||
case "count": {
|
case "count": {
|
||||||
return await collection.countDocuments(query.json)
|
return await collection.countDocuments(json)
|
||||||
}
|
}
|
||||||
case "distinct": {
|
case "distinct": {
|
||||||
return await collection.distinct(query.json)
|
return await collection.distinct(json)
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -146,13 +220,30 @@ module MongoDBModule {
|
||||||
await this.connect()
|
await this.connect()
|
||||||
const db = this.client.db(this.config.db)
|
const db = this.client.db(this.config.db)
|
||||||
const collection = db.collection(query.extra.collection)
|
const collection = db.collection(query.extra.collection)
|
||||||
|
let queryJson = query.json
|
||||||
|
if (typeof queryJson === "string") {
|
||||||
|
queryJson = this.parseQueryParams(queryJson, "update")
|
||||||
|
}
|
||||||
|
let json = this.createObjectIds(queryJson) as {
|
||||||
|
filter: FilterQuery<any>
|
||||||
|
update: UpdateQuery<any>
|
||||||
|
options: object
|
||||||
|
}
|
||||||
|
|
||||||
switch (query.extra.actionTypes) {
|
switch (query.extra.actionTypes) {
|
||||||
case "updateOne": {
|
case "updateOne": {
|
||||||
return await collection.updateOne(query.json)
|
return await collection.updateOne(
|
||||||
|
json.filter,
|
||||||
|
json.update,
|
||||||
|
json.options as UpdateOneOptions
|
||||||
|
)
|
||||||
}
|
}
|
||||||
case "updateMany": {
|
case "updateMany": {
|
||||||
return await collection.updateMany(query.json).toArray()
|
return await collection.updateMany(
|
||||||
|
json.filter,
|
||||||
|
json.update,
|
||||||
|
json.options as UpdateManyOptions
|
||||||
|
)
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -173,13 +264,21 @@ module MongoDBModule {
|
||||||
await this.connect()
|
await this.connect()
|
||||||
const db = this.client.db(this.config.db)
|
const db = this.client.db(this.config.db)
|
||||||
const collection = db.collection(query.extra.collection)
|
const collection = db.collection(query.extra.collection)
|
||||||
|
let queryJson = query.json
|
||||||
|
if (typeof queryJson === "string") {
|
||||||
|
queryJson = this.parseQueryParams(queryJson, "delete")
|
||||||
|
}
|
||||||
|
let json = this.createObjectIds(queryJson) as {
|
||||||
|
filter: FilterQuery<any>
|
||||||
|
options: CommonOptions
|
||||||
|
}
|
||||||
|
|
||||||
switch (query.extra.actionTypes) {
|
switch (query.extra.actionTypes) {
|
||||||
case "deleteOne": {
|
case "deleteOne": {
|
||||||
return await collection.deleteOne(query.json)
|
return await collection.deleteOne(json.filter, json.options)
|
||||||
}
|
}
|
||||||
case "deleteMany": {
|
case "deleteMany": {
|
||||||
return await collection.deleteMany(query.json).toArray()
|
return await collection.deleteMany(json.filter, json.options)
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
|
@ -4,19 +4,19 @@ jest.mock("mongodb")
|
||||||
|
|
||||||
class TestConfiguration {
|
class TestConfiguration {
|
||||||
constructor(config = {}) {
|
constructor(config = {}) {
|
||||||
this.integration = new MongoDBIntegration.integration(config)
|
this.integration = new MongoDBIntegration.integration(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableConsole() {
|
function disableConsole() {
|
||||||
jest.spyOn(console, 'error');
|
jest.spyOn(console, "error")
|
||||||
console.error.mockImplementation(() => {});
|
console.error.mockImplementation(() => {})
|
||||||
|
|
||||||
return console.error.mockRestore;
|
return console.error.mockRestore
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("MongoDB Integration", () => {
|
describe("MongoDB Integration", () => {
|
||||||
let config
|
let config
|
||||||
let indexName = "Users"
|
let indexName = "Users"
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -25,12 +25,12 @@ describe("MongoDB Integration", () => {
|
||||||
|
|
||||||
it("calls the create method with the correct params", async () => {
|
it("calls the create method with the correct params", async () => {
|
||||||
const body = {
|
const body = {
|
||||||
name: "Hello"
|
name: "Hello",
|
||||||
}
|
}
|
||||||
await config.integration.create({
|
await config.integration.create({
|
||||||
index: indexName,
|
index: indexName,
|
||||||
json: body,
|
json: body,
|
||||||
extra: { collection: 'testCollection', actionTypes: 'insertOne'}
|
extra: { collection: "testCollection", actionTypes: "insertOne" },
|
||||||
})
|
})
|
||||||
expect(config.integration.client.insertOne).toHaveBeenCalledWith(body)
|
expect(config.integration.client.insertOne).toHaveBeenCalledWith(body)
|
||||||
})
|
})
|
||||||
|
@ -38,9 +38,9 @@ describe("MongoDB Integration", () => {
|
||||||
it("calls the read method with the correct params", async () => {
|
it("calls the read method with the correct params", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
json: {
|
json: {
|
||||||
address: "test"
|
address: "test",
|
||||||
},
|
},
|
||||||
extra: { collection: 'testCollection', actionTypes: 'find'}
|
extra: { collection: "testCollection", actionTypes: "find" },
|
||||||
}
|
}
|
||||||
const response = await config.integration.read(query)
|
const response = await config.integration.read(query)
|
||||||
expect(config.integration.client.find).toHaveBeenCalledWith(query.json)
|
expect(config.integration.client.find).toHaveBeenCalledWith(query.json)
|
||||||
|
@ -50,30 +50,47 @@ describe("MongoDB Integration", () => {
|
||||||
it("calls the delete method with the correct params", async () => {
|
it("calls the delete method with the correct params", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
json: {
|
json: {
|
||||||
id: "test"
|
filter: {
|
||||||
|
id: "test",
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
opt: "option"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
extra: { collection: 'testCollection', actionTypes: 'deleteOne'}
|
extra: { collection: "testCollection", actionTypes: "deleteOne" },
|
||||||
}
|
}
|
||||||
await config.integration.delete(query)
|
await config.integration.delete(query)
|
||||||
expect(config.integration.client.deleteOne).toHaveBeenCalledWith(query.json)
|
expect(config.integration.client.deleteOne).toHaveBeenCalledWith(query.json.filter, query.json.options)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("calls the update method with the correct params", async () => {
|
it("calls the update method with the correct params", async () => {
|
||||||
const query = {
|
const query = {
|
||||||
json: {
|
json: {
|
||||||
id: "test"
|
filter: {
|
||||||
|
id: "test",
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: "TestName",
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
upsert: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
extra: { collection: 'testCollection', actionTypes: 'updateOne'}
|
extra: { collection: "testCollection", actionTypes: "updateOne" },
|
||||||
}
|
}
|
||||||
await config.integration.update(query)
|
await config.integration.update(query)
|
||||||
expect(config.integration.client.updateOne).toHaveBeenCalledWith(query.json)
|
expect(config.integration.client.updateOne).toHaveBeenCalledWith(
|
||||||
|
query.json.filter,
|
||||||
|
query.json.update,
|
||||||
|
query.json.options
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("throws an error when an invalid query.extra.actionType is passed for each method", async () => {
|
it("throws an error when an invalid query.extra.actionType is passed for each method", async () => {
|
||||||
const restore = disableConsole()
|
const restore = disableConsole()
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
extra: { collection: 'testCollection', actionTypes: 'deleteOne'}
|
extra: { collection: "testCollection", actionTypes: "deleteOne" },
|
||||||
}
|
}
|
||||||
|
|
||||||
let error = null
|
let error = null
|
||||||
|
@ -85,4 +102,4 @@ describe("MongoDB Integration", () => {
|
||||||
expect(error).toBeDefined()
|
expect(error).toBeDefined()
|
||||||
restore()
|
restore()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1014,10 +1014,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/backend-core@1.0.159-alpha.2":
|
"@budibase/backend-core@1.0.164-alpha.3":
|
||||||
version "1.0.159-alpha.2"
|
version "1.0.164-alpha.3"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.159-alpha.2.tgz#6f1760c26f6c92930ce6ad93c6617f60713a6b48"
|
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.164-alpha.3.tgz#305c79ded2da369d087ac192ffc7c2a4e9445f44"
|
||||||
integrity sha512-yHOICD9m6SOIME7NnzP8lQBWDAwpkMwwkuv6igs7a6mrMk30nlenJB/nMW09bqbvryOEBN5XH1Nmgvh863fQ3A==
|
integrity sha512-EYh/N1osHv2VQs0KEoY3sXiR9N18dSVzwGwJauc6ucCP3cUUMv/rzPI6t7Amk2JRIjBaJmGTV+gMb0BBZiQBHw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@techpass/passport-openidconnect" "^0.3.0"
|
"@techpass/passport-openidconnect" "^0.3.0"
|
||||||
aws-sdk "^2.901.0"
|
aws-sdk "^2.901.0"
|
||||||
|
@ -1091,12 +1091,12 @@
|
||||||
svelte-flatpickr "^3.2.3"
|
svelte-flatpickr "^3.2.3"
|
||||||
svelte-portal "^1.0.0"
|
svelte-portal "^1.0.0"
|
||||||
|
|
||||||
"@budibase/pro@1.0.159-alpha.2":
|
"@budibase/pro@1.0.164-alpha.3":
|
||||||
version "1.0.159-alpha.2"
|
version "1.0.164-alpha.3"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.159-alpha.2.tgz#d8879f0ad16a709d8326594dd11e0f657f00d714"
|
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.164-alpha.3.tgz#ad599aea5ebfb5fe7a8587e0b96ca1329a2f7671"
|
||||||
integrity sha512-TKvRqxGOP+PWUeDiVcnEmDXFSuMr0aFlBbSXX2cML4KvYrEKFM3hjLygi5g32G0ICmsVMiluK+wjUGBAP6SHWA==
|
integrity sha512-VYu1SxVt2gTRj7U/600DYTzORDcXkPWfTxRUgNQSJRZxaro2y69RFsQtQc+5Aa/dykLCQ9P7b8Nyyf9Wgs7gIg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/backend-core" "1.0.159-alpha.2"
|
"@budibase/backend-core" "1.0.164-alpha.3"
|
||||||
node-fetch "^2.6.1"
|
node-fetch "^2.6.1"
|
||||||
|
|
||||||
"@budibase/standard-components@^0.9.139":
|
"@budibase/standard-components@^0.9.139":
|
||||||
|
@ -2430,6 +2430,13 @@
|
||||||
"@types/connect" "*"
|
"@types/connect" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/bson@*":
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/bson/-/bson-4.2.0.tgz#a2f71e933ff54b2c3bf267b67fa221e295a33337"
|
||||||
|
integrity sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==
|
||||||
|
dependencies:
|
||||||
|
bson "*"
|
||||||
|
|
||||||
"@types/bull@^3.15.1":
|
"@types/bull@^3.15.1":
|
||||||
version "3.15.8"
|
version "3.15.8"
|
||||||
resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.8.tgz#ae2139f94490d740b37c8da5d828ce75dd82ce7c"
|
resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.8.tgz#ae2139f94490d740b37c8da5d828ce75dd82ce7c"
|
||||||
|
@ -2651,6 +2658,14 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
||||||
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
|
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
|
||||||
|
|
||||||
|
"@types/mongodb@3.6.3":
|
||||||
|
version "3.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.6.3.tgz#5655af409d9e32d5d5ae9a653abf3e5f9c83eb7a"
|
||||||
|
integrity sha512-6YNqGP1hk5bjUFaim+QoFFuI61WjHiHE1BNeB41TA00Xd2K7zG4lcWyLLq/XtIp36uMavvS5hoAUJ+1u/GcX2Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/bson" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/node@*", "@types/node@>=13.13.4":
|
"@types/node@*", "@types/node@>=13.13.4":
|
||||||
version "17.0.18"
|
version "17.0.18"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074"
|
||||||
|
@ -3869,6 +3884,13 @@ bser@2.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
node-int64 "^0.4.0"
|
node-int64 "^0.4.0"
|
||||||
|
|
||||||
|
bson@*:
|
||||||
|
version "4.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/bson/-/bson-4.6.3.tgz#d1a9a0b84b9e84b62390811fc5580f6a8b1d858c"
|
||||||
|
integrity sha512-rAqP5hcUVJhXP2MCSNVsf0oM2OGU1So6A9pVRDYayvJ5+hygXHQApf87wd5NlhPM1J9RJnbqxIG/f8QTzRoQ4A==
|
||||||
|
dependencies:
|
||||||
|
buffer "^5.6.0"
|
||||||
|
|
||||||
bson@^1.1.4:
|
bson@^1.1.4:
|
||||||
version "1.1.6"
|
version "1.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.6.tgz#fb819be9a60cd677e0853aee4ca712a785d6618a"
|
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.6.tgz#fb819be9a60cd677e0853aee4ca712a785d6618a"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "1.0.159-alpha.2",
|
"version": "1.0.164-alpha.3",
|
||||||
"description": "Handlebars wrapper for Budibase templating.",
|
"description": "Handlebars wrapper for Budibase templating.",
|
||||||
"main": "src/index.cjs",
|
"main": "src/index.cjs",
|
||||||
"module": "dist/bundle.mjs",
|
"module": "dist/bundle.mjs",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/worker",
|
"name": "@budibase/worker",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.159-alpha.2",
|
"version": "1.0.164-alpha.3",
|
||||||
"description": "Budibase background service",
|
"description": "Budibase background service",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -31,9 +31,9 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/backend-core": "^1.0.159-alpha.2",
|
"@budibase/backend-core": "^1.0.164-alpha.3",
|
||||||
"@budibase/pro": "1.0.159-alpha.2",
|
"@budibase/pro": "1.0.164-alpha.3",
|
||||||
"@budibase/string-templates": "^1.0.159-alpha.2",
|
"@budibase/string-templates": "^1.0.164-alpha.3",
|
||||||
"@koa/router": "^8.0.0",
|
"@koa/router": "^8.0.0",
|
||||||
"@sentry/node": "6.17.7",
|
"@sentry/node": "6.17.7",
|
||||||
"@techpass/passport-openidconnect": "^0.3.0",
|
"@techpass/passport-openidconnect": "^0.3.0",
|
||||||
|
|
|
@ -293,10 +293,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/backend-core@1.0.159-alpha.2":
|
"@budibase/backend-core@1.0.164-alpha.3":
|
||||||
version "1.0.159-alpha.2"
|
version "1.0.164-alpha.3"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.159-alpha.2.tgz#6f1760c26f6c92930ce6ad93c6617f60713a6b48"
|
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.164-alpha.3.tgz#305c79ded2da369d087ac192ffc7c2a4e9445f44"
|
||||||
integrity sha512-yHOICD9m6SOIME7NnzP8lQBWDAwpkMwwkuv6igs7a6mrMk30nlenJB/nMW09bqbvryOEBN5XH1Nmgvh863fQ3A==
|
integrity sha512-EYh/N1osHv2VQs0KEoY3sXiR9N18dSVzwGwJauc6ucCP3cUUMv/rzPI6t7Amk2JRIjBaJmGTV+gMb0BBZiQBHw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@techpass/passport-openidconnect" "^0.3.0"
|
"@techpass/passport-openidconnect" "^0.3.0"
|
||||||
aws-sdk "^2.901.0"
|
aws-sdk "^2.901.0"
|
||||||
|
@ -321,12 +321,12 @@
|
||||||
uuid "^8.3.2"
|
uuid "^8.3.2"
|
||||||
zlib "^1.0.5"
|
zlib "^1.0.5"
|
||||||
|
|
||||||
"@budibase/pro@1.0.159-alpha.2":
|
"@budibase/pro@1.0.164-alpha.3":
|
||||||
version "1.0.159-alpha.2"
|
version "1.0.164-alpha.3"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.159-alpha.2.tgz#d8879f0ad16a709d8326594dd11e0f657f00d714"
|
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.164-alpha.3.tgz#ad599aea5ebfb5fe7a8587e0b96ca1329a2f7671"
|
||||||
integrity sha512-TKvRqxGOP+PWUeDiVcnEmDXFSuMr0aFlBbSXX2cML4KvYrEKFM3hjLygi5g32G0ICmsVMiluK+wjUGBAP6SHWA==
|
integrity sha512-VYu1SxVt2gTRj7U/600DYTzORDcXkPWfTxRUgNQSJRZxaro2y69RFsQtQc+5Aa/dykLCQ9P7b8Nyyf9Wgs7gIg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/backend-core" "1.0.159-alpha.2"
|
"@budibase/backend-core" "1.0.164-alpha.3"
|
||||||
node-fetch "^2.6.1"
|
node-fetch "^2.6.1"
|
||||||
|
|
||||||
"@cspotcode/source-map-consumer@0.8.0":
|
"@cspotcode/source-map-consumer@0.8.0":
|
||||||
|
|
52
yarn.lock
52
yarn.lock
|
@ -972,6 +972,13 @@
|
||||||
estree-walker "^1.0.1"
|
estree-walker "^1.0.1"
|
||||||
picomatch "^2.2.2"
|
picomatch "^2.2.2"
|
||||||
|
|
||||||
|
"@types/bson@*":
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/bson/-/bson-4.2.0.tgz#a2f71e933ff54b2c3bf267b67fa221e295a33337"
|
||||||
|
integrity sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==
|
||||||
|
dependencies:
|
||||||
|
bson "*"
|
||||||
|
|
||||||
"@types/estree@0.0.39":
|
"@types/estree@0.0.39":
|
||||||
version "0.0.39"
|
version "0.0.39"
|
||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||||
|
@ -982,6 +989,19 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c"
|
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c"
|
||||||
integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==
|
integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==
|
||||||
|
|
||||||
|
"@types/mongodb@3.6.3":
|
||||||
|
version "3.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.6.3.tgz#5655af409d9e32d5d5ae9a653abf3e5f9c83eb7a"
|
||||||
|
integrity sha512-6YNqGP1hk5bjUFaim+QoFFuI61WjHiHE1BNeB41TA00Xd2K7zG4lcWyLLq/XtIp36uMavvS5hoAUJ+1u/GcX2Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/bson" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/node@*":
|
||||||
|
version "17.0.33"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.33.tgz#3c1879b276dc63e73030bb91165e62a4509cd506"
|
||||||
|
integrity sha512-miWq2m2FiQZmaHfdZNcbpp9PuXg34W5JZ5CrJ/BaS70VuhoJENBEQybeiYSaPBRNq6KQGnjfEnc/F3PN++D+XQ==
|
||||||
|
|
||||||
"@types/node@>= 8":
|
"@types/node@>= 8":
|
||||||
version "17.0.18"
|
version "17.0.18"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074"
|
||||||
|
@ -1300,6 +1320,11 @@ balanced-match@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
|
base64-js@^1.3.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
|
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||||
|
|
||||||
base@^0.11.1:
|
base@^0.11.1:
|
||||||
version "0.11.2"
|
version "0.11.2"
|
||||||
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
||||||
|
@ -1361,6 +1386,13 @@ braces@^3.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
fill-range "^7.0.1"
|
fill-range "^7.0.1"
|
||||||
|
|
||||||
|
bson@*:
|
||||||
|
version "4.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/bson/-/bson-4.6.3.tgz#d1a9a0b84b9e84b62390811fc5580f6a8b1d858c"
|
||||||
|
integrity sha512-rAqP5hcUVJhXP2MCSNVsf0oM2OGU1So6A9pVRDYayvJ5+hygXHQApf87wd5NlhPM1J9RJnbqxIG/f8QTzRoQ4A==
|
||||||
|
dependencies:
|
||||||
|
buffer "^5.6.0"
|
||||||
|
|
||||||
btoa-lite@^1.0.0:
|
btoa-lite@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337"
|
resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337"
|
||||||
|
@ -1371,6 +1403,14 @@ buffer-from@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||||
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
||||||
|
|
||||||
|
buffer@^5.6.0:
|
||||||
|
version "5.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||||
|
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.3.1"
|
||||||
|
ieee754 "^1.1.13"
|
||||||
|
|
||||||
builtins@^1.0.3:
|
builtins@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
|
resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
|
||||||
|
@ -2969,6 +3009,11 @@ iconv-lite@^0.6.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||||
|
|
||||||
|
ieee754@^1.1.13:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
|
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||||
|
|
||||||
iferr@^0.1.5:
|
iferr@^0.1.5:
|
||||||
version "0.1.5"
|
version "0.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
|
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
|
||||||
|
@ -4663,12 +4708,7 @@ performance-now@^2.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||||
|
|
||||||
picomatch@^2.2.2:
|
picomatch@^2.2.2, picomatch@^2.3.1:
|
||||||
version "2.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
|
||||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
|
||||||
|
|
||||||
picomatch@^2.3.1:
|
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||||
|
|
Loading…
Reference in New Issue