Merge branch 'master' of github.com:Budibase/budibase into interactive-layouts
This commit is contained in:
commit
2383e4e54a
|
@ -45,9 +45,10 @@ jobs:
|
|||
|
||||
- name: Build and Push Staging Docker Image
|
||||
# Only run on push
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
|
||||
run: |
|
||||
docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
|
||||
yarn build
|
||||
yarn build:docker:staging
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
|
||||
|
|
|
@ -4,6 +4,11 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
POSTHOG_TOKEN: ${{ secrets.POSTHOG_TOKEN }}
|
||||
POSTHOG_URL: ${{ secrets.POSTHOG_URL }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
|
||||
jobs:
|
||||
release:
|
||||
|
@ -18,10 +23,6 @@ jobs:
|
|||
- run: yarn lint
|
||||
- run: yarn bootstrap
|
||||
- run: yarn build
|
||||
env:
|
||||
POSTHOG_TOKEN: ${{ secrets.POSTHOG_TOKEN }}
|
||||
POSTHOG_URL: ${{ secrets.POSTHOG_URL }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
- run: yarn test
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
|
|
|
@ -6,6 +6,7 @@ services:
|
|||
app-service:
|
||||
restart: always
|
||||
image: budibase/apps
|
||||
container_name: bbapps
|
||||
ports:
|
||||
- "${APP_PORT}:4002"
|
||||
environment:
|
||||
|
@ -32,6 +33,7 @@ services:
|
|||
worker-service:
|
||||
restart: always
|
||||
image: budibase/worker
|
||||
container_name: bbworker
|
||||
ports:
|
||||
- "${WORKER_PORT}:4003"
|
||||
environment:
|
||||
|
@ -118,7 +120,7 @@ services:
|
|||
image: containrrr/watchtower
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
command: --debug --http-api-update budibase/apps budibase/worker
|
||||
command: --debug --http-api-update bbapps bbworker
|
||||
environment:
|
||||
- WATCHTOWER_HTTP_API=true
|
||||
- WATCHTOWER_HTTP_API_TOKEN=budibase
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
tag=$1
|
||||
tag=${tag:-latest}
|
||||
|
||||
echo "Tagging images with SHA: $GITHUB_SHA and version: $BUDIBASE_VERSION"
|
||||
echo "Tagging images with SHA: $GITHUB_SHA and tag: $tag"
|
||||
|
||||
docker tag app-service budibase/apps:$tag
|
||||
docker tag worker-service budibase/worker:$tag
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "0.9.27",
|
||||
"version": "0.9.39",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
|
|
|
@ -39,6 +39,6 @@
|
|||
"test:e2e": "lerna run cy:test",
|
||||
"test:e2e:ci": "lerna run cy:ci",
|
||||
"build:docker": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh && cd -",
|
||||
"build:docker:staging": "cd hosting/scripts/linux/ && ./release-to-docker-hub.sh staging && cd -"
|
||||
"build:docker:staging": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh staging && cd -"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/auth",
|
||||
"version": "0.9.27",
|
||||
"version": "0.9.39",
|
||||
"description": "Authentication middlewares for budibase builder and apps",
|
||||
"main": "src/index.js",
|
||||
"author": "Budibase",
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const { newid } = require("../hashing")
|
||||
const Replication = require("./Replication")
|
||||
const { getCouch } = require("./index")
|
||||
|
||||
const UNICODE_MAX = "\ufff0"
|
||||
const SEPARATOR = "_"
|
||||
|
@ -164,14 +163,14 @@ exports.getDeployedAppID = appId => {
|
|||
* different users/companies apps as there is no security around it - all apps are returned.
|
||||
* @return {Promise<object[]>} returns the app information document stored in each app database.
|
||||
*/
|
||||
exports.getAllApps = async ({ dev, all } = {}) => {
|
||||
const CouchDB = getCouch()
|
||||
exports.getAllApps = async ({ CouchDB, dev, all } = {}) => {
|
||||
let allDbs = await CouchDB.allDbs()
|
||||
const appDbNames = allDbs.filter(dbName =>
|
||||
dbName.startsWith(exports.APP_PREFIX)
|
||||
)
|
||||
const appPromises = appDbNames.map(db =>
|
||||
new CouchDB(db).get(DocumentTypes.APP_METADATA)
|
||||
// skip setup otherwise databases could be re-created
|
||||
new CouchDB(db, { skip_setup: true }).get(DocumentTypes.APP_METADATA)
|
||||
)
|
||||
if (appPromises.length === 0) {
|
||||
return []
|
||||
|
@ -196,6 +195,21 @@ exports.getAllApps = async ({ dev, all } = {}) => {
|
|||
}
|
||||
}
|
||||
|
||||
exports.dbExists = async (CouchDB, dbName) => {
|
||||
let exists = false
|
||||
try {
|
||||
const db = CouchDB(dbName, { skip_setup: true })
|
||||
// check if database exists
|
||||
const info = await db.info()
|
||||
if (info && !info.error) {
|
||||
exists = true
|
||||
}
|
||||
} catch (err) {
|
||||
exists = false
|
||||
}
|
||||
return exists
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new configuration ID.
|
||||
* @returns {string} The new configuration ID which the config doc can be stored under.
|
||||
|
|
|
@ -20,16 +20,10 @@ async function authenticate(token, tokenSecret, profile, done) {
|
|||
// use the google profile id
|
||||
dbUser = await db.get(userId)
|
||||
} catch (err) {
|
||||
console.log("Google user not found. Creating..")
|
||||
|
||||
// create the user
|
||||
const user = {
|
||||
_id: userId,
|
||||
provider: profile.provider,
|
||||
roles: {},
|
||||
builder: {
|
||||
global: true,
|
||||
},
|
||||
...profile._json,
|
||||
}
|
||||
|
||||
|
@ -50,12 +44,18 @@ async function authenticate(token, tokenSecret, profile, done) {
|
|||
user.roles = existing.roles
|
||||
user.builder = existing.builder
|
||||
user.admin = existing.admin
|
||||
|
||||
const response = await db.post(user)
|
||||
dbUser = user
|
||||
dbUser._rev = response.rev
|
||||
} else {
|
||||
return done(
|
||||
new Error(
|
||||
"email does not yet exist. You must set up your local budibase account first."
|
||||
),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
const response = await db.post(user)
|
||||
|
||||
dbUser = user
|
||||
dbUser._rev = response.rev
|
||||
}
|
||||
|
||||
// authenticate
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/bbui",
|
||||
"description": "A UI solution used in the different Budibase projects.",
|
||||
"version": "0.9.27",
|
||||
"version": "0.9.39",
|
||||
"license": "AGPL-3.0",
|
||||
"svelte": "src/index.js",
|
||||
"module": "dist/bbui.es.js",
|
||||
|
|
|
@ -402,13 +402,13 @@ braces@~3.0.2:
|
|||
fill-range "^7.0.1"
|
||||
|
||||
browserslist@^4.0.0:
|
||||
version "4.16.4"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.4.tgz#7ebf913487f40caf4637b892b268069951c35d58"
|
||||
integrity sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ==
|
||||
version "4.16.6"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
|
||||
integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001208"
|
||||
caniuse-lite "^1.0.30001219"
|
||||
colorette "^1.2.2"
|
||||
electron-to-chromium "^1.3.712"
|
||||
electron-to-chromium "^1.3.723"
|
||||
escalade "^3.1.1"
|
||||
node-releases "^1.1.71"
|
||||
|
||||
|
@ -469,10 +469,10 @@ caniuse-api@^3.0.0:
|
|||
lodash.memoize "^4.1.2"
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001208:
|
||||
version "1.0.30001209"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001209.tgz#1bb4be0bd118e98e21cfb7ef617b1ef2164622f4"
|
||||
integrity sha512-2Ktt4OeRM7EM/JaOZjuLzPYAIqmbwQMNnYbgooT+icoRGrKOyAxA1xhlnotBD1KArRSPsuJp3TdYcZYrL7qNxA==
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001219:
|
||||
version "1.0.30001235"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz#ad5ca75bc5a1f7b12df79ad806d715a43a5ac4ed"
|
||||
integrity sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A==
|
||||
|
||||
chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
|
@ -856,10 +856,10 @@ ee-first@1.1.1:
|
|||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
||||
|
||||
electron-to-chromium@^1.3.712:
|
||||
version "1.3.717"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.717.tgz#78d4c857070755fb58ab64bcc173db1d51cbc25f"
|
||||
integrity sha512-OfzVPIqD1MkJ7fX+yTl2nKyOE4FReeVfMCzzxQS+Kp43hZYwHwThlGP+EGIZRXJsxCM7dqo8Y65NOX/HP12iXQ==
|
||||
electron-to-chromium@^1.3.723:
|
||||
version "1.3.749"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.749.tgz#0ecebc529ceb49dd2a7c838ae425236644c3439a"
|
||||
integrity sha512-F+v2zxZgw/fMwPz/VUGIggG4ZndDsYy0vlpthi3tjmDZlcfbhN5mYW0evXUsBr2sUtuDANFtle410A9u/sd/4A==
|
||||
|
||||
emojis-list@^3.0.0:
|
||||
version "3.0.0"
|
||||
|
@ -1554,9 +1554,9 @@ negotiator@0.6.2:
|
|||
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
|
||||
|
||||
node-releases@^1.1.71:
|
||||
version "1.1.71"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb"
|
||||
integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==
|
||||
version "1.1.73"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20"
|
||||
integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==
|
||||
|
||||
nollup@^0.14.1:
|
||||
version "0.14.4"
|
||||
|
@ -2564,9 +2564,9 @@ wrappy@1:
|
|||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
ws@^5.2.0:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
|
||||
integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d"
|
||||
integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"baseUrl": "http://localhost:10000/builder/",
|
||||
"baseUrl": "http://localhost:10001/builder/",
|
||||
"video": true,
|
||||
"projectId": "bmbemn",
|
||||
"env": {
|
||||
"PORT": "10000",
|
||||
"PORT": "10001",
|
||||
"JWT_SECRET": "test"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,31 +8,27 @@ context("Create a automation", () => {
|
|||
it("should create a automation", () => {
|
||||
cy.createTestTableWithData()
|
||||
|
||||
cy.contains("automate").click()
|
||||
cy.get("[data-cy=new-automation]").click()
|
||||
cy.get(".modal").within(() => {
|
||||
cy.contains("Automate").click()
|
||||
cy.get("[data-cy='new-screen'] > .spectrum-Icon").click()
|
||||
cy.get(".spectrum-Dialog-grid").within(() => {
|
||||
cy.get("input").type("Add Row")
|
||||
cy.get(".buttons")
|
||||
.contains("Create")
|
||||
.click()
|
||||
cy.get(".spectrum-Button--cta").click()
|
||||
})
|
||||
|
||||
// Add trigger
|
||||
cy.contains("Trigger").click()
|
||||
cy.contains("Row Created").click()
|
||||
cy.get(".setup").within(() => {
|
||||
cy.get("select")
|
||||
.first()
|
||||
.select("dog")
|
||||
cy.get(".spectrum-Picker-label").click()
|
||||
cy.contains("dog").click()
|
||||
})
|
||||
|
||||
// Create action
|
||||
cy.contains("Action").click()
|
||||
cy.contains("Create Row").click()
|
||||
cy.get(".setup").within(() => {
|
||||
cy.get("select")
|
||||
.first()
|
||||
.select("dog")
|
||||
cy.get(".spectrum-Picker-label").click()
|
||||
cy.contains("dog").click()
|
||||
cy.get("input")
|
||||
.first()
|
||||
.type("goodboy")
|
||||
|
@ -45,12 +41,11 @@ context("Create a automation", () => {
|
|||
cy.contains("Save Automation").click()
|
||||
|
||||
// Activate Automation
|
||||
cy.get("[data-cy=activate-automation]").click()
|
||||
cy.get(".ri-stop-circle-fill.highlighted").should("be.visible")
|
||||
cy.get("[aria-label=PlayCircle]").click()
|
||||
})
|
||||
|
||||
it("should add row when a new row is added", () => {
|
||||
cy.contains("data").click()
|
||||
cy.contains("Data").click()
|
||||
cy.addRow(["Rover", 15])
|
||||
cy.reload()
|
||||
cy.contains("goodboy").should("have.text", "goodboy")
|
||||
|
|
|
@ -36,7 +36,9 @@ context("Create Bindings", () => {
|
|||
it("should add a binding with a handlebars helper", () => {
|
||||
cy.addComponent("Elements", "Paragraph").then(componentId => {
|
||||
// Cypress needs to escape curly brackets
|
||||
addSettingBinding("text", "{{}{{} add 1 2 {}}{}}", false)
|
||||
cy.get("[data-cy=setting-text] input")
|
||||
.type("{{}{{} add 1 2 {}}{}}")
|
||||
.blur()
|
||||
cy.getComponent(componentId).should("have.text", "3")
|
||||
})
|
||||
})
|
||||
|
@ -51,6 +53,6 @@ const addSettingBinding = (setting, bindingText, clickOption = true) => {
|
|||
} else {
|
||||
cy.get("textarea").type(bindingText)
|
||||
}
|
||||
cy.get("button").click()
|
||||
cy.contains("Save").click()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
context("Create Components", () => {
|
||||
// TODO for now components are skipped, might not be good to keep doing this
|
||||
xcontext("Create Components", () => {
|
||||
let headlineId
|
||||
|
||||
before(() => {
|
||||
cy.login()
|
||||
cy.createTestApp()
|
||||
cy.createTable("dog")
|
||||
cy.addColumn("dog", "name", "string")
|
||||
cy.addColumn("dog", "age", "number")
|
||||
cy.addColumn("dog", "type", "options")
|
||||
cy.addColumn("dog", "name", "Text")
|
||||
cy.addColumn("dog", "age", "Number")
|
||||
cy.addColumn("dog", "type", "Options")
|
||||
cy.navigateToFrontend()
|
||||
})
|
||||
|
||||
|
|
|
@ -22,54 +22,49 @@ context("Create a Table", () => {
|
|||
})
|
||||
|
||||
it("updates a column on the table", () => {
|
||||
cy.contains("header", "name")
|
||||
.trigger("mouseover")
|
||||
.find(".ri-pencil-line")
|
||||
.click({ force: true })
|
||||
cy.get(".actions input")
|
||||
.first()
|
||||
.type("updated")
|
||||
cy.get(".title").click()
|
||||
cy.get(".spectrum-Table-editIcon > use").click()
|
||||
cy.get("input")
|
||||
.eq(1)
|
||||
.type("updated", { force: true })
|
||||
// Unset table display column
|
||||
cy.contains("display column").click()
|
||||
cy.get(".spectrum-Switch-input").eq(1).click()
|
||||
cy.contains("Save Column").click()
|
||||
cy.contains("nameupdated ").should("have.text", "nameupdated ")
|
||||
cy.contains("nameupdated ").should("contain", "nameupdated")
|
||||
})
|
||||
|
||||
it("edits a row", () => {
|
||||
cy.contains("button", "Edit").click({ force: true })
|
||||
cy.wait(1000)
|
||||
cy.get(".modal input").type("Updated")
|
||||
cy.get(".spectrum-Modal input").type("Updated")
|
||||
cy.contains("Save").click()
|
||||
cy.contains("RoverUpdated").should("have.text", "RoverUpdated")
|
||||
})
|
||||
|
||||
it("deletes a row", () => {
|
||||
cy.get(".ag-checkbox-input").check({ force: true })
|
||||
cy.get(".spectrum-Checkbox-input").check({ force: true })
|
||||
cy.contains("Delete 1 row(s)").click()
|
||||
cy.get(".modal")
|
||||
cy.get(".spectrum-Modal")
|
||||
.contains("Delete")
|
||||
.click()
|
||||
cy.contains("RoverUpdated").should("not.exist")
|
||||
})
|
||||
|
||||
it("deletes a column", () => {
|
||||
cy.contains("header", "name")
|
||||
.trigger("mouseover")
|
||||
.find(".ri-pencil-line")
|
||||
.click({ force: true })
|
||||
cy.get(".title").click()
|
||||
cy.get(".spectrum-Table-editIcon > use").click()
|
||||
cy.contains("Delete").click()
|
||||
cy.wait(50)
|
||||
cy.get(".buttons")
|
||||
.contains("Delete")
|
||||
cy.contains("Delete Column")
|
||||
.click()
|
||||
cy.contains("nameupdated").should("not.exist")
|
||||
})
|
||||
|
||||
it("deletes a table", () => {
|
||||
cy.get(".ri-more-line")
|
||||
cy.get(".actions > :nth-child(1) > .icon > .spectrum-Icon > use")
|
||||
.first()
|
||||
.click({ force: true })
|
||||
cy.get("[data-cy=delete-table]").click()
|
||||
cy.get(".spectrum-Menu > :nth-child(2)").click()
|
||||
cy.contains("Delete Table").click()
|
||||
cy.contains("dog").should("not.exist")
|
||||
})
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
context("Create a User", () => {
|
||||
before(() => {
|
||||
cy.login()
|
||||
cy.createTestApp()
|
||||
})
|
||||
|
||||
it("should create a user", () => {
|
||||
cy.createUser("bbuser@test.com", "test", "ADMIN")
|
||||
cy.createUser("bbuser@test.com")
|
||||
cy.contains("bbuser").should("be.visible")
|
||||
})
|
||||
})
|
||||
|
|
|
@ -17,21 +17,21 @@ context("Create a View", () => {
|
|||
})
|
||||
|
||||
it("creates a view", () => {
|
||||
cy.contains("Create New View").click()
|
||||
cy.get(".menu-container").within(() => {
|
||||
cy.contains("Create view").click()
|
||||
cy.get(".modal-inner-wrapper").within(() => {
|
||||
cy.get("input").type("Test View")
|
||||
cy.contains("Save View").click()
|
||||
cy.get("button").contains("Create View").click({ force: true })
|
||||
})
|
||||
cy.get(".table-title h1").contains("Test View")
|
||||
cy.get("[data-cy=table-header]").then($headers => {
|
||||
cy.get(".title").then($headers => {
|
||||
expect($headers).to.have.length(3)
|
||||
const headers = Array.from($headers).map(header =>
|
||||
header.textContent.trim()
|
||||
)
|
||||
expect(removeSpacing(headers)).to.deep.eq([
|
||||
"rating Number",
|
||||
"age Number",
|
||||
"group Text",
|
||||
"group",
|
||||
"age",
|
||||
"rating",
|
||||
])
|
||||
})
|
||||
})
|
||||
|
@ -39,97 +39,95 @@ context("Create a View", () => {
|
|||
it("filters the view by age over 10", () => {
|
||||
cy.contains("Filter").click()
|
||||
cy.contains("Add Filter").click()
|
||||
cy.get(".menu-container")
|
||||
.find("select")
|
||||
.first()
|
||||
.select("age")
|
||||
cy.get(".menu-container")
|
||||
.find("select")
|
||||
.eq(1)
|
||||
.select("More Than")
|
||||
cy.get(".menu-container")
|
||||
.find("input")
|
||||
.type(18)
|
||||
cy.contains("Save").click()
|
||||
cy.get("[role=rowgroup] .ag-row").get($values => {
|
||||
|
||||
cy.get(".modal-inner-wrapper").within(() => {
|
||||
cy.get(".spectrum-Picker-label").eq(0).click()
|
||||
cy.contains("age").click({ force: true })
|
||||
|
||||
cy.get(".spectrum-Picker-label").eq(1).click()
|
||||
cy.contains("More Than").click({ force: true })
|
||||
|
||||
cy.get("input").type(18)
|
||||
cy.contains("Save").click()
|
||||
})
|
||||
|
||||
cy.get(".spectrum-Table-row").get($values => {
|
||||
expect($values).to.have.length(5)
|
||||
})
|
||||
})
|
||||
|
||||
it("creates a stats calculation view based on age", () => {
|
||||
// Required due to responsive bug with ag grid in cypress
|
||||
cy.viewport("macbook-15")
|
||||
|
||||
cy.contains("Calculate").click()
|
||||
cy.get(".menu-container")
|
||||
.find("select")
|
||||
.eq(0)
|
||||
.select("Statistics")
|
||||
cy.wait(50)
|
||||
cy.get(".menu-container")
|
||||
.find("select")
|
||||
.eq(1)
|
||||
.select("age")
|
||||
cy.contains("Save").click()
|
||||
cy.get(".modal-inner-wrapper").within(() => {
|
||||
cy.get(".spectrum-Picker-label").eq(0).click()
|
||||
cy.contains("Statistics").click()
|
||||
|
||||
cy.get(".spectrum-Picker-label").eq(1).click()
|
||||
cy.contains("age").click({ force: true })
|
||||
|
||||
cy.contains("Save").click()
|
||||
})
|
||||
cy.wait(1000)
|
||||
cy.get(".ag-center-cols-viewport").scrollTo("100%")
|
||||
cy.get("[data-cy=table-header]").then($headers => {
|
||||
|
||||
cy.get(".title").then($headers => {
|
||||
expect($headers).to.have.length(7)
|
||||
const headers = Array.from($headers).map(header =>
|
||||
header.textContent.trim()
|
||||
)
|
||||
expect(removeSpacing(headers)).to.deep.eq([
|
||||
"avg Number",
|
||||
"sumsqr Number",
|
||||
"count Number",
|
||||
"max Number",
|
||||
"min Number",
|
||||
"sum Number",
|
||||
"field Text",
|
||||
"field",
|
||||
"sum",
|
||||
"min",
|
||||
"max",
|
||||
"count",
|
||||
"sumsqr",
|
||||
"avg",
|
||||
])
|
||||
})
|
||||
cy.get(".ag-cell").then($values => {
|
||||
cy.get(".spectrum-Table-cell").then($values => {
|
||||
let values = Array.from($values).map(header => header.textContent.trim())
|
||||
expect(values).to.deep.eq(["31", "5347", "5", "49", "20", "155", "age"])
|
||||
expect(values).to.deep.eq(["age", "155", "20", "49", "5", "5347", "31"])
|
||||
})
|
||||
})
|
||||
|
||||
it("groups the view by group", () => {
|
||||
// Required due to responsive bug with ag grid in cypress
|
||||
cy.viewport("macbook-15")
|
||||
|
||||
cy.contains("Group By").click()
|
||||
cy.get("select").select("group")
|
||||
cy.contains("Save").click()
|
||||
cy.contains("Group by").click()
|
||||
cy.get(".modal-inner-wrapper").within(() => {
|
||||
cy.get(".spectrum-Picker-label").eq(0).click()
|
||||
cy.contains("group").click()
|
||||
cy.contains("Save").click()
|
||||
})
|
||||
cy.wait(1000)
|
||||
cy.get(".ag-center-cols-viewport").scrollTo("100%")
|
||||
cy.contains("Students").should("be.visible")
|
||||
cy.contains("Teachers").should("be.visible")
|
||||
|
||||
cy.get(".ag-row[row-index=0]")
|
||||
.find(".ag-cell")
|
||||
.then($values => {
|
||||
const values = Array.from($values).map(value => value.textContent)
|
||||
expect(values.sort()).to.deep.eq(
|
||||
[
|
||||
cy.get(".spectrum-Table-cell").then($values => {
|
||||
let values = Array.from($values).map(header => header.textContent.trim())
|
||||
expect(values).to.deep.eq([
|
||||
"Students",
|
||||
"23.333333333333332",
|
||||
"1650",
|
||||
"3",
|
||||
"25",
|
||||
"20",
|
||||
"70",
|
||||
].sort()
|
||||
)
|
||||
})
|
||||
"20",
|
||||
"25",
|
||||
"3",
|
||||
"1650",
|
||||
"23.333333333333332",
|
||||
"Teachers",
|
||||
"85",
|
||||
"36",
|
||||
"49",
|
||||
"2",
|
||||
"3697",
|
||||
"42.5",
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
it("renames a view", () => {
|
||||
cy.contains(".nav-item", "Test View")
|
||||
.find(".ri-more-line")
|
||||
.find(".actions .icon")
|
||||
.click({ force: true })
|
||||
cy.get("[data-cy=edit-view]").click()
|
||||
cy.get(".menu-container").within(() => {
|
||||
cy.contains("Edit").click()
|
||||
cy.get(".modal-inner-wrapper").within(() => {
|
||||
cy.get("input").type(" Updated")
|
||||
cy.contains("Save").click()
|
||||
})
|
||||
|
@ -139,9 +137,9 @@ context("Create a View", () => {
|
|||
|
||||
it("deletes a view", () => {
|
||||
cy.contains(".nav-item", "Test View Updated")
|
||||
.find(".ri-more-line")
|
||||
.find(".actions .icon")
|
||||
.click({ force: true })
|
||||
cy.get("[data-cy=delete-view]").click()
|
||||
cy.contains("Delete").click()
|
||||
cy.contains("Delete View").click()
|
||||
cy.wait(1000)
|
||||
cy.contains("TestView Updated").should("not.be.visible")
|
||||
|
|
|
@ -3,7 +3,9 @@ const path = require("path")
|
|||
|
||||
const tmpdir = path.join(require("os").tmpdir(), ".budibase")
|
||||
|
||||
const WORKER_PORT = "4002"
|
||||
// these run on ports we don't normally use so that they can run alongside the
|
||||
// normal development system
|
||||
const WORKER_PORT = "10002"
|
||||
const MAIN_PORT = cypressConfig.env.PORT
|
||||
process.env.BUDIBASE_API_KEY = "6BE826CB-6B30-4AEC-8777-2E90464633DE"
|
||||
process.env.NODE_ENV = "cypress"
|
||||
|
@ -12,8 +14,8 @@ process.env.PORT = MAIN_PORT
|
|||
process.env.JWT_SECRET = cypressConfig.env.JWT_SECRET
|
||||
process.env.COUCH_URL = `leveldb://${tmpdir}/.data/`
|
||||
process.env.SELF_HOSTED = 1
|
||||
process.env.WORKER_URL = "http://localhost:4002/"
|
||||
process.env.MINIO_URL = "http://localhost:10000/"
|
||||
process.env.WORKER_URL = "http://localhost:10002/"
|
||||
process.env.MINIO_URL = `http://localhost:${MAIN_PORT}/`
|
||||
process.env.MINIO_ACCESS_KEY = "budibase"
|
||||
process.env.MINIO_SECRET_KEY = "budibase"
|
||||
process.env.COUCH_DB_USER = "budibase"
|
||||
|
|
|
@ -6,80 +6,61 @@
|
|||
//
|
||||
|
||||
Cypress.Commands.add("login", () => {
|
||||
cy.getCookie("budibase:auth").then(cookie => {
|
||||
// Already logged in
|
||||
if (cookie) return
|
||||
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
|
||||
// cy.get("button").then(btn => {
|
||||
// const adminUserButton = "Create super admin user"
|
||||
// console.log(btn.first().first())
|
||||
// if (!btn.first().contains(adminUserButton)) {
|
||||
// // create admin user
|
||||
// cy.get("input").first().type("test@test.com")
|
||||
// cy.get('input[type="password"]').first().type("test")
|
||||
// cy.get('input[type="password"]').eq(1).type("test")
|
||||
// cy.contains(adminUserButton).click()
|
||||
// }
|
||||
|
||||
// login
|
||||
cy.get("input").first().type("test@test.com")
|
||||
cy.get('input[type="password"]').type("test")
|
||||
cy.contains("Login").click()
|
||||
// })
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createApp", name => {
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
// wait for init API calls on visit
|
||||
cy.wait(100)
|
||||
cy.contains("Create New Web App").click()
|
||||
cy.get("body")
|
||||
.then($body => {
|
||||
if ($body.find("input[name=apiKey]").length) {
|
||||
// input was found, do something else here
|
||||
cy.get("input[name=apiKey]").type(name).should("have.value", name)
|
||||
cy.contains("Next").click()
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
cy.get(".spectrum-Modal")
|
||||
.within(() => {
|
||||
cy.get("input").eq(0).type(name).should("have.value", name).blur()
|
||||
cy.contains("Next").click()
|
||||
cy.get("input").eq(1).type("test@test.com").blur()
|
||||
cy.get("input").eq(2).type("test").blur()
|
||||
cy.contains("Submit").click()
|
||||
})
|
||||
.then(() => {
|
||||
cy.get("[data-cy=new-table]", {
|
||||
timeout: 20000,
|
||||
}).should("be.visible")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add("deleteApp", name => {
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
cy.get(".apps").then($apps => {
|
||||
cy.wait(1000)
|
||||
if ($apps.find(`[data-cy="app-${name}"]`).length) {
|
||||
cy.get(`[data-cy="app-${name}"]`).contains("Open").click()
|
||||
cy.get("[data-cy=settings-icon]").click()
|
||||
cy.get(".spectrum-Dialog").within(() => {
|
||||
cy.contains("Danger Zone").click()
|
||||
cy.get("input").type("DELETE").blur()
|
||||
cy.contains("Delete Entire App").click()
|
||||
cy.wait(500)
|
||||
cy.url().then(url => {
|
||||
if (url.includes("builder/admin")) {
|
||||
// create admin user
|
||||
cy.get("input").first().type("test@test.com")
|
||||
cy.get('input[type="password"]').first().type("test")
|
||||
cy.get('input[type="password"]').eq(1).type("test")
|
||||
cy.contains("Create super admin user").click()
|
||||
}
|
||||
if (url.includes("builder/auth/login") || url.includes("builder/admin")) {
|
||||
// login
|
||||
cy.contains("Sign in to Budibase").then(() => {
|
||||
cy.get("input").first().type("test@test.com")
|
||||
cy.get('input[type="password"]').type("test")
|
||||
cy.get("button").first().click()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createApp", name => {
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
cy.wait(500)
|
||||
cy.contains(/Create (new )?app/).click()
|
||||
cy.get(".spectrum-Modal")
|
||||
.within(() => {
|
||||
cy.get("input").eq(0).type(name).should("have.value", name).blur()
|
||||
cy.contains("Create app").click()
|
||||
})
|
||||
.then(() => {
|
||||
cy.get("[data-cy=new-table]", {
|
||||
timeout: 20000,
|
||||
}).should("be.visible")
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add("deleteApp", () => {
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
cy.wait(1000)
|
||||
cy.request(`localhost:${Cypress.env("PORT")}/api/applications?status=all`)
|
||||
.its("body")
|
||||
.then(val => {
|
||||
console.log(val)
|
||||
if (val.length > 0) {
|
||||
cy.get(".hoverable > use").click()
|
||||
cy.contains("Delete").click()
|
||||
cy.get(".spectrum-Button--warning").click()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createTestApp", () => {
|
||||
const appName = "Cypress Tests"
|
||||
cy.deleteApp(appName)
|
||||
cy.deleteApp()
|
||||
cy.createApp(appName, "This app is used for Cypress testing.")
|
||||
})
|
||||
|
||||
|
@ -110,8 +91,10 @@ Cypress.Commands.add("addColumn", (tableName, columnName, type) => {
|
|||
|
||||
// Unset table display column
|
||||
cy.contains("display column").click({ force: true })
|
||||
cy.get("select").select(type)
|
||||
cy.contains("Save").click()
|
||||
cy.get(".spectrum-Picker-label").click()
|
||||
cy.contains(type).click()
|
||||
|
||||
cy.contains("Save Column").click()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -125,18 +108,18 @@ Cypress.Commands.add("addRow", values => {
|
|||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createUser", (email, password, role) => {
|
||||
// Create User
|
||||
Cypress.Commands.add("createUser", email => {
|
||||
// quick hacky recorded way to create a user
|
||||
cy.contains("Users").click()
|
||||
cy.contains("Create user").click()
|
||||
cy.get(".spectrum-Modal").within(() => {
|
||||
cy.get("input").first().type(email).blur()
|
||||
cy.get("input").eq(1).type(password).blur()
|
||||
cy.get("select").first().select(role)
|
||||
|
||||
// Save
|
||||
cy.get(".spectrum-ButtonGroup").contains("Create User").click()
|
||||
})
|
||||
cy.get(".spectrum-Button--primary").click()
|
||||
cy.get(".spectrum-Picker-label").click()
|
||||
cy.get(".spectrum-Menu-item:nth-child(2) > .spectrum-Menu-itemLabel").click()
|
||||
cy.get(
|
||||
":nth-child(2) > .spectrum-Form-itemField > .spectrum-Textfield > .spectrum-Textfield-input"
|
||||
)
|
||||
.first()
|
||||
.type(email, { force: true })
|
||||
cy.get(".spectrum-Button--cta").click({ force: true })
|
||||
})
|
||||
|
||||
Cypress.Commands.add("addComponent", (category, component) => {
|
||||
|
@ -165,17 +148,16 @@ Cypress.Commands.add("getComponent", componentId => {
|
|||
})
|
||||
|
||||
Cypress.Commands.add("navigateToFrontend", () => {
|
||||
cy.contains("design").click()
|
||||
cy.wait(1000)
|
||||
cy.contains("Design").click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createScreen", (screenName, route) => {
|
||||
cy.get("[data-cy=new-screen]").click()
|
||||
cy.get("[aria-label=AddCircle]").click()
|
||||
cy.get(".spectrum-Modal").within(() => {
|
||||
cy.get("input").eq(0).type(screenName).blur()
|
||||
if (route) {
|
||||
cy.get("input").eq(1).type(route).blur()
|
||||
}
|
||||
cy.contains("Create Screen").click()
|
||||
cy.get("input").first().type(screenName)
|
||||
cy.get("input").eq(1).type(route)
|
||||
cy.get(".spectrum-Button--cta").click()
|
||||
})
|
||||
cy.get(".nav-items-container").within(() => {
|
||||
cy.contains(route).should("exist")
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import "./cookies"
|
||||
import "./commands"
|
||||
import "./cookies"
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/builder",
|
||||
"version": "0.9.27",
|
||||
"version": "0.9.39",
|
||||
"license": "AGPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -15,9 +15,9 @@
|
|||
"cy:run": "cypress run",
|
||||
"cy:open": "cypress open",
|
||||
"cy:run:ci": "cypress run --record --key f308590b-6070-41af-b970-794a3823d451",
|
||||
"cy:test": "start-server-and-test cy:setup http://localhost:10000/builder cy:run",
|
||||
"cy:ci": "start-server-and-test cy:setup http://localhost:10000/builder cy:run:ci",
|
||||
"cy:debug": "start-server-and-test cy:setup http://localhost:10000/builder cy:open"
|
||||
"cy:test": "start-server-and-test cy:setup http://localhost:10001/builder cy:run",
|
||||
"cy:ci": "start-server-and-test cy:setup http://localhost:10001/builder cy:run:ci",
|
||||
"cy:debug": "start-server-and-test cy:setup http://localhost:10001/builder cy:open"
|
||||
},
|
||||
"jest": {
|
||||
"globals": {
|
||||
|
@ -65,10 +65,10 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^0.9.27",
|
||||
"@budibase/client": "^0.9.27",
|
||||
"@budibase/bbui": "^0.9.39",
|
||||
"@budibase/client": "^0.9.39",
|
||||
"@budibase/colorpicker": "1.1.2",
|
||||
"@budibase/string-templates": "^0.9.27",
|
||||
"@budibase/string-templates": "^0.9.39",
|
||||
"@sentry/browser": "5.19.1",
|
||||
"@spectrum-css/page": "^3.0.1",
|
||||
"@spectrum-css/vars": "^3.0.1",
|
||||
|
|
|
@ -35,6 +35,7 @@ const createScreen = table => {
|
|||
const form = makeMainForm()
|
||||
.instanceName("Form")
|
||||
.customProps({
|
||||
actionType: "Create",
|
||||
theme: "spectrum--lightest",
|
||||
size: "spectrum--medium",
|
||||
dataSource: {
|
||||
|
|
|
@ -110,6 +110,7 @@ const createScreen = table => {
|
|||
const form = makeMainForm()
|
||||
.instanceName("Form")
|
||||
.customProps({
|
||||
actionType: "Update",
|
||||
theme: "spectrum--lightest",
|
||||
size: "spectrum--medium",
|
||||
dataSource: {
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
tables.deleteField(field)
|
||||
notifications.success(`Column ${field.name} deleted.`)
|
||||
confirmDeleteDialog.hide()
|
||||
hide()
|
||||
deletion = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { store, allScreens } from "builderStore"
|
||||
import { store, allScreens, selectedAccessRole } from "builderStore"
|
||||
import { tables } from "stores/backend"
|
||||
import { roles } from "stores/backend"
|
||||
import { Input, Select, ModalContent, Toggle } from "@budibase/bbui"
|
||||
|
@ -14,7 +14,7 @@
|
|||
let templateIndex
|
||||
let draftScreen
|
||||
let createLink = true
|
||||
let roleId = "BASIC"
|
||||
let roleId = $selectedAccessRole || "BASIC"
|
||||
|
||||
$: templates = getTemplates($store, $tables.list)
|
||||
$: route = !route && $allScreens.length === 0 ? "*" : route
|
||||
|
|
|
@ -48,6 +48,8 @@
|
|||
$: integrationInfo = $integrations[datasourceType]
|
||||
$: queryConfig = integrationInfo?.query
|
||||
$: shouldShowQueryConfig = queryConfig && query.queryVerb
|
||||
$: readQuery = query.queryVerb === "read" || query.readable
|
||||
$: queryInvalid = !query.name || (readQuery && data.length === 0)
|
||||
|
||||
function newField() {
|
||||
fields = [...fields, {}]
|
||||
|
@ -150,11 +152,7 @@
|
|||
<div class="viewer-controls">
|
||||
<Heading size="S">Results</Heading>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
cta
|
||||
disabled={data.length === 0 || !query.name}
|
||||
on:click={saveQuery}
|
||||
>
|
||||
<Button cta disabled={queryInvalid} on:click={saveQuery}>
|
||||
Save Query
|
||||
</Button>
|
||||
<Button secondary on:click={previewQuery}>Run Query</Button>
|
||||
|
|
|
@ -6,6 +6,13 @@ export function createPermissionStore() {
|
|||
|
||||
return {
|
||||
subscribe,
|
||||
save: async ({ level, role, resource }) => {
|
||||
const response = await api.post(
|
||||
`/api/permission/${role}/${resource}/${level}`
|
||||
)
|
||||
const json = await response.json()
|
||||
return json
|
||||
},
|
||||
forResource: async resourceId => {
|
||||
const response = await api.get(`/api/permission/${resourceId}`)
|
||||
const json = await response.json()
|
||||
|
|
|
@ -101,6 +101,12 @@ export function createTablesStore() {
|
|||
// Optionally set display column
|
||||
if (primaryDisplay) {
|
||||
state.draft.primaryDisplay = field.name
|
||||
} else if (state.draft.primaryDisplay === originalName) {
|
||||
const fields = Object.keys(state.draft.schema)
|
||||
// pick another display column randomly if unselecting
|
||||
state.draft.primaryDisplay = fields.filter(
|
||||
name => name !== originalName || name !== field
|
||||
)[0]
|
||||
}
|
||||
|
||||
if (indexes) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/cli",
|
||||
"version": "0.9.27",
|
||||
"version": "0.9.39",
|
||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
|
@ -25,7 +25,7 @@
|
|||
"inquirer": "^8.0.0",
|
||||
"lookpath": "^1.1.0",
|
||||
"pkg": "^4.4.9",
|
||||
"posthog-node": "^1.0.7",
|
||||
"posthog-node": "1.0.7",
|
||||
"randomstring": "^1.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -1223,7 +1223,7 @@ pkg@^4.4.9:
|
|||
resolve "^1.15.1"
|
||||
stream-meter "^1.0.4"
|
||||
|
||||
posthog-node@^1.0.7:
|
||||
posthog-node@1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.0.7.tgz#a7a9525eebff23312117e57cff3ddac82afb2262"
|
||||
integrity sha512-KTCwyU+PU1eAQtjy5ZSJ47mrxv2d/mMkxo+vvV5c+YqfE4mBAY1UPEPMv1nElb5Vq0UnxvyQXaUnOn8d8Xr6Eg==
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/client",
|
||||
"version": "0.9.27",
|
||||
"version": "0.9.39",
|
||||
"license": "MPL-2.0",
|
||||
"module": "dist/budibase-client.js",
|
||||
"main": "dist/budibase-client.js",
|
||||
|
@ -19,18 +19,18 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^0.9.27",
|
||||
"@budibase/string-templates": "^0.9.27",
|
||||
"@budibase/string-templates": "^0.9.39",
|
||||
"regexparam": "^1.3.0",
|
||||
"shortid": "^2.2.15",
|
||||
"svelte-spa-router": "^3.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@budibase/standard-components": "^0.9.27",
|
||||
"@budibase/standard-components": "^0.9.39",
|
||||
"@rollup/plugin-commonjs": "^18.0.0",
|
||||
"@rollup/plugin-node-resolve": "^11.2.1",
|
||||
"fs-extra": "^8.1.0",
|
||||
"jsdom": "^16.0.1",
|
||||
"postcss": "^8.2.9",
|
||||
"postcss": "^8.2.10",
|
||||
"rollup": "^2.44.0",
|
||||
"rollup-plugin-json": "^4.0.0",
|
||||
"rollup-plugin-node-builtins": "^2.1.2",
|
||||
|
|
|
@ -3,9 +3,9 @@ import API from "./api"
|
|||
/**
|
||||
* Uploads an attachment to the server.
|
||||
*/
|
||||
export const uploadAttachment = async data => {
|
||||
export const uploadAttachment = async (data, tableId = "") => {
|
||||
return await API.post({
|
||||
url: "/api/attachments/upload",
|
||||
url: `/api/attachments/${tableId}/upload`,
|
||||
body: data,
|
||||
json: false,
|
||||
})
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/server",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "0.9.27",
|
||||
"version": "0.9.39",
|
||||
"description": "Budibase Web Server",
|
||||
"main": "src/electron.js",
|
||||
"repository": {
|
||||
|
@ -55,9 +55,9 @@
|
|||
"author": "Budibase",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@budibase/auth": "^0.9.27",
|
||||
"@budibase/client": "^0.9.27",
|
||||
"@budibase/string-templates": "^0.9.27",
|
||||
"@budibase/auth": "^0.9.39",
|
||||
"@budibase/client": "^0.9.39",
|
||||
"@budibase/string-templates": "^0.9.39",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
"@koa/router": "8.0.0",
|
||||
"@sendgrid/mail": "7.1.1",
|
||||
|
@ -109,7 +109,7 @@
|
|||
"devDependencies": {
|
||||
"@babel/core": "^7.14.3",
|
||||
"@babel/preset-env": "^7.14.4",
|
||||
"@budibase/standard-components": "^0.9.27",
|
||||
"@budibase/standard-components": "^0.9.39",
|
||||
"@jest/test-sequencer": "^24.8.0",
|
||||
"babel-jest": "^27.0.2",
|
||||
"docker-compose": "^0.23.6",
|
||||
|
|
|
@ -5,6 +5,10 @@ const { join } = require("path")
|
|||
const CouchDB = require("../src/db")
|
||||
// load environment
|
||||
const env = require("../src/environment")
|
||||
const {
|
||||
USER_METDATA_PREFIX,
|
||||
LINK_USER_METADATA_PREFIX,
|
||||
} = require("../src/db/utils")
|
||||
|
||||
// Script to export a chosen budibase app into a package
|
||||
// Usage: ./scripts/exportAppTemplate.js export --name=Funky --appId=appId
|
||||
|
@ -44,7 +48,13 @@ yargs
|
|||
// perform couch dump
|
||||
|
||||
const instanceDb = new CouchDB(appId)
|
||||
await instanceDb.dump(writeStream, {})
|
||||
await instanceDb.dump(writeStream, {
|
||||
filter: doc =>
|
||||
!(
|
||||
doc._id.includes(USER_METDATA_PREFIX) ||
|
||||
doc.includes(LINK_USER_METADATA_PREFIX)
|
||||
),
|
||||
})
|
||||
console.log(`Template ${name} exported to ${exportPath}`)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -99,18 +99,11 @@ async function createInstance(template) {
|
|||
// replicate the template data to the instance DB
|
||||
// this is currently very hard to test, downloading and importing template files
|
||||
/* istanbul ignore next */
|
||||
let _rev
|
||||
if (template && template.useTemplate === "true") {
|
||||
const { ok } = await db.load(await getTemplateStream(template))
|
||||
if (!ok) {
|
||||
throw "Error loading database dump from template."
|
||||
}
|
||||
try {
|
||||
const response = await db.get(DocumentTypes.APP_METADATA)
|
||||
_rev = response._rev
|
||||
} catch (err) {
|
||||
_rev = null
|
||||
}
|
||||
} else {
|
||||
// create the users table
|
||||
await db.put(USERS_TABLE_SCHEMA)
|
||||
|
@ -121,13 +114,13 @@ async function createInstance(template) {
|
|||
await createRoutingView(appId)
|
||||
await createAllSearchIndex(appId)
|
||||
|
||||
return { _id: appId, _rev }
|
||||
return { _id: appId }
|
||||
}
|
||||
|
||||
exports.fetch = async function (ctx) {
|
||||
const dev = ctx.query && ctx.query.status === AppStatus.DEV
|
||||
const all = ctx.query && ctx.query.status === AppStatus.ALL
|
||||
const apps = await getAllApps({ dev, all })
|
||||
const apps = await getAllApps({ CouchDB, dev, all })
|
||||
|
||||
// get the locks for all the dev apps
|
||||
if (dev || all) {
|
||||
|
@ -188,11 +181,21 @@ exports.create = async function (ctx) {
|
|||
instanceConfig.file = ctx.request.files.templateFile
|
||||
}
|
||||
const instance = await createInstance(instanceConfig)
|
||||
const appId = instance._id
|
||||
|
||||
const url = await getAppUrlIfNotInUse(ctx)
|
||||
const appId = instance._id
|
||||
const db = new CouchDB(appId)
|
||||
let _rev
|
||||
try {
|
||||
// if template there will be an existing doc
|
||||
const existing = await db.get(DocumentTypes.APP_METADATA)
|
||||
_rev = existing._rev
|
||||
} catch (err) {
|
||||
// nothing to do
|
||||
}
|
||||
const newApplication = {
|
||||
_id: DocumentTypes.APP_METADATA,
|
||||
_rev,
|
||||
appId: instance._id,
|
||||
type: "app",
|
||||
version: packageJson.version,
|
||||
|
@ -203,15 +206,8 @@ exports.create = async function (ctx) {
|
|||
instance: instance,
|
||||
updatedAt: new Date().toISOString(),
|
||||
createdAt: new Date().toISOString(),
|
||||
deployment: {
|
||||
type: "cloud",
|
||||
},
|
||||
}
|
||||
if (instance._rev) {
|
||||
newApplication._rev = instance._rev
|
||||
}
|
||||
const instanceDb = new CouchDB(appId)
|
||||
await instanceDb.put(newApplication)
|
||||
await db.put(newApplication, { force: true })
|
||||
|
||||
await createEmptyAppPackage(ctx, newApplication)
|
||||
/* istanbul ignore next */
|
||||
|
|
|
@ -60,7 +60,7 @@ exports.save = async function (ctx) {
|
|||
ctx.message = `Query ${query.name} saved successfully.`
|
||||
}
|
||||
|
||||
async function enrichQueryFields(fields, parameters) {
|
||||
async function enrichQueryFields(fields, parameters = {}) {
|
||||
const enrichedQuery = {}
|
||||
|
||||
// enrich the fields with dynamic parameters
|
||||
|
|
|
@ -75,8 +75,6 @@ exports.save = async function (ctx) {
|
|||
/* istanbul ignore next */
|
||||
if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) {
|
||||
ctx.throw(400, "Cannot rename a linked column.")
|
||||
} else if (_rename && tableToSave.primaryDisplay === _rename.old) {
|
||||
ctx.throw(400, "Cannot rename the display column.")
|
||||
}
|
||||
|
||||
tableToSave = await tableSaveFunctions.mid(tableToSave)
|
||||
|
|
|
@ -4,10 +4,8 @@ const {
|
|||
getUserMetadataParams,
|
||||
} = require("../../db/utils")
|
||||
const { InternalTables } = require("../../db/utils")
|
||||
const {
|
||||
getGlobalUsers,
|
||||
addAppRoleToUser,
|
||||
} = require("../../utilities/workerRequests")
|
||||
const { addAppRoleToUser } = require("../../utilities/workerRequests")
|
||||
const { getGlobalUsers } = require("../../utilities/global")
|
||||
const { getFullUser } = require("../../utilities/users")
|
||||
|
||||
function removeGlobalProps(user) {
|
||||
|
@ -20,7 +18,7 @@ function removeGlobalProps(user) {
|
|||
|
||||
exports.fetchMetadata = async function (ctx) {
|
||||
const database = new CouchDB(ctx.appId)
|
||||
const global = await getGlobalUsers(ctx, ctx.appId)
|
||||
const global = await getGlobalUsers(ctx.appId)
|
||||
const metadata = (
|
||||
await database.allDocs(
|
||||
getUserMetadataParams(null, {
|
||||
|
|
|
@ -7,8 +7,8 @@ const {
|
|||
PermissionTypes,
|
||||
PermissionLevels,
|
||||
} = require("@budibase/auth/permissions")
|
||||
const usage = require("../../middleware/usageQuota")
|
||||
const env = require("../../environment")
|
||||
const { paramResource } = require("../../middleware/resourceId")
|
||||
|
||||
const router = Router()
|
||||
|
||||
|
@ -39,9 +39,9 @@ router
|
|||
.get("/builder/:file*", controller.serveBuilder)
|
||||
.post("/api/attachments/process", authorized(BUILDER), controller.uploadFile)
|
||||
.post(
|
||||
"/api/attachments/upload",
|
||||
"/api/attachments/:tableId/upload",
|
||||
paramResource("tableId"),
|
||||
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||
usage,
|
||||
controller.uploadFile
|
||||
)
|
||||
.get("/componentlibrary", controller.serveComponentLibrary)
|
||||
|
|
|
@ -37,7 +37,37 @@ describe("run misc tests", () => {
|
|||
|
||||
describe("test table utilities", () => {
|
||||
it("should be able to import a CSV", async () => {
|
||||
const table = await config.createTable()
|
||||
const table = await config.createTable({
|
||||
name: "table",
|
||||
type: "table",
|
||||
key: "name",
|
||||
schema: {
|
||||
a: {
|
||||
type: "string",
|
||||
constraints: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
b: {
|
||||
type: "string",
|
||||
constraints: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
c: {
|
||||
type: "string",
|
||||
constraints: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
d: {
|
||||
type: "string",
|
||||
constraints: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
const dataImport = {
|
||||
csvString: "a,b,c,d\n1,2,3,4"
|
||||
}
|
||||
|
|
|
@ -26,11 +26,6 @@ describe("/routing", () => {
|
|||
|
||||
describe("fetch", () => {
|
||||
it("returns the correct routing for basic user", async () => {
|
||||
workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => {
|
||||
return {
|
||||
roleId: BUILTIN_ROLE_IDS.BASIC,
|
||||
}
|
||||
})
|
||||
const res = await request
|
||||
.get(`/api/routing/client`)
|
||||
.set(await config.roleHeaders({
|
||||
|
@ -52,13 +47,6 @@ describe("/routing", () => {
|
|||
})
|
||||
|
||||
it("returns the correct routing for power user", async () => {
|
||||
workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => {
|
||||
return {
|
||||
roles: {
|
||||
[appId]: BUILTIN_ROLE_IDS.POWER,
|
||||
}
|
||||
}
|
||||
})
|
||||
const res = await request
|
||||
.get(`/api/routing/client`)
|
||||
.set(await config.roleHeaders({
|
||||
|
|
|
@ -125,6 +125,7 @@ describe("/rows", () => {
|
|||
numberNull: number,
|
||||
numberUndefined: number,
|
||||
numberString: number,
|
||||
numberNumber: number,
|
||||
datetimeEmptyString: datetime,
|
||||
datetimeNull: datetime,
|
||||
datetimeUndefined: datetime,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
||||
const { checkPermissionsEndpoint } = require("./utilities/TestFunctions")
|
||||
const setup = require("./utilities")
|
||||
const workerRequests = require("../../../utilities/workerRequests")
|
||||
|
||||
jest.mock("../../../utilities/workerRequests", () => ({
|
||||
getGlobalUsers: jest.fn(() => {
|
||||
|
@ -25,30 +24,18 @@ describe("/users", () => {
|
|||
})
|
||||
|
||||
describe("fetch", () => {
|
||||
beforeEach(() => {
|
||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ([
|
||||
{
|
||||
_id: "us_uuid1",
|
||||
},
|
||||
{
|
||||
_id: "us_uuid2",
|
||||
}
|
||||
]
|
||||
))
|
||||
})
|
||||
|
||||
it("returns a list of users from an instance db", async () => {
|
||||
await config.createUser("brenda@brenda.com", "brendas_password")
|
||||
await config.createUser("pam@pam.com", "pam_password")
|
||||
await config.createUser("uuidx")
|
||||
await config.createUser("uuidy")
|
||||
const res = await request
|
||||
.get(`/api/users/metadata`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.length).toBe(2)
|
||||
expect(res.body.find(u => u._id === `ro_ta_users_us_uuid1`)).toBeDefined()
|
||||
expect(res.body.find(u => u._id === `ro_ta_users_us_uuid2`)).toBeDefined()
|
||||
expect(res.body.length).toBe(3)
|
||||
expect(res.body.find(u => u._id === `ro_ta_users_us_uuidx`)).toBeDefined()
|
||||
expect(res.body.find(u => u._id === `ro_ta_users_us_uuidy`)).toBeDefined()
|
||||
})
|
||||
|
||||
it("should apply authorization to endpoint", async () => {
|
||||
|
@ -65,9 +52,6 @@ describe("/users", () => {
|
|||
})
|
||||
|
||||
describe("update", () => {
|
||||
beforeEach(() => {
|
||||
})
|
||||
|
||||
it("should be able to update the user", async () => {
|
||||
const user = await config.createUser()
|
||||
user.roleId = BUILTIN_ROLE_IDS.BASIC
|
||||
|
@ -94,14 +78,6 @@ describe("/users", () => {
|
|||
})
|
||||
|
||||
describe("find", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks()
|
||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ({
|
||||
_id: "us_uuid1",
|
||||
roleId: BUILTIN_ROLE_IDS.POWER,
|
||||
}))
|
||||
})
|
||||
|
||||
it("should be able to find the user", async () => {
|
||||
const user = await config.createUser()
|
||||
const res = await request
|
||||
|
@ -110,7 +86,7 @@ describe("/users", () => {
|
|||
.expect(200)
|
||||
.expect("Content-Type", /json/)
|
||||
expect(res.body._id).toEqual(user._id)
|
||||
expect(res.body.roleId).toEqual(BUILTIN_ROLE_IDS.POWER)
|
||||
expect(res.body.roleId).toEqual(BUILTIN_ROLE_IDS.ADMIN)
|
||||
expect(res.body.tableId).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
const setup = require("./utilities")
|
||||
|
||||
function priceTable() {
|
||||
return {
|
||||
name: "table",
|
||||
type: "table",
|
||||
key: "name",
|
||||
schema: {
|
||||
Price: {
|
||||
type: "number",
|
||||
constraints: {},
|
||||
},
|
||||
Category: {
|
||||
type: "string",
|
||||
constraints: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
describe("/views", () => {
|
||||
let request = setup.getRequest()
|
||||
let config = setup.getConfig()
|
||||
|
@ -13,7 +33,7 @@ describe("/views", () => {
|
|||
|
||||
describe("create", () => {
|
||||
beforeEach(async () => {
|
||||
table = await config.createTable()
|
||||
table = await config.createTable(priceTable())
|
||||
})
|
||||
|
||||
it("returns a success message when the view is successfully created", async () => {
|
||||
|
@ -83,7 +103,7 @@ describe("/views", () => {
|
|||
|
||||
describe("fetch", () => {
|
||||
beforeEach(async () => {
|
||||
table = await config.createTable()
|
||||
table = await config.createTable(priceTable())
|
||||
})
|
||||
|
||||
it("returns only custom views", async () => {
|
||||
|
@ -105,7 +125,7 @@ describe("/views", () => {
|
|||
|
||||
describe("query", () => {
|
||||
beforeEach(async () => {
|
||||
table = await config.createTable()
|
||||
table = await config.createTable(priceTable())
|
||||
})
|
||||
|
||||
it("returns data for the created view", async () => {
|
||||
|
@ -172,7 +192,7 @@ describe("/views", () => {
|
|||
|
||||
describe("destroy", () => {
|
||||
it("should be able to delete a view", async () => {
|
||||
const table = await config.createTable()
|
||||
const table = await config.createTable(priceTable())
|
||||
const view = await config.createView()
|
||||
const res = await request
|
||||
.delete(`/api/views/${view.name}`)
|
||||
|
@ -186,7 +206,7 @@ describe("/views", () => {
|
|||
|
||||
describe("exportView", () => {
|
||||
it("should be able to delete a view", async () => {
|
||||
await config.createTable()
|
||||
await config.createTable(priceTable())
|
||||
await config.createRow()
|
||||
const view = await config.createView()
|
||||
let res = await request
|
||||
|
|
|
@ -11,7 +11,9 @@ const {
|
|||
const { flatten } = require("lodash")
|
||||
const CouchDB = require("../../db")
|
||||
const { FieldTypes } = require("../../constants")
|
||||
const { getMultiIDParams } = require("../../db/utils")
|
||||
const { getMultiIDParams, USER_METDATA_PREFIX } = require("../../db/utils")
|
||||
const { partition } = require("lodash")
|
||||
const { getGlobalUsers } = require("../../utilities/global")
|
||||
|
||||
/**
|
||||
* This functionality makes sure that when rows with links are created, updated or deleted they are processed
|
||||
|
@ -57,6 +59,31 @@ async function getLinksForRows(appId, rows) {
|
|||
)
|
||||
}
|
||||
|
||||
async function getFullLinkedDocs(appId, links) {
|
||||
// create DBs
|
||||
const db = new CouchDB(appId)
|
||||
const linkedRowIds = links.map(link => link.id)
|
||||
let linked = (await db.allDocs(getMultiIDParams(linkedRowIds))).rows.map(
|
||||
row => row.doc
|
||||
)
|
||||
// need to handle users as specific cases
|
||||
let [users, other] = partition(linked, linkRow =>
|
||||
linkRow._id.startsWith(USER_METDATA_PREFIX)
|
||||
)
|
||||
const globalUsers = await getGlobalUsers(appId, users)
|
||||
users = users.map(user => {
|
||||
const globalUser = globalUsers.find(
|
||||
globalUser => globalUser && user._id.includes(globalUser._id)
|
||||
)
|
||||
return {
|
||||
...globalUser,
|
||||
// doing user second overwrites the id and rev (always metadata)
|
||||
...user,
|
||||
}
|
||||
})
|
||||
return [...other, ...users]
|
||||
}
|
||||
|
||||
/**
|
||||
* Update link documents for a row or table - this is to be called by the API controller when a change is occurring.
|
||||
* @param {string} eventType states what type of change which is occurring, means this can be expanded upon in the
|
||||
|
@ -154,14 +181,13 @@ exports.attachFullLinkedDocs = async (appId, table, rows) => {
|
|||
if (linkedTableIds.length === 0) {
|
||||
return rows
|
||||
}
|
||||
// create DBs
|
||||
const db = new CouchDB(appId)
|
||||
// get all the links
|
||||
const links = (await getLinksForRows(appId, rows)).filter(link =>
|
||||
rows.some(row => row._id === link.thisId)
|
||||
)
|
||||
const linkedRowIds = links.map(link => link.id)
|
||||
const linked = (await db.allDocs(getMultiIDParams(linkedRowIds))).rows.map(
|
||||
row => row.doc
|
||||
)
|
||||
let linked = await getFullLinkedDocs(appId, links)
|
||||
const linkedTables = []
|
||||
for (let row of rows) {
|
||||
for (let link of links.filter(link => link.thisId === row._id)) {
|
||||
|
|
|
@ -6,17 +6,11 @@ const {
|
|||
APP_DEV_PREFIX,
|
||||
APP_PREFIX,
|
||||
SEPARATOR,
|
||||
StaticDatabases,
|
||||
} = require("@budibase/auth/db")
|
||||
|
||||
const UNICODE_MAX = "\ufff0"
|
||||
|
||||
const StaticDatabases = {
|
||||
BUILDER: {
|
||||
name: "builder-db",
|
||||
baseDoc: "builder-doc",
|
||||
},
|
||||
}
|
||||
|
||||
const AppStatus = {
|
||||
DEV: "development",
|
||||
ALL: "all",
|
||||
|
@ -54,9 +48,18 @@ const SearchIndexes = {
|
|||
ROWS: "rows",
|
||||
}
|
||||
|
||||
exports.StaticDatabases = {
|
||||
BUILDER: {
|
||||
name: "builder-db",
|
||||
baseDoc: "builder-doc",
|
||||
},
|
||||
...StaticDatabases,
|
||||
}
|
||||
|
||||
exports.APP_PREFIX = APP_PREFIX
|
||||
exports.APP_DEV_PREFIX = APP_DEV_PREFIX
|
||||
exports.StaticDatabases = StaticDatabases
|
||||
exports.USER_METDATA_PREFIX = `${DocumentTypes.ROW}${SEPARATOR}${InternalTables.USER_METADATA}${SEPARATOR}`
|
||||
exports.LINK_USER_METADATA_PREFIX = `${DocumentTypes.LINK}${SEPARATOR}${InternalTables.USER_METADATA}${SEPARATOR}`
|
||||
exports.ViewNames = ViewNames
|
||||
exports.InternalTables = InternalTables
|
||||
exports.DocumentTypes = DocumentTypes
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
const { getAppId, setCookie, getCookie } = require("@budibase/auth").utils
|
||||
const {
|
||||
getAppId,
|
||||
setCookie,
|
||||
getCookie,
|
||||
clearCookie,
|
||||
} = require("@budibase/auth").utils
|
||||
const { Cookies } = require("@budibase/auth").constants
|
||||
const { getRole } = require("@budibase/auth/roles")
|
||||
const { getGlobalSelf } = require("../utilities/workerRequests")
|
||||
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
||||
const { generateUserMetadataID } = require("../db/utils")
|
||||
const { dbExists } = require("@budibase/auth/db")
|
||||
const CouchDB = require("../db")
|
||||
|
||||
module.exports = async (ctx, next) => {
|
||||
// try to get the appID from the request
|
||||
|
@ -13,6 +20,15 @@ module.exports = async (ctx, next) => {
|
|||
if (!appCookie && !requestAppId) {
|
||||
return next()
|
||||
}
|
||||
// check the app exists referenced in cookie
|
||||
if (appCookie) {
|
||||
const appId = appCookie.appId
|
||||
const exists = await dbExists(CouchDB, appId)
|
||||
if (!exists) {
|
||||
clearCookie(ctx, Cookies.CurrentApp)
|
||||
return next()
|
||||
}
|
||||
}
|
||||
|
||||
let updateCookie = false,
|
||||
appId,
|
||||
|
|
|
@ -306,8 +306,8 @@ class TestConfiguration {
|
|||
return await this._req(config, null, controllers.layout.save)
|
||||
}
|
||||
|
||||
async createUser() {
|
||||
const globalId = `us_${Math.random()}`
|
||||
async createUser(id = null) {
|
||||
const globalId = !id ? `us_${Math.random()}` : `us_${id}`
|
||||
const resp = await this.globalUser(globalId)
|
||||
return {
|
||||
...resp,
|
||||
|
|
|
@ -46,7 +46,6 @@ exports.basicRow = tableId => {
|
|||
return {
|
||||
name: "Test Contact",
|
||||
description: "original description",
|
||||
status: "new",
|
||||
tableId: tableId,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@ const download = require("download")
|
|||
const env = require("../../environment")
|
||||
const { homedir } = require("os")
|
||||
const fetch = require("node-fetch")
|
||||
const {
|
||||
USER_METDATA_PREFIX,
|
||||
LINK_USER_METADATA_PREFIX,
|
||||
} = require("../../db/utils")
|
||||
|
||||
const DEFAULT_AUTOMATION_BUCKET =
|
||||
"https://prod-budi-automations.s3-eu-west-1.amazonaws.com"
|
||||
|
@ -117,7 +121,14 @@ exports.performBackup = async (appId, backupName) => {
|
|||
const writeStream = fs.createWriteStream(path)
|
||||
// perform couch dump
|
||||
const instanceDb = new CouchDB(appId)
|
||||
await instanceDb.dump(writeStream, {})
|
||||
await instanceDb.dump(writeStream, {
|
||||
// filter out anything that has a user metadata structure in its ID
|
||||
filter: doc =>
|
||||
!(
|
||||
doc._id.includes(USER_METDATA_PREFIX) ||
|
||||
doc.includes(LINK_USER_METADATA_PREFIX)
|
||||
),
|
||||
})
|
||||
// write the file to the object store
|
||||
await streamUpload(
|
||||
ObjectStoreBuckets.BACKUPS,
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
const CouchDB = require("../db")
|
||||
const {
|
||||
getMultiIDParams,
|
||||
getGlobalIDFromUserMetadataID,
|
||||
StaticDatabases,
|
||||
} = require("../db/utils")
|
||||
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
||||
const { getDeployedAppID } = require("@budibase/auth/db")
|
||||
const { getGlobalUserParams } = require("@budibase/auth/db")
|
||||
|
||||
exports.updateAppRole = (appId, user) => {
|
||||
if (!user.roles) {
|
||||
return user
|
||||
}
|
||||
if (user.builder && user.builder.global) {
|
||||
user.roleId = BUILTIN_ROLE_IDS.ADMIN
|
||||
} else {
|
||||
// always use the deployed app
|
||||
user.roleId = user.roles[getDeployedAppID(appId)]
|
||||
if (!user.roleId) {
|
||||
user.roleId = BUILTIN_ROLE_IDS.PUBLIC
|
||||
}
|
||||
}
|
||||
delete user.roles
|
||||
return user
|
||||
}
|
||||
|
||||
exports.getGlobalUser = async (appId, userId) => {
|
||||
const db = CouchDB(StaticDatabases.GLOBAL.name)
|
||||
let user = await db.get(getGlobalIDFromUserMetadataID(userId))
|
||||
if (user) {
|
||||
delete user.password
|
||||
}
|
||||
return exports.updateAppRole(appId, user)
|
||||
}
|
||||
|
||||
exports.getGlobalUsers = async (appId = null, users = null) => {
|
||||
const db = CouchDB(StaticDatabases.GLOBAL.name)
|
||||
let globalUsers
|
||||
if (users) {
|
||||
const globalIds = users.map(user => getGlobalIDFromUserMetadataID(user._id))
|
||||
globalUsers = (await db.allDocs(getMultiIDParams(globalIds))).rows.map(
|
||||
row => row.doc
|
||||
)
|
||||
} else {
|
||||
globalUsers = (
|
||||
await db.allDocs(
|
||||
getGlobalUserParams(null, {
|
||||
include_docs: true,
|
||||
})
|
||||
)
|
||||
).rows.map(row => row.doc)
|
||||
}
|
||||
globalUsers = globalUsers
|
||||
.filter(user => user != null)
|
||||
.map(user => {
|
||||
delete user.password
|
||||
return user
|
||||
})
|
||||
if (!appId) {
|
||||
return globalUsers
|
||||
}
|
||||
return globalUsers.map(user => exports.updateAppRole(appId, user))
|
||||
}
|
|
@ -1,13 +1,9 @@
|
|||
const CouchDB = require("../db")
|
||||
const { getGlobalIDFromUserMetadataID, InternalTables } = require("../db/utils")
|
||||
const { getGlobalUsers } = require("../utilities/workerRequests")
|
||||
const { InternalTables } = require("../db/utils")
|
||||
const { getGlobalUser } = require("../utilities/global")
|
||||
|
||||
exports.getFullUser = async (ctx, userId) => {
|
||||
const global = await getGlobalUsers(
|
||||
ctx,
|
||||
ctx.appId,
|
||||
getGlobalIDFromUserMetadataID(userId)
|
||||
)
|
||||
const global = await getGlobalUser(ctx.appId, userId)
|
||||
let metadata
|
||||
try {
|
||||
// this will throw an error if the db doesn't exist, or there is no appId
|
||||
|
|
|
@ -1,26 +1,8 @@
|
|||
const fetch = require("node-fetch")
|
||||
const env = require("../environment")
|
||||
const { checkSlashesInUrl } = require("./index")
|
||||
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
||||
const { getDeployedAppID } = require("@budibase/auth/db")
|
||||
const { getGlobalIDFromUserMetadataID } = require("../db/utils")
|
||||
|
||||
function getAppRole(appId, user) {
|
||||
if (!user.roles) {
|
||||
return user
|
||||
}
|
||||
if (user.builder && user.builder.global) {
|
||||
user.roleId = BUILTIN_ROLE_IDS.ADMIN
|
||||
} else {
|
||||
// always use the deployed app
|
||||
user.roleId = user.roles[getDeployedAppID(appId)]
|
||||
if (!user.roleId) {
|
||||
user.roleId = BUILTIN_ROLE_IDS.PUBLIC
|
||||
}
|
||||
}
|
||||
delete user.roles
|
||||
return user
|
||||
}
|
||||
const { updateAppRole, getGlobalUser } = require("./global")
|
||||
|
||||
function request(ctx, request, noApiKey) {
|
||||
if (!request.headers) {
|
||||
|
@ -90,27 +72,6 @@ exports.getDeployedApps = async ctx => {
|
|||
}
|
||||
}
|
||||
|
||||
exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => {
|
||||
const endpoint = globalId
|
||||
? `/api/admin/users/${globalId}`
|
||||
: `/api/admin/users`
|
||||
const reqCfg = { method: "GET" }
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(env.WORKER_URL + endpoint),
|
||||
request(ctx, reqCfg)
|
||||
)
|
||||
let users = await response.json()
|
||||
if (!appId) {
|
||||
return users
|
||||
}
|
||||
if (Array.isArray(users)) {
|
||||
users = users.map(user => getAppRole(appId, user))
|
||||
} else {
|
||||
users = getAppRole(appId, users)
|
||||
}
|
||||
return users
|
||||
}
|
||||
|
||||
exports.getGlobalSelf = async (ctx, appId = null) => {
|
||||
const endpoint = `/api/admin/users/self`
|
||||
const response = await fetch(
|
||||
|
@ -123,7 +84,7 @@ exports.getGlobalSelf = async (ctx, appId = null) => {
|
|||
}
|
||||
let json = await response.json()
|
||||
if (appId) {
|
||||
json = getAppRole(appId, json)
|
||||
json = updateAppRole(appId, json)
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
@ -136,8 +97,7 @@ exports.addAppRoleToUser = async (ctx, appId, roleId, userId = null) => {
|
|||
user = await exports.getGlobalSelf(ctx)
|
||||
endpoint = `/api/admin/users/self`
|
||||
} else {
|
||||
userId = getGlobalIDFromUserMetadataID(userId)
|
||||
user = await exports.getGlobalUsers(ctx, appId, userId)
|
||||
user = await getGlobalUser(appId, userId)
|
||||
body._id = userId
|
||||
endpoint = `/api/admin/users`
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1121,6 +1121,13 @@
|
|||
"ValidateForm"
|
||||
],
|
||||
"settings": [
|
||||
{
|
||||
"type": "select",
|
||||
"label": "Type",
|
||||
"key": "actionType",
|
||||
"options": ["Create", "Update"],
|
||||
"defaultValue": "Create"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"label": "Schema",
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
"keywords": [
|
||||
"svelte"
|
||||
],
|
||||
"version": "0.9.27",
|
||||
"version": "0.9.39",
|
||||
"license": "MIT",
|
||||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^0.9.27",
|
||||
"@budibase/bbui": "^0.9.39",
|
||||
"@spectrum-css/page": "^3.0.1",
|
||||
"@spectrum-css/vars": "^3.0.1",
|
||||
"apexcharts": "^3.22.1",
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
|
||||
let fieldState
|
||||
let fieldApi
|
||||
let fieldSchema
|
||||
|
||||
const { API, notifications } = getContext("sdk")
|
||||
const formContext = getContext("form")
|
||||
const BYTES_IN_MB = 1000000
|
||||
|
||||
export let files = []
|
||||
|
@ -28,7 +30,7 @@
|
|||
for (let i = 0; i < fileList.length; i++) {
|
||||
data.append("file", fileList[i])
|
||||
}
|
||||
return await API.uploadAttachment(data)
|
||||
return await API.uploadAttachment(data, formContext?.dataSource?.tableId)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -45,7 +47,9 @@
|
|||
<CoreDropzone
|
||||
value={$fieldState.value}
|
||||
disabled={$fieldState.disabled}
|
||||
on:change={e => fieldApi.setValue(e.detail)}
|
||||
on:change={e => {
|
||||
fieldApi.setValue(e.detail)
|
||||
}}
|
||||
{processFiles}
|
||||
{handleFileTooLarge}
|
||||
/>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
export let theme
|
||||
export let size
|
||||
export let disabled = false
|
||||
export let actionType = "Create"
|
||||
|
||||
const component = getContext("component")
|
||||
const context = getContext("context")
|
||||
|
@ -19,15 +20,29 @@
|
|||
let fieldMap = {}
|
||||
|
||||
// Returns the closes data context which isn't a built in context
|
||||
const getInitialValues = context => {
|
||||
const getInitialValues = (type, dataSource, context) => {
|
||||
// Only inherit values for update forms
|
||||
if (type !== "Update") {
|
||||
return {}
|
||||
}
|
||||
// Only inherit values for forms targetting internal tables
|
||||
if (!dataSource?.tableId) {
|
||||
return {}
|
||||
}
|
||||
// Don't inherit values representing built in contexts
|
||||
if (["user", "url"].includes(context.closestComponentId)) {
|
||||
return {}
|
||||
}
|
||||
return context[`${context.closestComponentId}`] || {}
|
||||
// Only inherit values if the table ID matches
|
||||
const closestContext = context[`${context.closestComponentId}`] || {}
|
||||
if (dataSource.tableId !== closestContext?.tableId) {
|
||||
return {}
|
||||
}
|
||||
return closestContext
|
||||
}
|
||||
|
||||
// Use the closest data context as the initial form values
|
||||
const initialValues = getInitialValues($context)
|
||||
const initialValues = getInitialValues(actionType, dataSource, $context)
|
||||
|
||||
// Form state contains observable data about the form
|
||||
const formState = writable({ values: initialValues, errors: {}, valid: true })
|
||||
|
@ -42,22 +57,11 @@
|
|||
// Auto columns are always disabled
|
||||
const isAutoColumn = !!schema?.[field]?.autocolumn
|
||||
|
||||
if (fieldMap[field] != null) {
|
||||
// Update disabled property just so that toggling the disabled field
|
||||
// state in the builder makes updates in real time.
|
||||
// We only need this because of optimisations which prevent fully
|
||||
// remounting when settings change.
|
||||
fieldMap[field].fieldState.update(state => {
|
||||
state.disabled = disabled || fieldDisabled || isAutoColumn
|
||||
return state
|
||||
})
|
||||
return fieldMap[field]
|
||||
}
|
||||
|
||||
// Create validation function based on field schema
|
||||
const constraints = schema?.[field]?.constraints
|
||||
const validate = createValidatorFromConstraints(constraints, field, table)
|
||||
|
||||
// Construct field object
|
||||
fieldMap[field] = {
|
||||
fieldState: makeFieldState(
|
||||
field,
|
||||
|
@ -67,6 +71,17 @@
|
|||
fieldApi: makeFieldApi(field, defaultValue, validate),
|
||||
fieldSchema: schema?.[field] ?? {},
|
||||
}
|
||||
|
||||
// Set initial value
|
||||
const initialValue = get(fieldMap[field].fieldState).value
|
||||
formState.update(state => ({
|
||||
...state,
|
||||
values: {
|
||||
...state.values,
|
||||
[field]: initialValue,
|
||||
},
|
||||
}))
|
||||
|
||||
return fieldMap[field]
|
||||
},
|
||||
validate: () => {
|
||||
|
@ -80,7 +95,7 @@
|
|||
}
|
||||
|
||||
// Provide both form API and state to children
|
||||
setContext("form", { formApi, formState })
|
||||
setContext("form", { formApi, formState, dataSource })
|
||||
|
||||
// Action context to pass to children
|
||||
$: actions = [{ type: ActionTypes.ValidateForm, callback: formApi.validate }]
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@adobe/spectrum-css-workflow-icons/-/spectrum-css-workflow-icons-1.2.1.tgz#7e2cb3fcfb5c8b12d7275afafbb6ec44913551b4"
|
||||
integrity sha512-uVgekyBXnOVkxp+CUssjN/gefARtudZC8duEn1vm0lBQFwGRZFlDEzU1QC+aIRWCrD1Z8OgRpmBYlSZ7QS003w==
|
||||
|
||||
"@budibase/bbui@^0.9.27":
|
||||
version "0.9.30"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.30.tgz#ec16ba15c36d86bfb0131e7a33bc6beeb127a1d7"
|
||||
integrity sha512-Bleqck7C3UPYQk9j0VF8AUl9EBHNOuQVM8uv7S9+IVdL6NH2SbdR1V9sOXiX6D+qZwqNwbXM+MZ3aXRMOPThkQ==
|
||||
"@budibase/bbui@^0.9.39":
|
||||
version "0.9.39"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.39.tgz#3bccaa86ed7ae02f67661dadc57becd3db5ca9b6"
|
||||
integrity sha512-5cPMFOGBOpvu3fMWLGBvgVpw+O8gvUh2g6xgWTZi2bCAgtXej+ryL0s7Xs4/mmoS2Y4z5ggc+wE4mOtCM9eFUQ==
|
||||
dependencies:
|
||||
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
|
||||
"@spectrum-css/actionbutton" "^1.0.1"
|
||||
|
@ -333,9 +333,9 @@ debug@^4.3.2:
|
|||
ms "2.1.2"
|
||||
|
||||
esbuild@^0.12.5:
|
||||
version "0.12.6"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.6.tgz#85bc755c7cf3005d4f34b4f10f98049ce0ee67ce"
|
||||
integrity sha512-RDvVLvAjsq/kIZJoneMiUOH7EE7t2QaW7T3Q7EdQij14+bZbDq5sndb0tTanmHIFSqZVMBMMyqzVHkS3dJobeA==
|
||||
version "0.12.8"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.8.tgz#ac90da77cb3bfbf49ab815200bcef7ffe1a3348f"
|
||||
integrity sha512-sx/LwlP/SWTGsd9G4RlOPrXnIihAJ2xwBUmzoqe2nWwbXORMQWtAGNJNYLBJJqa3e9PWvVzxdrtyFZJcr7D87g==
|
||||
|
||||
estree-walker@^2.0.1:
|
||||
version "2.0.2"
|
||||
|
@ -401,7 +401,7 @@ picomatch@^2.2.2:
|
|||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
|
||||
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
|
||||
|
||||
postcss@^8.2.10:
|
||||
postcss@^8.3.0:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.0.tgz#b1a713f6172ca427e3f05ef1303de8b65683325f"
|
||||
integrity sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ==
|
||||
|
@ -542,12 +542,12 @@ svg.select.js@^3.0.1:
|
|||
svg.js "^2.6.5"
|
||||
|
||||
vite@^2.1.5:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-2.3.6.tgz#1f7cfde88a51a802d69000c7bac85d481c2e871c"
|
||||
integrity sha512-fsEpNKDHgh3Sn66JH06ZnUBnIgUVUtw6ucDhlOj1CEqxIkymU25yv1/kWDPlIjyYHnalr0cN6V+zzUJ+fmWHYw==
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-2.3.7.tgz#3023892419367465e1af1739578f8663d04243b2"
|
||||
integrity sha512-Y0xRz11MPYu/EAvzN94+FsOZHbSvO6FUvHv127CyG7mV6oDoay2bw+g5y9wW3Blf8OY3chaz3nc/DcRe1IQ3Nw==
|
||||
dependencies:
|
||||
esbuild "^0.12.5"
|
||||
postcss "^8.2.10"
|
||||
postcss "^8.3.0"
|
||||
resolve "^1.19.0"
|
||||
rollup "^2.38.5"
|
||||
optionalDependencies:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/string-templates",
|
||||
"version": "0.9.27",
|
||||
"version": "0.9.39",
|
||||
"description": "Handlebars wrapper for Budibase templating.",
|
||||
"main": "src/index.cjs",
|
||||
"module": "dist/bundle.mjs",
|
||||
|
|
|
@ -19,8 +19,9 @@ const ADDED_HELPERS = {
|
|||
date: {
|
||||
args: ["datetime", "format"],
|
||||
numArgs: 2,
|
||||
example: '{{date now "DD-MM-YYYY"}} -> 21-01-2021',
|
||||
description: "Format a date using moment.js date formatting.",
|
||||
example: '{{date now "DD-MM-YYYY" "America/New_York" }} -> 21-01-2021',
|
||||
description:
|
||||
"Format a date using moment.js date formatting - the timezone is optional and uses the tz database.",
|
||||
},
|
||||
duration: {
|
||||
args: ["time", "durationType"],
|
||||
|
|
|
@ -3,6 +3,7 @@ dayjs.extend(require("dayjs/plugin/duration"))
|
|||
dayjs.extend(require("dayjs/plugin/advancedFormat"))
|
||||
dayjs.extend(require("dayjs/plugin/relativeTime"))
|
||||
dayjs.extend(require("dayjs/plugin/utc"))
|
||||
dayjs.extend(require("dayjs/plugin/timezone"))
|
||||
|
||||
/**
|
||||
* This file was largely taken from the helper-date package - we did this for two reasons:
|
||||
|
@ -72,7 +73,8 @@ function setLocale(str, pattern, options) {
|
|||
// if options is null then it'll get updated here
|
||||
const config = initialConfig(str, pattern, options)
|
||||
const defaults = { lang: "en", date: new Date(config.str) }
|
||||
const opts = getContext(this, defaults, config.options)
|
||||
// for now don't allow this to be configurable, don't pass in options
|
||||
const opts = getContext(this, defaults, {})
|
||||
|
||||
// set the language to use
|
||||
dayjs.locale(opts.lang || opts.language)
|
||||
|
@ -89,7 +91,15 @@ module.exports.date = (str, pattern, options) => {
|
|||
|
||||
setLocale(config.str, config.pattern, config.options)
|
||||
|
||||
const date = dayjs(new Date(config.str)).utc()
|
||||
let date = dayjs(new Date(config.str))
|
||||
if (typeof config.options === "string") {
|
||||
date =
|
||||
config.options.toLowerCase() === "utc"
|
||||
? date.utc()
|
||||
: date.tz(config.options)
|
||||
} else {
|
||||
date = date.tz(dayjs.tz.guess())
|
||||
}
|
||||
if (config.pattern === "") {
|
||||
return date.toISOString()
|
||||
}
|
||||
|
|
|
@ -81,14 +81,14 @@ describe("Test that the object processing works correctly", () => {
|
|||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it("should fail gracefully when wrong type is passed in", async () => {
|
||||
it("should be able to handle null objects", async () => {
|
||||
let error = null
|
||||
try {
|
||||
await processObject(null, null)
|
||||
} catch (err) {
|
||||
error = err
|
||||
}
|
||||
expect(error).not.toBeNull()
|
||||
expect(error).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const { processString, processObject, isValid } = require("../src/index.cjs")
|
||||
const tableJson = require("./examples/table.json")
|
||||
const dayjs = require("dayjs")
|
||||
|
||||
describe("test the custom helpers we have applied", () => {
|
||||
it("should be able to use the object helper", async () => {
|
||||
|
@ -162,7 +163,7 @@ describe("test the date helpers", () => {
|
|||
it("should allow use of the date helper", async () => {
|
||||
const date = new Date(1611577535000)
|
||||
const output = await processString("{{ date time 'YYYY-MM-DD' }}", {
|
||||
time: date.toISOString(),
|
||||
time: date.toUTCString(),
|
||||
})
|
||||
expect(output).toBe("2021-01-25")
|
||||
})
|
||||
|
@ -172,6 +173,25 @@ describe("test the date helpers", () => {
|
|||
const output = await processString("{{ date now 'DD' }}", {})
|
||||
expect(parseInt(output)).toBe(date.getDate())
|
||||
})
|
||||
|
||||
it("should test the timezone capabilities", async () => {
|
||||
const date = new Date(1611577535000)
|
||||
const output = await processString("{{ date time 'HH-mm-ss Z' 'America/New_York' }}", {
|
||||
time: date.toUTCString(),
|
||||
})
|
||||
const formatted = new dayjs(date).tz("America/New_York").format("HH-mm-ss Z")
|
||||
expect(output).toBe(formatted)
|
||||
})
|
||||
|
||||
it("should guess the users timezone when not specified", async () => {
|
||||
const date = new Date()
|
||||
const output = await processString("{{ date time 'Z' }}", {
|
||||
time: date.toUTCString()
|
||||
})
|
||||
const timezone = dayjs.tz.guess()
|
||||
const offset = new dayjs(date).tz(timezone).format("Z")
|
||||
expect(output).toBe(offset)
|
||||
})
|
||||
})
|
||||
|
||||
describe("test the string helpers", () => {
|
||||
|
|
|
@ -2313,9 +2313,9 @@ hmac-drbg@^1.0.1:
|
|||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
hosted-git-info@^2.1.4:
|
||||
version "2.8.8"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
||||
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
|
||||
version "2.8.9"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
||||
|
||||
html-encoding-sniffer@^2.0.1:
|
||||
version "2.0.1"
|
||||
|
@ -4968,9 +4968,9 @@ write-file-atomic@^3.0.0:
|
|||
typedarray-to-buffer "^3.1.5"
|
||||
|
||||
ws@^7.2.3:
|
||||
version "7.4.2"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.2.tgz#782100048e54eb36fe9843363ab1c68672b261dd"
|
||||
integrity sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==
|
||||
version "7.4.6"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
|
||||
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
|
||||
|
||||
xml-name-validator@^3.0.0:
|
||||
version "3.0.0"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/worker",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "0.9.27",
|
||||
"version": "0.9.39",
|
||||
"description": "Budibase background service",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
|
@ -21,8 +21,8 @@
|
|||
"author": "Budibase",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@budibase/auth": "^0.9.27",
|
||||
"@budibase/string-templates": "^0.9.27",
|
||||
"@budibase/auth": "^0.9.39",
|
||||
"@budibase/string-templates": "^0.9.39",
|
||||
"@koa/router": "^8.0.0",
|
||||
"aws-sdk": "^2.811.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
|
|
|
@ -8,7 +8,7 @@ const CouchDB = require("../../../db")
|
|||
|
||||
exports.fetch = async ctx => {
|
||||
// always use the dev apps as they'll be most up to date (true)
|
||||
const apps = await getAllApps({ dev: true })
|
||||
const apps = await getAllApps({ CouchDB, dev: true })
|
||||
const promises = []
|
||||
for (let app of apps) {
|
||||
// use dev app IDs
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
const fetch = require("node-fetch")
|
||||
const { DocumentTypes } = require("@budibase/auth").db
|
||||
const CouchDB = require("../../db")
|
||||
const env = require("../../environment")
|
||||
|
||||
const APP_PREFIX = "app_"
|
||||
const URL_REGEX_SLASH = /\/|\\/g
|
||||
|
||||
exports.getApps = async ctx => {
|
||||
let allDbs
|
||||
// allDbs call of CouchDB is very inaccurate in production
|
||||
if (env.COUCH_DB_URL) {
|
||||
allDbs = await (await fetch(`${env.COUCH_DB_URL}/_all_dbs`)).json()
|
||||
} else {
|
||||
allDbs = await CouchDB.allDbs()
|
||||
}
|
||||
const allDbs = await CouchDB.allDbs()
|
||||
const appDbNames = allDbs.filter(dbName => dbName.startsWith(APP_PREFIX))
|
||||
const appPromises = appDbNames.map(db =>
|
||||
new CouchDB(db).get(DocumentTypes.APP_METADATA)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2355,8 +2355,9 @@ has@^1.0.3:
|
|||
function-bind "^1.1.1"
|
||||
|
||||
hosted-git-info@^2.1.4, hosted-git-info@^2.7.1:
|
||||
version "2.8.5"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
|
||||
version "2.8.9"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
||||
|
||||
http-cache-semantics@^3.8.1:
|
||||
version "3.8.1"
|
||||
|
|
Loading…
Reference in New Issue