Merge branch 'cypress-testing' of https://github.com/Budibase/budibase into cypress-testing
This commit is contained in:
commit
daebee184d
34 changed files with 739 additions and 1195 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -93,5 +93,5 @@ hosting/.generated-envoy.dev.yaml
|
|||
# Sublime text
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
packages/builder/cypress.env.json
|
||||
|
||||
bin/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.0.44-alpha.1",
|
||||
"version": "1.0.44-alpha.7",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/backend-core",
|
||||
"version": "1.0.46-alpha.3",
|
||||
"version": "1.0.44-alpha.7",
|
||||
"description": "Budibase backend core libraries used in server and worker",
|
||||
"main": "src/index.js",
|
||||
"author": "Budibase",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/bbui",
|
||||
"description": "A UI solution used in the different Budibase projects.",
|
||||
"version": "1.0.44-alpha.1",
|
||||
"version": "1.0.44-alpha.7",
|
||||
"license": "MPL-2.0",
|
||||
"svelte": "src/index.js",
|
||||
"module": "dist/bbui.es.js",
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
export let error = null
|
||||
export let fileTags = []
|
||||
export let maximum = null
|
||||
export let extensions = "*"
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const imageExtensions = [
|
||||
|
@ -207,6 +208,7 @@
|
|||
{disabled}
|
||||
type="file"
|
||||
multiple
|
||||
accept={extensions}
|
||||
on:change={handleFile}
|
||||
/>
|
||||
<svg
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
{
|
||||
"baseUrl": "http://localhost:10001",
|
||||
"baseUrl": "http://localhost:10001/builder/",
|
||||
"video": true,
|
||||
"projectId": "bmbemn",
|
||||
"env": {
|
||||
"PORT": "10001",
|
||||
"JWT_SECRET": "test",
|
||||
"HOST_IP": "52.49.184.138",
|
||||
"TEST_ENV" : false
|
||||
"JWT_SECRET": "test"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,33 +13,35 @@ filterTests(['all'], () => {
|
|||
cy.addRowMultiValue(["1", "2", "3", "4", "5"])
|
||||
})
|
||||
|
||||
it ("should add form with multi select picker, containing 5 options", () => {
|
||||
cy.navigateToFrontend()
|
||||
cy.wait(500)
|
||||
// Add data provider
|
||||
cy.addComponent("Data", "Data Provider")
|
||||
cy.get('[data-cy="dataSource-prop-control"]').click()
|
||||
cy.get(".dropdown").contains("Multi Data").click()
|
||||
cy.wait(500)
|
||||
// Add Form with schema to match table
|
||||
cy.addComponent("Form", "Form")
|
||||
cy.get('[data-cy="dataSource-prop-control"').click()
|
||||
cy.get(".dropdown").contains("Multi Data").click()
|
||||
cy.wait(500)
|
||||
// Add multi-select picker to form
|
||||
cy.addComponent("Form", "Multi-select Picker").then((componentId) => {
|
||||
cy.get('[data-cy="field-prop-control"]').type("Test Data").type('{enter}')
|
||||
cy.wait(1000)
|
||||
cy.getComponent(componentId).contains("Choose some options").click()
|
||||
// Check picker has 5 items
|
||||
cy.getComponent(componentId).find('li').should('have.length', 5)
|
||||
// Select all items
|
||||
for (let i = 1; i < 6; i++) {
|
||||
cy.getComponent(componentId).find('li').contains(i).click()
|
||||
}
|
||||
// Check items have been selected
|
||||
cy.getComponent(componentId).find('.spectrum-Picker-label').contains("(5)")
|
||||
})
|
||||
it("should add form with multi select picker, containing 5 options", () => {
|
||||
cy.navigateToFrontend()
|
||||
cy.wait(500)
|
||||
// Add data provider
|
||||
cy.get(`[data-cy="category-Data"]`).click()
|
||||
cy.get(`[data-cy="component-Data Provider"]`).click()
|
||||
cy.get('[data-cy="dataSource-prop-control"]').click()
|
||||
cy.get(".dropdown").contains("Multi Data").click()
|
||||
cy.wait(500)
|
||||
// Add Form with schema to match table
|
||||
cy.addComponent("Form", "Form")
|
||||
cy.get('[data-cy="dataSource-prop-control"').click()
|
||||
cy.get(".dropdown").contains("Multi Data").click()
|
||||
cy.wait(500)
|
||||
// Add multi-select picker to form
|
||||
cy.addComponent("Form", "Multi-select Picker").then(componentId => {
|
||||
cy.get('[data-cy="field-prop-control"]').type("Test Data").type("{enter}")
|
||||
cy.wait(1000)
|
||||
cy.getComponent(componentId).contains("Choose some options").click()
|
||||
// Check picker has 5 items
|
||||
cy.getComponent(componentId).find("li").should("have.length", 5)
|
||||
// Select all items
|
||||
for (let i = 1; i < 6; i++) {
|
||||
cy.getComponent(componentId).find("li").contains(i).click()
|
||||
}
|
||||
// Check items have been selected
|
||||
cy.getComponent(componentId)
|
||||
.find(".spectrum-Picker-label")
|
||||
.contains("(5)")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
10
packages/builder/cypress/integration/createUser.spec.js
Normal file
10
packages/builder/cypress/integration/createUser.spec.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
context("Create a User", () => {
|
||||
before(() => {
|
||||
cy.login()
|
||||
})
|
||||
|
||||
it("should create a user", () => {
|
||||
cy.createUser("bbuser@test.com")
|
||||
cy.contains("bbuser").should("be.visible")
|
||||
})
|
||||
})
|
|
@ -53,6 +53,7 @@ filterTests(['all'], () => {
|
|||
cy.wait(1000)
|
||||
cy.searchForApplication(appName)
|
||||
cy.get(".appTable").find(".title").should("have.length", 1)
|
||||
|
||||
})
|
||||
|
||||
xit("Should create two applications with the same name", () => {
|
||||
|
@ -126,6 +127,7 @@ filterTests(['all'], () => {
|
|||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -10,7 +10,7 @@ Cypress.on("uncaught:exception", () => {
|
|||
})
|
||||
|
||||
Cypress.Commands.add("login", () => {
|
||||
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
cy.wait(2000)
|
||||
cy.url().then(url => {
|
||||
if (url.includes("builder/admin")) {
|
||||
|
@ -33,77 +33,31 @@ Cypress.Commands.add("login", () => {
|
|||
})
|
||||
|
||||
Cypress.Commands.add("createApp", name => {
|
||||
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
cy.wait(500)
|
||||
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
||||
.its("body")
|
||||
.then(body => {
|
||||
if (body.length > 0) {
|
||||
cy.get(".spectrum-Button").contains("Create app").click({ force: true })
|
||||
}
|
||||
})
|
||||
cy.contains(/Start from scratch/).dblclick()
|
||||
cy.wait(2000)
|
||||
cy.get(".spectrum-Modal").within(() => {
|
||||
cy.get("input").eq(0).type(name).should("have.value", name).blur()
|
||||
cy.get(".spectrum-ButtonGroup").contains("Create app").click()
|
||||
cy.wait(5000)
|
||||
cy.wait(7000)
|
||||
})
|
||||
cy.createTable("Cypress Tests", true)
|
||||
})
|
||||
|
||||
Cypress.Commands.add("deleteApp", name => {
|
||||
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||
cy.wait(2000)
|
||||
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
||||
Cypress.Commands.add("deleteApp", appName => {
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
cy.wait(1000)
|
||||
cy.request(`localhost:${Cypress.env("PORT")}/api/applications?status=all`)
|
||||
.its("body")
|
||||
.then(val => {
|
||||
if (val.length > 0) {
|
||||
cy.searchForApplication(name)
|
||||
cy.get(".appTable").within(() => {
|
||||
cy.get(".spectrum-Icon").eq(1).click()
|
||||
cy.get(
|
||||
".appTable > :nth-child(5) > :nth-child(2) > .spectrum-Icon"
|
||||
).click()
|
||||
cy.contains("Delete").click()
|
||||
cy.get(".spectrum-Modal").within(() => {
|
||||
cy.get("input").type(appName)
|
||||
cy.get(".spectrum-Button--warning").click()
|
||||
})
|
||||
cy.get(".spectrum-Menu").then($menu => {
|
||||
if ($menu.text().includes("Unpublish")) {
|
||||
cy.get(".spectrum-Menu").contains("Unpublish").click()
|
||||
cy.get(".spectrum-Dialog-grid").contains("Unpublish app").click()
|
||||
} else {
|
||||
cy.get(".spectrum-Menu").contains("Delete").click()
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
cy.get("input").type(name)
|
||||
})
|
||||
cy.get(".spectrum-Button--warning").click()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add("deleteAllApps", () => {
|
||||
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||
cy.wait(500)
|
||||
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
|
||||
.its("body")
|
||||
.then(val => {
|
||||
for (let i = 0; i < val.length; i++) {
|
||||
cy.get(".spectrum-Heading")
|
||||
.eq(1)
|
||||
.then(app => {
|
||||
const name = app.text()
|
||||
cy.get(".title")
|
||||
.children()
|
||||
.within(() => {
|
||||
cy.get(".spectrum-Icon").eq(0).click()
|
||||
})
|
||||
cy.get(".spectrum-Menu").contains("Delete").click()
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
cy.get("input").type(name)
|
||||
cy.get(".spectrum-Button--warning").click()
|
||||
})
|
||||
cy.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -112,7 +66,6 @@ Cypress.Commands.add("createTestApp", () => {
|
|||
const appName = "Cypress Tests"
|
||||
cy.deleteApp(appName)
|
||||
cy.createApp(appName, "This app is used for Cypress testing.")
|
||||
cy.createScreen("home", "home")
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createTestTableWithData", () => {
|
||||
|
@ -121,18 +74,10 @@ Cypress.Commands.add("createTestTableWithData", () => {
|
|||
cy.addColumn("dog", "age", "Number")
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createTable", (tableName, initialTable) => {
|
||||
if (!initialTable) {
|
||||
cy.navigateToDataSection()
|
||||
cy.get(".add-button").click()
|
||||
}
|
||||
cy.wait(7000)
|
||||
cy.get(".spectrum-Modal")
|
||||
.contains("Budibase DB")
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.get(".spectrum-Button").contains("Continue").click({ force: true })
|
||||
})
|
||||
Cypress.Commands.add("createTable", tableName => {
|
||||
cy.contains("Budibase DB").click()
|
||||
cy.contains("Create new table").click()
|
||||
|
||||
cy.get(".spectrum-Modal").within(() => {
|
||||
cy.wait(1000)
|
||||
cy.get("input").first().type(tableName).blur()
|
||||
|
@ -239,49 +184,22 @@ Cypress.Commands.add("navigateToFrontend", () => {
|
|||
cy.wait(1000)
|
||||
cy.contains("Design").click()
|
||||
cy.get(".spectrum-Search").type("/")
|
||||
cy.createScreen("home", "home")
|
||||
cy.addComponent("Elements", "Headline")
|
||||
cy.get(".nav-item").contains("home").click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add("navigateToDataSection", () => {
|
||||
// Clicks on the Data tab
|
||||
cy.wait(500)
|
||||
cy.contains("Data").click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createScreen", (screenName, route) => {
|
||||
cy.contains("Design").click()
|
||||
cy.get("[aria-label=AddCircle]").click()
|
||||
cy.get(".spectrum-Modal").within(() => {
|
||||
cy.get(".item").contains("Blank").click()
|
||||
cy.get(".spectrum-Button").contains("Add Screens").click({ force: true })
|
||||
cy.wait(500)
|
||||
cy.get(".item").first().click()
|
||||
cy.get(".spectrum-Button--cta").click()
|
||||
})
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
cy.get(".spectrum-Form-itemField").eq(0).type(screenName)
|
||||
cy.get(".spectrum-Form-itemField").eq(1).type(route)
|
||||
cy.get(".spectrum-Button").contains("Continue").click({ force: true })
|
||||
cy.wait(1000)
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createAutogeneratedScreens", screenNames => {
|
||||
// Screen name must already exist within data source
|
||||
cy.contains("Design").click()
|
||||
cy.get("[aria-label=AddCircle]").click()
|
||||
for (let i = 0; i < screenNames.length; i++) {
|
||||
cy.get(".item").contains(screenNames[i]).click()
|
||||
}
|
||||
cy.get(".spectrum-Button").contains("Add Screens").click({ force: true })
|
||||
cy.wait(2000)
|
||||
})
|
||||
|
||||
Cypress.Commands.add("addRow", values => {
|
||||
cy.contains("Create row").click()
|
||||
cy.get(".spectrum-Modal").within(() => {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
cy.get("input").eq(i).type(values[i]).blur()
|
||||
}
|
||||
cy.get(".spectrum-ButtonGroup").contains("Create").click()
|
||||
cy.get("input").first().clear().type(screenName)
|
||||
cy.get("input").eq(1).clear().type(route)
|
||||
cy.get(".spectrum-Button--cta").click()
|
||||
cy.wait(2000)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -319,144 +237,7 @@ Cypress.Commands.add("addCustomSourceOptions", totalOptions => {
|
|||
})
|
||||
|
||||
Cypress.Commands.add("searchForApplication", appName => {
|
||||
cy.wait(1000)
|
||||
// Searches for the app
|
||||
cy.get(".filter").then(() => {
|
||||
cy.get(".spectrum-Textfield").within(() => {
|
||||
cy.get("input").eq(0).type(appName)
|
||||
})
|
||||
})
|
||||
// Confirms app exists after search
|
||||
cy.get(".appTable").contains(appName)
|
||||
})
|
||||
|
||||
Cypress.Commands.add("selectExternalDatasource", datasourceName => {
|
||||
// Navigates to Data Section
|
||||
cy.navigateToDataSection()
|
||||
// Open Data Source modal
|
||||
cy.get(".nav").within(() => {
|
||||
cy.get(".add-button").click()
|
||||
})
|
||||
// Clicks specified datasource & continue
|
||||
cy.get(".item-list").contains(datasourceName).click()
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
cy.get(".spectrum-Button").contains("Continue").click({ force: true })
|
||||
cy.get(".spectrum-Textfield").within(() => {
|
||||
cy.get("input").eq(0).type(appName)
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add("addDatasourceConfig", (datasource, skipFetch) => {
|
||||
// selectExternalDatasource should be called prior to this
|
||||
// Adds the config for specified datasource & fetches tables
|
||||
// Currently supports MySQL, PostgreSQL, Oracle
|
||||
// Host IP Address
|
||||
cy.wait(500)
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
cy.get(".form-row")
|
||||
.eq(0)
|
||||
.within(() => {
|
||||
cy.get(".spectrum-Textfield").within(() => {
|
||||
cy.log(datasource)
|
||||
if (datasource == "Oracle") {
|
||||
cy.get("input").clear().type(Cypress.env("oracle").HOST)
|
||||
} else {
|
||||
cy.get("input").clear().type(Cypress.env("HOST_IP"))
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
// Database Name
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
if (datasource == "MySQL") {
|
||||
cy.get(".form-row")
|
||||
.eq(4)
|
||||
.within(() => {
|
||||
cy.get("input").clear().type(Cypress.env("mysql").DATABASE)
|
||||
})
|
||||
} else {
|
||||
cy.get(".form-row")
|
||||
.eq(2)
|
||||
.within(() => {
|
||||
if (datasource == "PostgreSQL") {
|
||||
cy.get("input").clear().type(Cypress.env("postgresql").DATABASE)
|
||||
}
|
||||
if (datasource == "Oracle") {
|
||||
cy.get("input").clear().type(Cypress.env("oracle").DATABASE)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// User
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
if (datasource == "MySQL") {
|
||||
cy.get(".form-row")
|
||||
.eq(2)
|
||||
.within(() => {
|
||||
cy.get("input").clear().type(Cypress.env("mysql").USER)
|
||||
})
|
||||
} else {
|
||||
cy.get(".form-row")
|
||||
.eq(3)
|
||||
.within(() => {
|
||||
if (datasource == "PostgreSQL") {
|
||||
cy.get("input").clear().type(Cypress.env("postgresql").USER)
|
||||
}
|
||||
if (datasource == "Oracle") {
|
||||
cy.get("input").clear().type(Cypress.env("oracle").USER)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// Password
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
if (datasource == "MySQL") {
|
||||
cy.get(".form-row")
|
||||
.eq(3)
|
||||
.within(() => {
|
||||
cy.get("input").clear().type(Cypress.env("mysql").PASSWORD)
|
||||
})
|
||||
} else {
|
||||
cy.get(".form-row")
|
||||
.eq(4)
|
||||
.within(() => {
|
||||
if (datasource == "PostgreSQL") {
|
||||
cy.get("input").clear().type(Cypress.env("postgresql").PASSWORD)
|
||||
}
|
||||
if (datasource == "Oracle") {
|
||||
cy.get("input").clear().type(Cypress.env("oracle").PASSWORD)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// Click to fetch tables
|
||||
if (skipFetch) {
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
cy.get(".spectrum-Button").contains("Skip table fetch")
|
||||
.click({ force: true })
|
||||
})
|
||||
}
|
||||
else {
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
cy.get(".spectrum-Button").contains("Save and fetch tables")
|
||||
.click({ force: true })
|
||||
cy.wait(1000)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createRestQuery", (method, restUrl) => {
|
||||
// addExternalDatasource should be called prior to this
|
||||
// Configures REST datasource & sends query
|
||||
cy.wait(500)
|
||||
cy.get(".spectrum-Button").contains("Add query").click({ force: true })
|
||||
// Select Method & add Rest URL
|
||||
cy.get(".spectrum-Picker-label").eq(1).click()
|
||||
cy.get(".spectrum-Menu").contains(method).click()
|
||||
cy.get("input").clear().type(restUrl)
|
||||
// Send query
|
||||
cy.get(".spectrum-Button").contains("Send").click({ force: true })
|
||||
cy.wait(500)
|
||||
cy.get(".spectrum-Button").contains("Save query").click({ force: true })
|
||||
cy.get(".hierarchy-items-container")
|
||||
.should("contain", method)
|
||||
.and("contain", restUrl)
|
||||
})
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
const breweries = data
|
||||
const totals = {}
|
||||
|
||||
for (let brewery of breweries)
|
||||
{const state = brewery.state
|
||||
if (totals[state] == null)
|
||||
{totals[state] = 1
|
||||
} else
|
||||
{totals[state]++
|
||||
}
|
||||
}
|
||||
const entries = Object.entries(totals)
|
||||
return entries.map(([state, count]) => ({ state, count }))
|
|
@ -1,30 +0,0 @@
|
|||
const breweries = data
|
||||
const totals = {}
|
||||
for (let brewery of breweries)
|
||||
{const state = brewery.state
|
||||
if (totals[state] == null)
|
||||
{totals[state] = 1
|
||||
} else
|
||||
{totals[state]++
|
||||
}
|
||||
}
|
||||
const stateCodes =
|
||||
{texas: "tx",
|
||||
colorado: "co",
|
||||
florida: "fl",
|
||||
iwoa: "ia",
|
||||
louisiana: "la",
|
||||
california: "ca",
|
||||
pennsylvania: "pa",
|
||||
georgia: "ga",
|
||||
"new hampshire": "nh",
|
||||
virginia: "va",
|
||||
michigan: "mi",
|
||||
maryland: "md",
|
||||
ohio: "oh",
|
||||
}
|
||||
const entries = Object.entries(totals)
|
||||
return entries.map(([state, count]) =>
|
||||
{stateCodes[state.toLowerCase()]
|
||||
return { state, count, flag: "http://flags.ox3.in/svg/us/${stateCode}.svg" }
|
||||
})
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/builder",
|
||||
"version": "1.0.44-alpha.1",
|
||||
"version": "1.0.44-alpha.7",
|
||||
"license": "GPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -65,10 +65,10 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^1.0.44-alpha.1",
|
||||
"@budibase/client": "^1.0.44-alpha.1",
|
||||
"@budibase/bbui": "^1.0.44-alpha.7",
|
||||
"@budibase/client": "^1.0.44-alpha.7",
|
||||
"@budibase/colorpicker": "1.1.2",
|
||||
"@budibase/string-templates": "^1.0.44-alpha.1",
|
||||
"@budibase/string-templates": "^1.0.44-alpha.7",
|
||||
"@sentry/browser": "5.19.1",
|
||||
"@spectrum-css/page": "^3.0.1",
|
||||
"@spectrum-css/vars": "^3.0.1",
|
||||
|
@ -95,7 +95,7 @@
|
|||
"@testing-library/jest-dom": "^5.11.10",
|
||||
"@testing-library/svelte": "^3.0.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"cypress": "9.2.1",
|
||||
"cypress": "^5.1.0",
|
||||
"cypress-terminal-report": "^1.4.1",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^26.6.3",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
Select,
|
||||
Body,
|
||||
Layout,
|
||||
ActionButton,
|
||||
} from "@budibase/bbui"
|
||||
import { onMount, createEventDispatcher } from "svelte"
|
||||
import { FIELDS } from "constants/backend"
|
||||
|
@ -20,8 +21,8 @@
|
|||
let dispatcher = createEventDispatcher()
|
||||
let mode = "Form"
|
||||
let fieldCount = 0
|
||||
let fieldKeys = {},
|
||||
fieldTypes = {}
|
||||
let fieldKeys = [],
|
||||
fieldTypes = []
|
||||
let keyValueOptions = [
|
||||
{ label: "String", value: FIELDS.STRING.type },
|
||||
{ label: "Number", value: FIELDS.NUMBER.type },
|
||||
|
@ -50,27 +51,48 @@
|
|||
if (!schema) {
|
||||
schema = {}
|
||||
}
|
||||
let i = 0
|
||||
for (let [key, value] of Object.entries(schema)) {
|
||||
fieldKeys[i] = key
|
||||
fieldTypes[i] = value.type
|
||||
i++
|
||||
// find the entries which aren't in the list
|
||||
const schemaEntries = Object.entries(schema).filter(
|
||||
([key]) => !fieldKeys.includes(key)
|
||||
)
|
||||
for (let [key, value] of schemaEntries) {
|
||||
fieldKeys.push(key)
|
||||
fieldTypes.push(value.type)
|
||||
}
|
||||
fieldCount = i
|
||||
fieldCount = fieldKeys.length
|
||||
}
|
||||
|
||||
function saveSchema() {
|
||||
for (let i of Object.keys(fieldKeys)) {
|
||||
const key = fieldKeys[i]
|
||||
const newSchema = {}
|
||||
for (let [index, key] of fieldKeys.entries()) {
|
||||
// they were added to schema, rather than generated
|
||||
if (!schema[key]) {
|
||||
schema[key] = {
|
||||
type: fieldTypes[i],
|
||||
}
|
||||
newSchema[key] = {
|
||||
...schema[key],
|
||||
type: fieldTypes[index],
|
||||
}
|
||||
}
|
||||
dispatcher("save", { schema: newSchema, json })
|
||||
schema = newSchema
|
||||
}
|
||||
|
||||
dispatcher("save", { schema, json })
|
||||
function removeKey(index) {
|
||||
const keyToRemove = fieldKeys[index]
|
||||
if (fieldKeys[index + 1] != null) {
|
||||
fieldKeys[index] = fieldKeys[index + 1]
|
||||
fieldTypes[index] = fieldTypes[index + 1]
|
||||
}
|
||||
fieldKeys.splice(index, 1)
|
||||
fieldTypes.splice(index, 1)
|
||||
fieldCount--
|
||||
if (json) {
|
||||
try {
|
||||
const parsed = JSON.parse(json)
|
||||
delete parsed[keyToRemove]
|
||||
json = JSON.stringify(parsed, null, 2)
|
||||
} catch (err) {
|
||||
// json not valid, ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
@ -97,6 +119,7 @@
|
|||
getOptionValue={field => field.value}
|
||||
getOptionLabel={field => field.label}
|
||||
/>
|
||||
<ActionButton icon="Close" quiet on:click={() => removeKey(i)} />
|
||||
</div>
|
||||
{/each}
|
||||
<div class:add-field-btn={fieldCount !== 0}>
|
||||
|
@ -118,9 +141,9 @@
|
|||
<style>
|
||||
.horizontal {
|
||||
display: grid;
|
||||
grid-template-columns: 30% 1fr;
|
||||
grid-template-columns: 30% 1fr 40px;
|
||||
grid-gap: var(--spacing-s);
|
||||
align-items: center;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.add-field-btn {
|
||||
|
|
|
@ -6,9 +6,12 @@
|
|||
export let datasource
|
||||
|
||||
let name = ""
|
||||
let submitted = false
|
||||
$: valid = name && name.length > 0 && !datasource?.entities[name]
|
||||
$: error =
|
||||
name && datasource?.entities[name] ? "Table name already in use." : null
|
||||
!submitted && name && datasource?.entities[name]
|
||||
? "Table name already in use."
|
||||
: null
|
||||
|
||||
function buildDefaultTable(tableName, datasourceId) {
|
||||
return {
|
||||
|
@ -26,6 +29,7 @@
|
|||
}
|
||||
|
||||
async function saveTable() {
|
||||
submitted = true
|
||||
const table = await tables.save(buildDefaultTable(name, datasource._id))
|
||||
await datasources.fetch()
|
||||
$goto(`../../table/${table._id}`)
|
||||
|
|
|
@ -1,13 +1,38 @@
|
|||
<script>
|
||||
import { Body } from "@budibase/bbui"
|
||||
import { Label, Body, Layout } from "@budibase/bbui"
|
||||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||
|
||||
export let parameters
|
||||
export let bindings = []
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<Body size="S">This action doesn't require any additional settings.</Body>
|
||||
<Layout noPadding gap="M">
|
||||
<Body size="S">
|
||||
Please enter the URL you would like to be redirected to after logging out.
|
||||
If you don't enter a value, you'll be redirected to the login screen.
|
||||
</Body>
|
||||
<div class="content">
|
||||
<Label small>Redirect URL</Label>
|
||||
<DrawerBindableInput
|
||||
title="Return URL"
|
||||
value={parameters.redirectUrl}
|
||||
on:change={value => (parameters.redirectUrl = value.detail)}
|
||||
{bindings}
|
||||
/>
|
||||
</div>
|
||||
</Layout>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.content {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
gap: var(--spacing-m);
|
||||
grid-template-columns: auto 1fr;
|
||||
}
|
||||
</style>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/cli",
|
||||
"version": "1.0.44-alpha.1",
|
||||
"version": "1.0.44-alpha.7",
|
||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
|
|
|
@ -2065,6 +2065,26 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"label": "Direction",
|
||||
"key": "direction",
|
||||
"defaultValue": "vertical",
|
||||
"options": [
|
||||
{
|
||||
"label": "Horizontal",
|
||||
"value": "horizontal"
|
||||
},
|
||||
{
|
||||
"label": "Vertical",
|
||||
"value": "vertical"
|
||||
}
|
||||
],
|
||||
"dependsOn": {
|
||||
"setting": "optionsType",
|
||||
"value": "radio"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"label": "Default value",
|
||||
|
@ -2236,7 +2256,7 @@
|
|||
"setting": "optionsSource",
|
||||
"value": "provider"
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "options",
|
||||
"key": "customOptions",
|
||||
|
@ -2419,6 +2439,11 @@
|
|||
"label": "Label",
|
||||
"key": "label"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"label": "Extensions",
|
||||
"key": "extensions"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"label": "Disabled",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/client",
|
||||
"version": "1.0.44-alpha.1",
|
||||
"version": "1.0.44-alpha.7",
|
||||
"license": "MPL-2.0",
|
||||
"module": "dist/budibase-client.js",
|
||||
"main": "dist/budibase-client.js",
|
||||
|
@ -19,9 +19,9 @@
|
|||
"dev:builder": "rollup -cw"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^1.0.44-alpha.1",
|
||||
"@budibase/bbui": "^1.0.44-alpha.7",
|
||||
"@budibase/standard-components": "^0.9.139",
|
||||
"@budibase/string-templates": "^1.0.44-alpha.1",
|
||||
"@budibase/string-templates": "^1.0.44-alpha.7",
|
||||
"regexparam": "^1.3.0",
|
||||
"shortid": "^2.2.15",
|
||||
"svelte-spa-router": "^3.0.5"
|
||||
|
|
|
@ -18,6 +18,15 @@ export const logIn = async ({ email, password }) => {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the user out and invaidates their session.
|
||||
*/
|
||||
export const logOut = async () => {
|
||||
return await API.post({
|
||||
url: "/api/global/auth/logout",
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the currently logged in user object
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
export let label
|
||||
export let disabled = false
|
||||
export let validation
|
||||
export let extensions
|
||||
|
||||
let fieldState
|
||||
let fieldApi
|
||||
|
@ -52,6 +53,7 @@
|
|||
}}
|
||||
{processFiles}
|
||||
{handleFileTooLarge}
|
||||
{extensions}
|
||||
/>
|
||||
{/if}
|
||||
</Field>
|
||||
|
|
|
@ -141,8 +141,18 @@
|
|||
return
|
||||
}
|
||||
|
||||
// Create validation function based on field schema
|
||||
const schemaConstraints = schema?.[field]?.constraints
|
||||
const validator = createValidatorFromConstraints(
|
||||
schemaConstraints,
|
||||
validationRules,
|
||||
field,
|
||||
table
|
||||
)
|
||||
|
||||
// If we've already registered this field then keep some existing state
|
||||
let initialValue = deepGet(initialValues, field) ?? defaultValue
|
||||
let initialError = null
|
||||
let fieldId = `id-${generateID()}`
|
||||
const existingField = getField(field)
|
||||
if (existingField) {
|
||||
|
@ -156,20 +166,17 @@
|
|||
} else {
|
||||
initialValue = fieldState.value ?? initialValue
|
||||
}
|
||||
|
||||
// If this field has already been registered and we previously had an
|
||||
// error set, then re-run the validator to see if we can unset it
|
||||
if (fieldState.error) {
|
||||
initialError = validator(initialValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Auto columns are always disabled
|
||||
const isAutoColumn = !!schema?.[field]?.autocolumn
|
||||
|
||||
// Create validation function based on field schema
|
||||
const schemaConstraints = schema?.[field]?.constraints
|
||||
const validator = createValidatorFromConstraints(
|
||||
schemaConstraints,
|
||||
validationRules,
|
||||
field,
|
||||
table
|
||||
)
|
||||
|
||||
// Construct field info
|
||||
const fieldInfo = writable({
|
||||
name: field,
|
||||
|
@ -178,7 +185,7 @@
|
|||
fieldState: {
|
||||
fieldId,
|
||||
value: initialValue,
|
||||
error: null,
|
||||
error: initialError,
|
||||
disabled: disabled || fieldDisabled || isAutoColumn,
|
||||
defaultValue,
|
||||
validator,
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
let fieldSchema
|
||||
|
||||
$: flatOptions = optionsSource == null || optionsSource === "schema"
|
||||
$: expandedDefaultValue = expand(defaultValue)
|
||||
$: options = getOptions(
|
||||
optionsSource,
|
||||
fieldSchema,
|
||||
|
@ -28,6 +29,18 @@
|
|||
valueColumn,
|
||||
customOptions
|
||||
)
|
||||
|
||||
const expand = values => {
|
||||
if (!values) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (Array.isArray(values)) {
|
||||
return values
|
||||
}
|
||||
|
||||
return values.split(",").map(value => value.trim())
|
||||
}
|
||||
</script>
|
||||
|
||||
<Field
|
||||
|
@ -35,7 +48,7 @@
|
|||
{label}
|
||||
{disabled}
|
||||
{validation}
|
||||
{defaultValue}
|
||||
defaultValue={expandedDefaultValue}
|
||||
type="array"
|
||||
bind:fieldState
|
||||
bind:fieldApi
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
export let valueColumn
|
||||
export let customOptions
|
||||
export let autocomplete = false
|
||||
export let direction = "vertical"
|
||||
|
||||
let fieldState
|
||||
let fieldApi
|
||||
|
@ -64,6 +65,7 @@
|
|||
disabled={fieldState.disabled}
|
||||
error={fieldState.error}
|
||||
{options}
|
||||
{direction}
|
||||
on:change={e => fieldApi.setValue(e.detail)}
|
||||
getOptionLabel={flatOptions ? x => x : x => x.label}
|
||||
getOptionValue={flatOptions ? x => x : x => x.value}
|
||||
|
|
|
@ -11,8 +11,14 @@ const createAuthStore = () => {
|
|||
}
|
||||
|
||||
const logOut = async () => {
|
||||
try {
|
||||
await API.logOut()
|
||||
} catch (error) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
// Manually destroy cookie to be sure
|
||||
window.document.cookie = `budibase:auth=; budibase:currentapp=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
|
||||
window.location = "/builder/auth/login"
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -18,8 +18,8 @@ const createRouteStore = () => {
|
|||
const fetchRoutes = async () => {
|
||||
const routeConfig = await API.fetchRoutes()
|
||||
let routes = []
|
||||
Object.values(routeConfig.routes).forEach(route => {
|
||||
Object.entries(route.subpaths).forEach(([path, config]) => {
|
||||
Object.values(routeConfig.routes || {}).forEach(route => {
|
||||
Object.entries(route.subpaths || {}).forEach(([path, config]) => {
|
||||
routes.push({
|
||||
path,
|
||||
screenId: config.screenId,
|
||||
|
@ -83,12 +83,23 @@ const createRouteStore = () => {
|
|||
const setRouterLoaded = () => {
|
||||
store.update(state => ({ ...state, routerLoaded: true }))
|
||||
}
|
||||
const createFullURL = relativeURL => {
|
||||
if (!relativeURL?.startsWith("/")) {
|
||||
return relativeURL
|
||||
}
|
||||
if (!window.location.href.includes("#")) {
|
||||
return `${window.location.href}#${relativeURL}`
|
||||
}
|
||||
const base = window.location.href.split("#")[0]
|
||||
return `${base}#${relativeURL}`
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: {
|
||||
fetchRoutes,
|
||||
navigate,
|
||||
createFullURL,
|
||||
setRouteParams,
|
||||
setQueryParams,
|
||||
setActiveRoute,
|
||||
|
|
|
@ -112,8 +112,20 @@ const refreshDataProviderHandler = async (action, context) => {
|
|||
)
|
||||
}
|
||||
|
||||
const logoutHandler = async () => {
|
||||
const logoutHandler = async action => {
|
||||
await authStore.actions.logOut()
|
||||
let redirectUrl = "/builder/auth/login"
|
||||
let internal = false
|
||||
if (action.parameters.redirectUrl) {
|
||||
internal = action.parameters.redirectUrl?.startsWith("/")
|
||||
redirectUrl = routeStore.actions.createFullURL(
|
||||
action.parameters.redirectUrl
|
||||
)
|
||||
}
|
||||
window.location.href = redirectUrl
|
||||
if (internal) {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
const clearFormHandler = async (action, context) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/server",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "1.0.44-alpha.1",
|
||||
"version": "1.0.44-alpha.7",
|
||||
"description": "Budibase Web Server",
|
||||
"main": "src/index.ts",
|
||||
"repository": {
|
||||
|
@ -70,9 +70,9 @@
|
|||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@apidevtools/swagger-parser": "^10.0.3",
|
||||
"@budibase/backend-core": "^1.0.44-alpha.1",
|
||||
"@budibase/client": "^1.0.44-alpha.1",
|
||||
"@budibase/string-templates": "^1.0.44-alpha.1",
|
||||
"@budibase/backend-core": "^1.0.44-alpha.7",
|
||||
"@budibase/client": "^1.0.44-alpha.7",
|
||||
"@budibase/string-templates": "^1.0.44-alpha.7",
|
||||
"@bull-board/api": "^3.7.0",
|
||||
"@bull-board/koa": "^3.7.0",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
|
|
|
@ -143,11 +143,46 @@ export function isIsoDateString(str: string) {
|
|||
return d.toISOString() === str
|
||||
}
|
||||
|
||||
// add the existing relationships from the entities if they exist, to prevent them from being overridden
|
||||
/**
|
||||
* This function will determine whether a column is a relationship and whether it
|
||||
* is currently valid. The reason for the validity check is that tables can be deleted
|
||||
* outside of Budibase control and if this is the case it will break Budibase relationships.
|
||||
* The tableIds is a list passed down from the main finalise tables function, which is
|
||||
* based on the tables that have just been fetched. This will only really be used on subsequent
|
||||
* fetches to the first one - if the user is periodically refreshing Budibase knowledge of tables.
|
||||
* @param column The column to check, to see if it is a valid relationship.
|
||||
* @param tableIds The IDs of the tables which currently exist.
|
||||
*/
|
||||
function shouldCopyRelationship(column: { type: string, tableId?: string }, tableIds: [string]) {
|
||||
return column.type === FieldTypes.LINK && column.tableId && tableIds.includes(column.tableId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar function to the shouldCopyRelationship function, but instead this looks for options and boolean
|
||||
* types. It is possible to switch a string -> options and a number -> boolean (and vice versus) need to make
|
||||
* sure that these get copied over when tables are fetched. Also checks whether they are still valid, if a
|
||||
* column has changed type in the external database then copying it over may not be possible.
|
||||
* @param column The column to check for options or boolean type.
|
||||
* @param fetchedColumn The fetched column to check for the type in the external database.
|
||||
*/
|
||||
function shouldCopySpecialColumn(column: { type: string }, fetchedColumn: { type: string } | undefined) {
|
||||
return column.type === FieldTypes.OPTIONS ||
|
||||
((!fetchedColumn || fetchedColumn.type === FieldTypes.NUMBER) && column.type === FieldTypes.BOOLEAN)
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for columns which need to be copied over into the new table definitions, like relationships
|
||||
* and options types.
|
||||
* @param tableName The name of the table which is being checked.
|
||||
* @param table The specific table which is being checked.
|
||||
* @param entities All the tables that existed before - the old table definitions.
|
||||
* @param tableIds The IDs of the tables which exist now, to check if anything has been removed.
|
||||
*/
|
||||
function copyExistingPropsOver(
|
||||
tableName: string,
|
||||
table: Table,
|
||||
entities: { [key: string]: any }
|
||||
entities: { [key: string]: any },
|
||||
tableIds: [string]
|
||||
) {
|
||||
if (entities && entities[tableName]) {
|
||||
if (entities[tableName].primaryDisplay) {
|
||||
|
@ -158,11 +193,10 @@ function copyExistingPropsOver(
|
|||
if (!existingTableSchema.hasOwnProperty(key)) {
|
||||
continue
|
||||
}
|
||||
const column = existingTableSchema[key]
|
||||
if (
|
||||
existingTableSchema[key].type === FieldTypes.LINK ||
|
||||
existingTableSchema[key].type === FieldTypes.OPTIONS ||
|
||||
((!table.schema[key] || table.schema[key].type === FieldTypes.NUMBER) &&
|
||||
existingTableSchema[key].type === FieldTypes.BOOLEAN)
|
||||
shouldCopyRelationship(column, tableIds) ||
|
||||
shouldCopySpecialColumn(column, table.schema[key])
|
||||
) {
|
||||
table.schema[key] = existingTableSchema[key]
|
||||
}
|
||||
|
@ -171,6 +205,13 @@ function copyExistingPropsOver(
|
|||
return table
|
||||
}
|
||||
|
||||
/**
|
||||
* Look through the final table definitions to see if anything needs to be
|
||||
* copied over from the old and if any errors have occurred mark them so
|
||||
* that the user can be made aware.
|
||||
* @param tables The list of tables that have been retrieved from the external database.
|
||||
* @param entities The old list of tables, if there was any to look for definitions in.
|
||||
*/
|
||||
export function finaliseExternalTables(
|
||||
tables: { [key: string]: any },
|
||||
entities: { [key: string]: any }
|
||||
|
@ -178,6 +219,8 @@ export function finaliseExternalTables(
|
|||
const invalidColumns = Object.values(InvalidColumns)
|
||||
let finalTables: { [key: string]: any } = {}
|
||||
const errors: { [key: string]: string } = {}
|
||||
// @ts-ignore
|
||||
const tableIds: [string] = Object.values(tables).map(table => table._id)
|
||||
for (let [name, table] of Object.entries(tables)) {
|
||||
const schemaFields = Object.keys(table.schema)
|
||||
// make sure every table has a key
|
||||
|
@ -189,7 +232,7 @@ export function finaliseExternalTables(
|
|||
continue
|
||||
}
|
||||
// make sure all previous props have been added back
|
||||
finalTables[name] = copyExistingPropsOver(name, table, entities)
|
||||
finalTables[name] = copyExistingPropsOver(name, table, entities, tableIds)
|
||||
}
|
||||
// sort the tables by name
|
||||
finalTables = Object.entries(finalTables)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/string-templates",
|
||||
"version": "1.0.44-alpha.1",
|
||||
"version": "1.0.44-alpha.7",
|
||||
"description": "Handlebars wrapper for Budibase templating.",
|
||||
"main": "src/index.cjs",
|
||||
"module": "dist/bundle.mjs",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/worker",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "1.0.23-alpha.2",
|
||||
"version": "1.0.44-alpha.7",
|
||||
"description": "Budibase background service",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
|
@ -29,8 +29,8 @@
|
|||
"author": "Budibase",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@budibase/backend-core": "^1.0.44-alpha.1",
|
||||
"@budibase/string-templates": "^1.0.44-alpha.1",
|
||||
"@budibase/backend-core": "^1.0.44-alpha.7",
|
||||
"@budibase/string-templates": "^1.0.44-alpha.7",
|
||||
"@koa/router": "^8.0.0",
|
||||
"@sentry/node": "^6.0.0",
|
||||
"@techpass/passport-openidconnect": "^0.3.0",
|
||||
|
|
|
@ -141,7 +141,9 @@ exports.resetUpdate = async ctx => {
|
|||
}
|
||||
|
||||
exports.logout = async ctx => {
|
||||
await platformLogout({ ctx, userId: ctx.user._id })
|
||||
if (ctx.user && ctx.user._id) {
|
||||
await platformLogout({ ctx, userId: ctx.user._id })
|
||||
}
|
||||
ctx.body = { message: "User logged out." }
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ exports.fetch = async ctx => {
|
|||
cloud: !env.SELF_HOSTED,
|
||||
accountPortalUrl: env.ACCOUNT_PORTAL_URL,
|
||||
disableAccountPortal: env.DISABLE_ACCOUNT_PORTAL,
|
||||
isDev: env.isDev(),
|
||||
// in test need to pretend its in production for the UI (Cypress)
|
||||
isDev: env.isDev() && !env.isTest(),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue