diff --git a/.github/workflows/release-develop.yml b/.github/workflows/release-develop.yml
index d4050ab40e..a20e292923 100644
--- a/.github/workflows/release-develop.yml
+++ b/.github/workflows/release-develop.yml
@@ -72,3 +72,56 @@ jobs:
env:
DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }}
+
+ - name: Get the latest budibase release version
+ id: version
+ run: |
+ release_version=$(cat lerna.json | jq -r '.version')
+ echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
+
+ - name: Tag and release Proxy service docker image
+ run: |
+ docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
+ yarn build:docker:proxy:release
+ docker tag proxy-service budibase/proxy:$RELEASE_TAG
+ docker push budibase/proxy:$RELEASE_TAG
+ env:
+ DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
+ DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }}
+ RELEASE_TAG: k8s-release
+
+ - name: Pull values.yaml from budibase-infra
+ run: |
+ curl -H "Authorization: token ${{ secrets.GH_PERSONAL_TOKEN }}" \
+ -H 'Accept: application/vnd.github.v3.raw' \
+ -o values.release.yaml \
+ -L https://api.github.com/repos/budibase/budibase-infra/contents/kubernetes/budibase-release/values.yaml
+ wc -l values.release.yaml
+
+ - name: Deploy to Release Environment
+ uses: glopezep/helm@v1.7.1
+ with:
+ release: budibase-release
+ namespace: budibase
+ chart: charts/budibase
+ token: ${{ github.token }}
+ helm: helm3
+ values: |
+ globals:
+ appVersion: develop
+ ingress:
+ enabled: true
+ nginx: true
+ value-files: >-
+ [
+ "values.release.yaml"
+ ]
+ env:
+ KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
+
+ - name: Discord Webhook Action
+ uses: tsickert/discord-webhook@v4.0.0
+ with:
+ webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }}
+ content: "Release Env Deployment Complete: ${{ env.RELEASE_VERSION }} deployed to Budibase Release Env."
+ embed-title: ${{ env.RELEASE_VERSION }}
\ No newline at end of file
diff --git a/lerna.json b/lerna.json
index 8f68c88cd1..716f366af9 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "1.0.167-alpha.8",
+ "version": "1.0.189",
"npmClient": "yarn",
"packages": [
"packages/*"
diff --git a/package.json b/package.json
index aca1762bea..7a410482db 100644
--- a/package.json
+++ b/package.json
@@ -56,6 +56,7 @@
"build:docker:proxy": "docker build hosting/proxy -t proxy-service",
"build:docker:proxy:compose": "node scripts/proxy/generateProxyConfig compose && npm run build:docker:proxy",
"build:docker:proxy:preprod": "node scripts/proxy/generateProxyConfig preprod && npm run build:docker:proxy",
+ "build:docker:proxy:release": "node scripts/proxy/generateProxyConfig release && npm run build:docker:proxy",
"build:docker:proxy:prod": "node scripts/proxy/generateProxyConfig prod && npm run build:docker:proxy",
"build:docker:selfhost": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh latest && cd -",
"build:docker:develop": "node scripts/pinVersions && lerna run build:docker && npm run build:docker:proxy:compose && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh develop && cd -",
diff --git a/packages/backend-core/cache.js b/packages/backend-core/cache.js
index 02344586a9..932fd7b901 100644
--- a/packages/backend-core/cache.js
+++ b/packages/backend-core/cache.js
@@ -1,4 +1,7 @@
+const generic = require("./src/cache/generic")
+
module.exports = {
user: require("./src/cache/user"),
app: require("./src/cache/appMetadata"),
+ ...generic,
}
diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json
index a717fd1211..8f3a8a9f52 100644
--- a/packages/backend-core/package.json
+++ b/packages/backend-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/backend-core",
- "version": "1.0.167-alpha.8",
+ "version": "1.0.189",
"description": "Budibase backend core libraries used in server and worker",
"main": "src/index.ts",
"types": "dist/src/index.d.ts",
@@ -14,7 +14,7 @@
"dependencies": {
"@techpass/passport-openidconnect": "^0.3.0",
"aws-sdk": "^2.901.0",
- "bcryptjs": "^2.4.3",
+ "bcrypt": "^5.0.1",
"dotenv": "^16.0.1",
"emitter-listener": "^1.1.2",
"ioredis": "^4.27.1",
@@ -58,7 +58,6 @@
"jest": "^27.0.3",
"koa": "2.7.0",
"pouchdb-adapter-memory": "^7.2.2",
- "pouchdb-all-dbs": "^1.0.2",
"timekeeper": "^2.2.0",
"ts-jest": "^27.0.3",
"typescript": "^4.5.5",
diff --git a/packages/backend-core/src/auth.js b/packages/backend-core/src/auth.js
index f6d53522d5..b13cd932c6 100644
--- a/packages/backend-core/src/auth.js
+++ b/packages/backend-core/src/auth.js
@@ -29,7 +29,7 @@ passport.deserializeUser(async (user, done) => {
const user = await db.get(user._id)
return done(null, user)
} catch (err) {
- console.error("User not found", err)
+ console.error(`User not found`, err)
return done(null, false, { message: "User not found" })
}
})
diff --git a/packages/backend-core/src/cache/generic.js b/packages/backend-core/src/cache/generic.js
new file mode 100644
index 0000000000..b23568f8b9
--- /dev/null
+++ b/packages/backend-core/src/cache/generic.js
@@ -0,0 +1,49 @@
+const redis = require("../redis/authRedis")
+const env = require("../environment")
+const { getTenantId } = require("../context")
+
+exports.CacheKeys = {
+ CHECKLIST: "checklist",
+}
+
+exports.TTL = {
+ ONE_MINUTE: 600,
+ ONE_HOUR: 3600,
+ ONE_DAY: 86400,
+}
+
+function generateTenantKey(key) {
+ const tenantId = getTenantId()
+ return `${key}:${tenantId}`
+}
+
+exports.withCache = async (key, ttl, fetchFn) => {
+ key = generateTenantKey(key)
+ const client = await redis.getCacheClient()
+ const cachedValue = await client.get(key)
+ if (cachedValue) {
+ return cachedValue
+ }
+
+ try {
+ const fetchedValue = await fetchFn()
+
+ if (!env.isTest()) {
+ await client.store(key, fetchedValue, ttl)
+ }
+ return fetchedValue
+ } catch (err) {
+ console.error("Error fetching before cache - ", err)
+ throw err
+ }
+}
+
+exports.bustCache = async key => {
+ const client = await redis.getCacheClient()
+ try {
+ await client.delete(generateTenantKey(key))
+ } catch (err) {
+ console.error("Error busting cache - ", err)
+ throw err
+ }
+}
diff --git a/packages/backend-core/src/context/index.js b/packages/backend-core/src/context/index.js
index 59dc0cda79..18943fb8c9 100644
--- a/packages/backend-core/src/context/index.js
+++ b/packages/backend-core/src/context/index.js
@@ -73,7 +73,7 @@ exports.isMultiTenant = () => {
}
// used for automations, API endpoints should always be in context already
-exports.doInTenant = (tenantId, task) => {
+exports.doInTenant = (tenantId, task, { forceNew } = {}) => {
// the internal function is so that we can re-use an existing
// context - don't want to close DB on a parent context
async function internal(opts = { existing: false }) {
@@ -96,7 +96,11 @@ exports.doInTenant = (tenantId, task) => {
}
const using = cls.getFromContext(ContextKeys.IN_USE)
- if (using && cls.getFromContext(ContextKeys.TENANT_ID) === tenantId) {
+ if (
+ !forceNew &&
+ using &&
+ cls.getFromContext(ContextKeys.TENANT_ID) === tenantId
+ ) {
cls.setOnContext(ContextKeys.IN_USE, using + 1)
return internal({ existing: true })
} else {
@@ -133,7 +137,7 @@ const setAppTenantId = appId => {
exports.updateTenantId(appTenantId)
}
-exports.doInAppContext = (appId, task) => {
+exports.doInAppContext = (appId, task, { forceNew } = {}) => {
if (!appId) {
throw new Error("appId is required")
}
@@ -164,7 +168,7 @@ exports.doInAppContext = (appId, task) => {
}
}
const using = cls.getFromContext(ContextKeys.IN_USE)
- if (using && cls.getFromContext(ContextKeys.APP_ID) === appId) {
+ if (!forceNew && using && cls.getFromContext(ContextKeys.APP_ID) === appId) {
cls.setOnContext(ContextKeys.IN_USE, using + 1)
return internal({ existing: true })
} else {
diff --git a/packages/backend-core/src/db/index.js b/packages/backend-core/src/db/index.js
index 9a11aa5bed..75ae2e1b13 100644
--- a/packages/backend-core/src/db/index.js
+++ b/packages/backend-core/src/db/index.js
@@ -3,6 +3,7 @@ const env = require("../environment")
let PouchDB
let initialised = false
+const dbList = new Set()
const put =
dbPut =>
@@ -30,6 +31,9 @@ exports.init = opts => {
// in situations that using the function doWithDB does not work
exports.dangerousGetDB = (dbName, opts) => {
checkInitialised()
+ if (env.isTest()) {
+ dbList.add(dbName)
+ }
const db = new PouchDB(dbName, opts)
const dbPut = db.put
db.put = put(dbPut)
@@ -65,6 +69,9 @@ exports.doWithDB = async (dbName, cb, opts) => {
}
exports.allDbs = () => {
+ if (!env.isTest()) {
+ throw new Error("Cannot be used outside test environment.")
+ }
checkInitialised()
- return PouchDB.allDbs()
+ return [...dbList]
}
diff --git a/packages/backend-core/src/db/pouch.js b/packages/backend-core/src/db/pouch.js
index 9c1ada8d76..76390ac644 100644
--- a/packages/backend-core/src/db/pouch.js
+++ b/packages/backend-core/src/db/pouch.js
@@ -92,11 +92,5 @@ exports.getPouch = (opts = {}) => {
PouchDB.plugin(find)
}
- const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS)
- if (opts.allDbs) {
- const allDbs = require("pouchdb-all-dbs")
- allDbs(Pouch)
- }
-
- return Pouch
+ return PouchDB.defaults(POUCH_DB_DEFAULTS)
}
diff --git a/packages/backend-core/src/db/tests/index.spec.js b/packages/backend-core/src/db/tests/index.spec.js
index 9e555ab92a..0ede666197 100644
--- a/packages/backend-core/src/db/tests/index.spec.js
+++ b/packages/backend-core/src/db/tests/index.spec.js
@@ -22,18 +22,5 @@ describe("db", () => {
await db.destroy()
})
})
-
- describe("allDbs", () => {
- it("returns all dbs", async () => {
- let all = await allDbs()
- expect(all).toStrictEqual([])
- const db1 = dangerousGetDB("test1")
- await db1.put({ _id: "test1" })
- const db2 = dangerousGetDB("test2")
- await db2.put({ _id: "test2" })
- all = await allDbs()
- expect(all.length).toBe(2)
- })
- })
})
diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts
index 9cb1d24423..1576c4487d 100644
--- a/packages/backend-core/src/environment.ts
+++ b/packages/backend-core/src/environment.ts
@@ -53,6 +53,7 @@ const env: any = {
USE_COUCH: process.env.USE_COUCH || true,
DISABLE_DEVELOPER_LICENSE: process.env.DISABLE_DEVELOPER_LICENSE,
SERVICE: process.env.SERVICE || "budibase",
+ DEFAULT_LICENSE: process.env.DEFAULT_LICENSE,
_set(key: any, value: any) {
process.env[key] = value
module.exports[key] = value
diff --git a/packages/backend-core/src/errors/http.js b/packages/backend-core/src/errors/http.js
index 342d663da7..8e7cab4638 100644
--- a/packages/backend-core/src/errors/http.js
+++ b/packages/backend-core/src/errors/http.js
@@ -1,8 +1,8 @@
const { GenericError } = require("./generic")
class HTTPError extends GenericError {
- constructor(message, httpStatus, code, type) {
- super(message, code ? code : "http", type)
+ constructor(message, httpStatus, code = "http", type = "generic") {
+ super(message, code, type)
this.status = httpStatus
}
}
diff --git a/packages/backend-core/src/hashing.js b/packages/backend-core/src/hashing.js
index 7f3af2855f..45abe2f9bd 100644
--- a/packages/backend-core/src/hashing.js
+++ b/packages/backend-core/src/hashing.js
@@ -1,4 +1,4 @@
-const bcrypt = require("bcryptjs")
+const bcrypt = require("bcrypt")
const env = require("./environment")
const { v4 } = require("uuid")
diff --git a/packages/backend-core/src/middleware/passport/local.js b/packages/backend-core/src/middleware/passport/local.js
index 9377d895da..445893b1df 100644
--- a/packages/backend-core/src/middleware/passport/local.js
+++ b/packages/backend-core/src/middleware/passport/local.js
@@ -30,7 +30,7 @@ exports.authenticate = async function (ctx, email, password, done) {
const dbUser = await users.getGlobalUserByEmail(email)
if (dbUser == null) {
- return authError(done, "User not found")
+ return authError(done, `User not found: [${email}]`)
}
// check that the user is currently inactive, if this is the case throw invalid
diff --git a/packages/backend-core/src/middleware/passport/third-party-common.js b/packages/backend-core/src/middleware/passport/third-party-common.js
index 845362200f..1c5891fce7 100644
--- a/packages/backend-core/src/middleware/passport/third-party-common.js
+++ b/packages/backend-core/src/middleware/passport/third-party-common.js
@@ -86,7 +86,7 @@ exports.authenticateThirdParty = async function (
}
// now that we're sure user exists, load them from the db
- dbUser = await users.getGlobalUserByEmail(thirdPartyUser.email)
+ dbUser = await db.get(dbUser._id)
// authenticate
const sessionId = newid()
diff --git a/packages/backend-core/src/redis/authRedis.js b/packages/backend-core/src/redis/authRedis.js
index ca5c9bae37..b9f6d8d0b0 100644
--- a/packages/backend-core/src/redis/authRedis.js
+++ b/packages/backend-core/src/redis/authRedis.js
@@ -1,18 +1,20 @@
const Client = require("./index")
const utils = require("./utils")
-let userClient, sessionClient, appClient
+let userClient, sessionClient, appClient, cacheClient
async function init() {
userClient = await new Client(utils.Databases.USER_CACHE).init()
sessionClient = await new Client(utils.Databases.SESSIONS).init()
appClient = await new Client(utils.Databases.APP_METADATA).init()
+ cacheClient = await new Client(utils.Databases.GENERIC_CACHE).init()
}
process.on("exit", async () => {
if (userClient) await userClient.finish()
if (sessionClient) await sessionClient.finish()
if (appClient) await appClient.finish()
+ if (cacheClient) await cacheClient.finish()
})
module.exports = {
@@ -34,4 +36,10 @@ module.exports = {
}
return appClient
},
+ getCacheClient: async () => {
+ if (!cacheClient) {
+ await init()
+ }
+ return cacheClient
+ },
}
diff --git a/packages/backend-core/src/redis/utils.js b/packages/backend-core/src/redis/utils.js
index 77f64f6593..90ea5c33f9 100644
--- a/packages/backend-core/src/redis/utils.js
+++ b/packages/backend-core/src/redis/utils.js
@@ -18,6 +18,7 @@ exports.Databases = {
APP_METADATA: "appMetadata",
QUERY_VARS: "queryVars",
LICENSES: "license",
+ GENERIC_CACHE: "data_cache",
}
exports.SEPARATOR = SEPARATOR
diff --git a/packages/backend-core/src/security/sessions.js b/packages/backend-core/src/security/sessions.js
index 1720eeb820..4e6899c248 100644
--- a/packages/backend-core/src/security/sessions.js
+++ b/packages/backend-core/src/security/sessions.js
@@ -15,29 +15,33 @@ function makeSessionID(userId, sessionId) {
}
async function invalidateSessions(userId, sessionIds = null) {
- let sessions = []
+ try {
+ let sessions = []
- // If no sessionIds, get all the sessions for the user
- if (!sessionIds) {
- sessions = await getSessionsForUser(userId)
- sessions.forEach(
- session =>
- (session.key = makeSessionID(session.userId, session.sessionId))
- )
- } else {
- // use the passed array of sessionIds
- sessions = Array.isArray(sessionIds) ? sessionIds : [sessionIds]
- sessions = sessions.map(sessionId => ({
- key: makeSessionID(userId, sessionId),
- }))
- }
+ // If no sessionIds, get all the sessions for the user
+ if (!sessionIds) {
+ sessions = await getSessionsForUser(userId)
+ sessions.forEach(
+ session =>
+ (session.key = makeSessionID(session.userId, session.sessionId))
+ )
+ } else {
+ // use the passed array of sessionIds
+ sessions = Array.isArray(sessionIds) ? sessionIds : [sessionIds]
+ sessions = sessions.map(sessionId => ({
+ key: makeSessionID(userId, sessionId),
+ }))
+ }
- const client = await redis.getSessionClient()
- const promises = []
- for (let session of sessions) {
- promises.push(client.delete(session.key))
+ const client = await redis.getSessionClient()
+ const promises = []
+ for (let session of sessions) {
+ promises.push(client.delete(session.key))
+ }
+ await Promise.all(promises)
+ } catch (err) {
+ console.error(`Error invalidating sessions: ${err}`)
}
- await Promise.all(promises)
}
exports.createASession = async (userId, session) => {
@@ -76,6 +80,7 @@ exports.getSession = async (userId, sessionId) => {
return client.get(makeSessionID(userId, sessionId))
} catch (err) {
// if can't get session don't error, just don't return anything
+ console.error(err)
return null
}
}
diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js
index 746deb3428..2a40f89b8a 100644
--- a/packages/backend-core/src/tests/utilities/mocks/events.js
+++ b/packages/backend-core/src/tests/utilities/mocks/events.js
@@ -1,5 +1,9 @@
jest.mock("../../../events", () => {
return {
+ identification: {
+ identifyTenantGroup: jest.fn(),
+ identifyUser: jest.fn(),
+ },
analytics: {
enabled: () => false,
},
diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock
index 638fc47117..606e978ab1 100644
--- a/packages/backend-core/yarn.lock
+++ b/packages/backend-core/yarn.lock
@@ -713,6 +713,21 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
+"@mapbox/node-pre-gyp@^1.0.0":
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc"
+ integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==
+ dependencies:
+ detect-libc "^2.0.0"
+ https-proxy-agent "^5.0.0"
+ make-dir "^3.1.0"
+ node-fetch "^2.6.7"
+ nopt "^5.0.0"
+ npmlog "^5.0.1"
+ rimraf "^3.0.2"
+ semver "^7.3.5"
+ tar "^6.1.11"
+
"@shopify/jest-koa-mocks@^3.1.5":
version "3.1.5"
resolved "https://registry.yarnpkg.com/@shopify/jest-koa-mocks/-/jest-koa-mocks-3.1.5.tgz#11f77ccfbcaf35cf5ee2c6108a286e61e6bea084"
@@ -1173,6 +1188,19 @@ anymatch@^3.0.3, anymatch@~3.1.2:
normalize-path "^3.0.0"
picomatch "^2.0.4"
+"aproba@^1.0.3 || ^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
+ integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
+
+are-we-there-yet@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
+ integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==
+ dependencies:
+ delegates "^1.0.0"
+ readable-stream "^3.6.0"
+
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -1360,10 +1388,13 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
-bcryptjs@^2.4.3:
- version "2.4.3"
- resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
- integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
+bcrypt@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.0.1.tgz#f1a2c20f208e2ccdceea4433df0c8b2c54ecdf71"
+ integrity sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==
+ dependencies:
+ "@mapbox/node-pre-gyp" "^1.0.0"
+ node-addon-api "^3.1.0"
binary-extensions@^2.0.0:
version "2.2.0"
@@ -1586,6 +1617,11 @@ chownr@^1.1.1:
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
+chownr@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
+ integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
+
ci-info@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
@@ -1666,6 +1702,11 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+color-support@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
+ integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
+
combined-stream@^1.0.5, combined-stream@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
@@ -1722,6 +1763,11 @@ configstore@^5.0.1:
write-file-atomic "^3.0.0"
xdg-basedir "^4.0.0"
+console-control-strings@^1.0.0, console-control-strings@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+ integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
+
content-disposition@^0.5.3, content-disposition@~0.5.2:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
@@ -1934,6 +1980,11 @@ destroy@^1.0.4:
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
+detect-libc@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
+ integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
+
detect-newline@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
@@ -2132,12 +2183,12 @@ escodegen@^2.0.0:
esprima-fb@^15001.1.0-dev-harmony-fb:
version "15001.1.0-dev-harmony-fb"
resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz#30a947303c6b8d5e955bee2b99b1d233206a6901"
- integrity sha1-MKlHMDxrjV6VW+4rmbHSMyBqaQE=
+ integrity sha512-59dDGQo2b3M/JfKIws0/z8dcXH2mnVHkfSPRhCYS91JNGfGNwr7GsSF6qzWZuOGvw5Ii0w9TtylrX07MGmlOoQ==
esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
- integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=
+ integrity sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==
esprima@^4.0.0, esprima@^4.0.1:
version "4.0.1"
@@ -2147,7 +2198,7 @@ esprima@^4.0.0, esprima@^4.0.1:
esprima@~3.1.0:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
- integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=
+ integrity sha512-AWwVMNxwhN8+NIPQzAQZCm7RkLC4RbM3B1OobMuyp3i+w73X57KCKaVIxaRZb+DYCojq7rspo+fmuQfAboyhFg==
estraverse@^5.2.0:
version "5.3.0"
@@ -2326,6 +2377,13 @@ fs-constants@^1.0.0:
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
+fs-minipass@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
+ integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
+ dependencies:
+ minipass "^3.0.0"
+
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -2346,6 +2404,21 @@ functional-red-black-tree@^1.0.1:
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+gauge@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395"
+ integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==
+ dependencies:
+ aproba "^1.0.3 || ^2.0.0"
+ color-support "^1.1.2"
+ console-control-strings "^1.0.0"
+ has-unicode "^2.0.1"
+ object-assign "^4.1.1"
+ signal-exit "^3.0.0"
+ string-width "^4.2.3"
+ strip-ansi "^6.0.1"
+ wide-align "^1.1.2"
+
gensync@^1.0.0-beta.2:
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@@ -2472,12 +2545,7 @@ got@^9.6.0:
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
-graceful-fs@^4.1.2:
- version "4.2.8"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
- integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
-
-graceful-fs@^4.2.9:
+graceful-fs@^4.1.2, graceful-fs@^4.2.9:
version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
@@ -2540,6 +2608,11 @@ has-tostringtag@^1.0.0:
dependencies:
has-symbols "^1.0.2"
+has-unicode@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+ integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
+
has-yarn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77"
@@ -3872,7 +3945,7 @@ ltgt@2.2.1, ltgt@^2.1.2, ltgt@~2.2.0:
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5"
integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=
-make-dir@^3.0.0:
+make-dir@^3.0.0, make-dir@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
@@ -3996,29 +4069,56 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
-"minimatch@2 || 3", minimatch@^3.0.4:
+"minimatch@2 || 3":
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
-minimist@^1.2.0, minimist@^1.2.5:
+minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
+minipass@^3.0.0:
+ version "3.1.6"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee"
+ integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==
+ dependencies:
+ yallist "^4.0.0"
+
+minizlib@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
+ integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
+ dependencies:
+ minipass "^3.0.0"
+ yallist "^4.0.0"
+
mkdirp-classic@^0.5.2:
version "0.5.3"
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mkdirp@^0.5.0:
- version "0.5.5"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
- integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
+ integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
dependencies:
- minimist "^1.2.5"
+ minimist "^1.2.6"
+
+mkdirp@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
+ integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
ms@2.0.0:
version "2.0.0"
@@ -4060,12 +4160,17 @@ negotiator@0.6.3:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+node-addon-api@^3.1.0:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
+ integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
+
node-fetch@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
-node-fetch@2.6.7, node-fetch@^2.6.1:
+node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@@ -4129,6 +4234,13 @@ nodemon@^2.0.7:
undefsafe "^2.0.5"
update-notifier "^5.1.0"
+nopt@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
+ integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==
+ dependencies:
+ abbrev "1"
+
nopt@~1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
@@ -4153,6 +4265,16 @@ npm-run-path@^4.0.1:
dependencies:
path-key "^3.0.0"
+npmlog@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0"
+ integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==
+ dependencies:
+ are-we-there-yet "^2.0.0"
+ console-control-strings "^1.1.0"
+ gauge "^3.0.0"
+ set-blocking "^2.0.0"
+
nwsapi@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
@@ -4178,6 +4300,11 @@ object-assign@^2.0.0:
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa"
integrity sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=
+object-assign@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
on-finished@^2.3.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
@@ -4784,7 +4911,7 @@ readable-stream@1.1.14, readable-stream@^1.0.27-1:
isarray "0.0.1"
string_decoder "~0.10.x"
-"readable-stream@2 || 3", readable-stream@^3.1.1, readable-stream@^3.4.0:
+"readable-stream@2 || 3", readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@@ -4966,7 +5093,7 @@ responselike@^1.0.2:
dependencies:
lowercase-keys "^1.0.0"
-rimraf@^3.0.0:
+rimraf@^3.0.0, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
@@ -5017,7 +5144,7 @@ semver-diff@^3.1.1:
dependencies:
semver "^6.3.0"
-semver@7.x, semver@^7.0.0, semver@^7.3.4:
+semver@7.x, semver@^7.0.0, semver@^7.3.4, semver@^7.3.5:
version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
@@ -5041,6 +5168,11 @@ semver@^7.3.2:
dependencies:
lru-cache "^6.0.0"
+set-blocking@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
@@ -5063,16 +5195,16 @@ shimmer@^1.2.0:
resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337"
integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==
+signal-exit@^3.0.0, signal-exit@^3.0.3:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
signal-exit@^3.0.2:
version "3.0.5"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f"
integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==
-signal-exit@^3.0.3:
- version "3.0.7"
- resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
- integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
-
sisteransi@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
@@ -5198,7 +5330,7 @@ string-template@~1.0.0:
resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96"
integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y=
-string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2:
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -5328,6 +5460,18 @@ tar-stream@^2.1.4:
inherits "^2.0.3"
readable-stream "^3.1.1"
+tar@^6.1.11:
+ version "6.1.11"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
+ integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
+ dependencies:
+ chownr "^2.0.0"
+ fs-minipass "^2.0.0"
+ minipass "^3.0.0"
+ minizlib "^2.1.1"
+ mkdirp "^1.0.3"
+ yallist "^4.0.0"
+
terminal-link@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"
@@ -5736,6 +5880,13 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"
+wide-align@^1.1.2:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
+ integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
+ dependencies:
+ string-width "^1.0.2 || 2 || 3 || 4"
+
widest-line@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"
diff --git a/packages/bbui/package.json b/packages/bbui/package.json
index 86c1258e69..78628f9e68 100644
--- a/packages/bbui/package.json
+++ b/packages/bbui/package.json
@@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
- "version": "1.0.167-alpha.8",
+ "version": "1.0.189",
"license": "MPL-2.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
@@ -38,7 +38,7 @@
],
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
- "@budibase/string-templates": "^1.0.167-alpha.8",
+ "@budibase/string-templates": "^1.0.189",
"@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2",
diff --git a/packages/bbui/src/Form/Core/Dropzone.svelte b/packages/bbui/src/Form/Core/Dropzone.svelte
index d739e751c9..36515acbc5 100644
--- a/packages/bbui/src/Form/Core/Dropzone.svelte
+++ b/packages/bbui/src/Form/Core/Dropzone.svelte
@@ -18,6 +18,7 @@
export let fileSizeLimit = BYTES_IN_MB * 20
export let processFiles = null
export let handleFileTooLarge = null
+ export let handleTooManyFiles = null
export let gallery = true
export let error = null
export let fileTags = []
@@ -71,6 +72,13 @@
handleFileTooLarge(fileSizeLimit, value)
return
}
+
+ const fileCount = fileList.length + value.length
+ if (handleTooManyFiles && maximum && fileCount > maximum) {
+ handleTooManyFiles(maximum)
+ return
+ }
+
if (processFiles) {
const processedFiles = await processFiles(fileList)
const newValue = [...value, ...processedFiles]
diff --git a/packages/bbui/src/Form/Dropzone.svelte b/packages/bbui/src/Form/Dropzone.svelte
index 757d76398b..f1b548f7f1 100644
--- a/packages/bbui/src/Form/Dropzone.svelte
+++ b/packages/bbui/src/Form/Dropzone.svelte
@@ -11,6 +11,7 @@
export let fileSizeLimit = undefined
export let processFiles = undefined
export let handleFileTooLarge = undefined
+ export let handleTooManyFiles = undefined
export let gallery = true
export let fileTags = []
export let maximum = undefined
@@ -30,6 +31,7 @@
{fileSizeLimit}
{processFiles}
{handleFileTooLarge}
+ {handleTooManyFiles}
{gallery}
{fileTags}
{maximum}
diff --git a/packages/bbui/src/Layout/Layout.svelte b/packages/bbui/src/Layout/Layout.svelte
index c66a409242..6a01d9bece 100644
--- a/packages/bbui/src/Layout/Layout.svelte
+++ b/packages/bbui/src/Layout/Layout.svelte
@@ -40,6 +40,10 @@
padding-left: var(--spacing-xl);
padding-right: var(--spacing-xl);
}
+ .paddingX-XXL {
+ padding-left: var(--spectrum-alias-grid-gutter-large);
+ padding-right: var(--spectrum-alias-grid-gutter-large);
+ }
.paddingY-S {
padding-top: var(--spacing-s);
padding-bottom: var(--spacing-s);
@@ -56,6 +60,10 @@
padding-top: var(--spacing-xl);
padding-bottom: var(--spacing-xl);
}
+ .paddingY-XXL {
+ padding-top: var(--spectrum-alias-grid-gutter-large);
+ padding-bottom: var(--spectrum-alias-grid-gutter-large);
+ }
.gap-XXS {
grid-gap: var(--spacing-xs);
}
diff --git a/packages/bbui/src/Layout/Page.svelte b/packages/bbui/src/Layout/Page.svelte
index c12d54787b..2996bcc613 100644
--- a/packages/bbui/src/Layout/Page.svelte
+++ b/packages/bbui/src/Layout/Page.svelte
@@ -1,9 +1,10 @@
-
+
@@ -23,4 +24,9 @@
max-width: none;
margin: 0;
}
+
+ .noPadding {
+ padding: 0px;
+ margin: 0px;
+ }
diff --git a/packages/bbui/src/SideNavigation/Item.svelte b/packages/bbui/src/SideNavigation/Item.svelte
index dfebdb46a6..30da1fa172 100644
--- a/packages/bbui/src/SideNavigation/Item.svelte
+++ b/packages/bbui/src/SideNavigation/Item.svelte
@@ -7,6 +7,7 @@
export let icon = ""
export let selected = false
export let disabled = false
+ export let dataCy
{#if heading}
diff --git a/packages/bbui/src/Tabs/Tab.svelte b/packages/bbui/src/Tabs/Tab.svelte
index 0aa59f7f8a..04791619dc 100644
--- a/packages/bbui/src/Tabs/Tab.svelte
+++ b/packages/bbui/src/Tabs/Tab.svelte
@@ -6,7 +6,7 @@
const dispatch = createEventDispatcher()
let selected = getContext("tab")
- let tab
+ let tab_internal
let tabInfo
const setTabInfo = () => {
@@ -16,7 +16,7 @@
// We just need to get this off the main thread to fix this, by using
// a 0ms timeout.
setTimeout(() => {
- tabInfo = tab?.getBoundingClientRect()
+ tabInfo = tab_internal?.getBoundingClientRect()
if (tabInfo && $selected.title === title) {
$selected.info = tabInfo
}
@@ -27,14 +27,30 @@
setTabInfo()
})
+ //Ensure that the underline is in the correct location
+ $: {
+ if ($selected.title === title && tab_internal) {
+ if ($selected.info?.left !== tab_internal.getBoundingClientRect().left) {
+ $selected = {
+ ...$selected,
+ info: tab_internal.getBoundingClientRect(),
+ }
+ }
+ }
+ }
+
const onClick = () => {
- $selected = { ...$selected, title, info: tab.getBoundingClientRect() }
+ $selected = {
+ ...$selected,
+ title,
+ info: tab_internal.getBoundingClientRect(),
+ }
dispatch("click")
}
{
+ context("Application Overview screen", () => {
+ before(() => {
+ cy.login()
+ cy.createTestApp()
+ })
+
+ it("Should be accessible from the applications list", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+
+ cy.get(".appTable .title").eq(0)
+ .invoke('attr', 'data-cy')
+ .then(($dataCy) => {
+ const dataCy = $dataCy;
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.location().should((loc) => {
+ expect(loc.pathname).to.eq('/builder/portal/overview/' + dataCy)
+ })
+ })
+
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+
+ cy.get(".appTable .title").eq(0)
+ .invoke('attr', 'data-cy')
+ .then(($dataCy) => {
+ const dataCy = $dataCy;
+ cy.get(".appTable .app-row-actions button").contains("View").click({force: true})
+
+ cy.location().should((loc) => {
+ expect(loc.pathname).to.eq('/builder/portal/overview/' + dataCy)
+ })
+ })
+
+ })
+
+ // Find a more suitable place for this.
+ it("Should allow unlocking in the app list", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+
+ cy.get(".appTable .lock-status").eq(0).contains("Locked by you").click()
+
+ cy.unlockApp({ owned : true })
+
+ cy.get(".appTable").should("exist")
+ cy.get(".lock-status").should('not.be.visible')
+ })
+
+ it("Should allow unlocking in the app overview screen", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+
+ cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true})
+ cy.wait(1000)
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".lock-status").eq(0).contains("Locked by you").click()
+
+ cy.unlockApp({ owned : true })
+
+ cy.get(".lock-status").should("not.be.visible")
+ })
+
+ it("Should reflect the deploy state of an app that hasn't been published.", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".header-right button.spectrum-Button[data-cy='view-app']").should("be.disabled")
+
+ cy.get(".spectrum-Tabs-item.is-selected").contains("Overview")
+ cy.get(".overview-tab").should("be.visible")
+
+ cy.get(".overview-tab [data-cy='app-status']").within(() => {
+ cy.get(".status-display").contains("Unpublished")
+ cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should("exist")
+ cy.get(".status-text").contains("-")
+ })
+ })
+
+ it("Should reflect the app deployment state", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true})
+
+ cy.get(".toprightnav button.spectrum-Button").contains("Publish").click({ force : true })
+ cy.get(".spectrum-Modal [data-cy='deploy-app-modal']").should("be.visible")
+ .within(() => {
+ cy.get(".spectrum-Button").contains("Publish").click({ force : true })
+ cy.wait(1000)
+ });
+
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".header-right button.spectrum-Button[data-cy='view-app']").should("not.be.disabled")
+
+ cy.get(".overview-tab [data-cy='app-status']").within(() => {
+ cy.get(".status-display").contains("Published")
+ cy.get(".status-display .icon svg[aria-label='GlobeCheck']").should("exist")
+ cy.get(".status-text").contains("Last published a few seconds ago")
+ })
+ })
+
+ it("Should reflect an application that has been unpublished", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true})
+
+ cy.get(".deployment-top-nav svg[aria-label='Globe']")
+ .click({ force: true })
+
+ cy.get("[data-cy='publish-popover-menu']").should("be.visible")
+ cy.get("[data-cy='publish-popover-menu'] [data-cy='publish-popover-action']")
+ .click({ force : true })
+
+ cy.get("[data-cy='unpublish-modal']").should("be.visible")
+ .within(() => {
+ cy.get(".confirm-wrap button").click({ force: true }
+ )})
+ cy.wait(1000)
+
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".overview-tab [data-cy='app-status']").within(() => {
+ cy.get(".status-display").contains("Unpublished")
+ cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should("exist")
+ cy.get(".status-text").contains("Last published a few seconds ago")
+ })
+ })
+
+ it("Should allow the editing of the application icon", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".app-logo .edit-hover").should("exist").invoke("show").click()
+
+ cy.customiseAppIcon()
+
+ cy.get(".app-logo")
+ .within(() => {
+ cy.get('[aria-label]').eq(0).children()
+ .should('have.attr', 'xlink:href').and('not.contain', '#spectrum-icon-18-Apps')
+ cy.get(".app-icon")
+ .should('have.attr', 'style').and('contains', 'color')
+ })
+ })
+
+ it("Should reflect the last time the application was edited", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".header-right button").contains("Edit").click({ force: true });
+
+ cy.navigateToFrontend()
+
+ cy.addComponent("Elements", "Headline").then(componentId => {
+ cy.getComponent(componentId).should("exist")
+ })
+
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".overview-tab [data-cy='edited-by']").within(() => {
+ cy.get(".editor-name").contains("You")
+ cy.get(".last-edit-text").contains("Last edited a few seconds ago")
+ })
+ });
+
+ it("Should reflect application version is up-to-date", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".overview-tab [data-cy='app-version']").within(() => {
+ cy.get(".version-status").contains("You're running the latest!")
+ })
+ });
+
+ it("Should navigate to the settings tab when clicking the App Version card header", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".spectrum-Tabs-item.is-selected").contains("Overview")
+ cy.get(".overview-tab").should("be.visible")
+
+ cy.get(".overview-tab [data-cy='app-version'] .dash-card-header").click({ force : true })
+
+ cy.get(".spectrum-Tabs-item.is-selected").contains("Settings")
+ cy.get(".settings-tab").should("be.visible")
+ cy.get(".overview-tab").should("not.exist")
+
+ });
+
+ it("Should allow the upgrading of an application, if available.", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+ cy.wait(500)
+
+ cy.location().then(loc => {
+ const params = loc.pathname.split("/")
+ const appId = params[params.length - 1]
+ cy.log(appId)
+ //Downgrade the app for the test
+ cy.alterAppVersion(appId, "0.0.1-alpha.0")
+ .then(()=>{
+ cy.reload()
+ cy.wait(1000)
+ cy.log("Current deployment version: " + clientPackage.version)
+
+ cy.get(".version-status a").contains("Update").click()
+ cy.get(".spectrum-Tabs-item.is-selected").contains("Settings")
+
+ cy.get(".version-section .page-action button").contains("Update").click({ force: true })
+
+ cy.intercept('POST', '**/applications/**/client/update').as('updateVersion')
+ cy.get(".spectrum-Modal.is-open button").contains("Update").click({ force: true })
+
+ cy.wait("@updateVersion")
+ .its('response.statusCode').should('eq', 200)
+ .then(() => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".spectrum-Tabs-item").contains("Overview").click({ force: true })
+ cy.get(".overview-tab [data-cy='app-version']").within(() => {
+ cy.get(".spectrum-Heading").contains(clientPackage.version)
+ cy.get(".version-status").contains("You're running the latest!")
+ })
+ })
+ })
+ });
+
+ })
+
+ it("Should allow editing of the app details.", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".spectrum-Tabs-item").contains("Settings").click()
+ cy.get(".spectrum-Tabs-item.is-selected").contains("Settings")
+ cy.get(".settings-tab").should("be.visible")
+
+ cy.get(".details-section .page-action button").contains("Edit").click({ force: true })
+ cy.updateAppName("sample name")
+
+ //publish and check its disabled
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true})
+
+ cy.get(".toprightnav button.spectrum-Button").contains("Publish").click({ force : true })
+ cy.get(".spectrum-Modal [data-cy='deploy-app-modal']").should("be.visible")
+ .within(() => {
+ cy.get(".spectrum-Button").contains("Publish").click({ force : true })
+ cy.wait(1000)
+ });
+
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+ cy.get(".spectrum-Tabs-item").contains("Settings").click()
+ cy.get(".spectrum-Tabs-item.is-selected").contains("Settings")
+
+ cy.get(".details-section .page-action .spectrum-Button").scrollIntoView()
+ cy.wait(1000)
+ cy.get(".details-section .page-action .spectrum-Button").should("be.disabled")
+
+ })
+
+ it("Should allow copying of the published application Id", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .app-row-actions").eq(0)
+ .within(() => {
+ cy.get(".spectrum-Button").contains("Edit").click({ force: true })
+ })
+
+ cy.publishApp("sample-name")
+
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".app-overview-actions-icon > .icon").click({ force : true })
+
+ cy.get("[data-cy='app-overview-menu-popover']").eq(0).within(() => {
+ cy.get(".spectrum-Menu-item").contains("Copy App ID").click({ force: true })
+ })
+
+ cy.get(".spectrum-Toast-content").contains("App ID copied to clipboard.").should("be.visible")
+ })
+
+ it("Should allow unpublishing of the application", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".app-overview-actions-icon > .icon").click({ force : true })
+
+ cy.get("[data-cy='app-overview-menu-popover']").eq(0).within(() => {
+ cy.get(".spectrum-Menu-item").contains("Unpublish").click({ force: true })
+ cy.wait(500)
+ })
+
+ cy.get("[data-cy='unpublish-modal']").should("be.visible")
+ .within(() => {
+ cy.get(".confirm-wrap button").click({ force: true }
+ )})
+
+ cy.get(".overview-tab [data-cy='app-status']").within(() => {
+ cy.get(".status-display").contains("Unpublished")
+ cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should("exist")
+ })
+ })
+
+ it("Should allow deleting of the application", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".appTable .name").eq(0).click()
+
+ cy.get(".app-overview-actions-icon > .icon").click({ force : true })
+
+ cy.get("[data-cy='app-overview-menu-popover']").eq(0).within(() => {
+ cy.get(".spectrum-Menu-item").contains("Delete").click({ force: true })
+ cy.wait(500)
+ })
+
+ //The test application was renamed earlier in the spec
+ cy.get(".spectrum-Dialog-grid").within(() => {
+ cy.get("input").type("sample name")
+ cy.get(".spectrum-Button--warning").click()
+ })
+
+ cy.location().should((loc) => {
+ expect(loc.pathname).to.eq('/builder/portal/apps')
+ })
+
+ cy.get(".appTable").should("not.exist")
+
+ cy.get(".welcome .container h1").contains("Let's create your first app!")
+ })
+
+ after(() => {
+ cy.deleteAllApps()
+ })
+
+ })
+})
diff --git a/packages/builder/cypress/integration/appPublishWorkflow.spec.js b/packages/builder/cypress/integration/appPublishWorkflow.spec.js
index d18233e0e7..fb3c48645f 100644
--- a/packages/builder/cypress/integration/appPublishWorkflow.spec.js
+++ b/packages/builder/cypress/integration/appPublishWorkflow.spec.js
@@ -19,7 +19,7 @@ filterTests(['all'], () => {
cy.get(".appTable .app-row-actions").eq(0)
.within(() => {
- cy.get(".spectrum-Button").contains("Preview")
+ cy.get(".spectrum-Button").contains("View")
cy.get(".spectrum-Button").contains("Edit").click({ force: true })
})
@@ -29,22 +29,8 @@ filterTests(['all'], () => {
it("Should publish an application and correctly reflect that", () => {
//Assuming the previous test was run and the unpublished app is open in edit mode.
- cy.get(".toprightnav button.spectrum-Button").contains("Publish").click({ force : true })
- cy.get(".spectrum-Modal [data-cy='deploy-app-modal']").should("be.visible")
- .within(() => {
- cy.get(".spectrum-Button").contains("Publish").click({ force : true })
- cy.wait(1000)
- });
-
- //Verify that the app url is presented correctly to the user
- cy.get(".spectrum-Modal [data-cy='deploy-app-success-modal']")
- .should("be.visible")
- .within(() => {
- let appUrl = Cypress.config().baseUrl + '/app/cypress-tests'
- cy.get("[data-cy='deployed-app-url'] input").should('have.value', appUrl)
- cy.get(".spectrum-Button").contains("Done").click({ force: true })
- })
+ cy.publishApp("cypress-tests")
cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.wait(1000)
@@ -57,7 +43,7 @@ filterTests(['all'], () => {
cy.get(".appTable .app-row-actions").eq(0)
.within(() => {
- cy.get(".spectrum-Button").contains("View app")
+ cy.get(".spectrum-Button").contains("View")
cy.get(".spectrum-Button").contains("Edit").click({ force: true })
})
@@ -66,7 +52,7 @@ filterTests(['all'], () => {
cy.get("[data-cy='publish-popover-menu']").should("be.visible")
.within(() => {
cy.get("[data-cy='publish-popover-action']").should("exist")
- cy.get("button").contains("View app").should("exist")
+ cy.get("button").contains("View").should("exist")
cy.get(".publish-popover-message").should("have.text", "Last published a few seconds ago")
})
})
@@ -84,7 +70,7 @@ filterTests(['all'], () => {
cy.get(".appTable .app-row-actions").eq(0)
.within(() => {
- cy.get(".spectrum-Button").contains("View app")
+ cy.get(".spectrum-Button").contains("View")
cy.get(".spectrum-Button").contains("Edit").click({ force: true })
})
diff --git a/packages/builder/cypress/integration/renameAnApplication.spec.js b/packages/builder/cypress/integration/renameAnApplication.spec.js
index 120c0d54d7..7e611ac4ec 100644
--- a/packages/builder/cypress/integration/renameAnApplication.spec.js
+++ b/packages/builder/cypress/integration/renameAnApplication.spec.js
@@ -112,19 +112,9 @@ filterTests(['all'], () => {
cy.get("[data-cy='app-row-actions-menu-popover']").eq(0).within(() => {
cy.get(".spectrum-Menu-item").contains("Edit").click({ force: true })
})
- cy.get(".spectrum-Modal")
- .within(() => {
- if (noName == true) {
- cy.get("input").clear()
- cy.get(".spectrum-Dialog-grid").click()
- .contains("App name must be letters, numbers and spaces only")
- return cy
- }
- cy.get("input").clear()
- cy.get("input").eq(0).type(changedName).should("have.value", changedName).blur()
- cy.get(".spectrum-ButtonGroup").contains("Save").click({ force: true })
- cy.wait(500)
- })
- }
- })
+
+ cy.updateAppName(changedName, noName)
+
+ }
+ })
})
diff --git a/packages/builder/cypress/integration/templates/HR/hrTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/HR/hrTemplateDetails.spec.js
index 9a00645ced..fbac463bfe 100644
--- a/packages/builder/cypress/integration/templates/HR/hrTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/HR/hrTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter HR Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/HR/jobApplicationTracker.spec.js b/packages/builder/cypress/integration/templates/HR/jobApplicationTracker.spec.js
index ff6cb91bad..045a85d8f6 100644
--- a/packages/builder/cypress/integration/templates/HR/jobApplicationTracker.spec.js
+++ b/packages/builder/cypress/integration/templates/HR/jobApplicationTracker.spec.js
@@ -8,13 +8,13 @@ filterTests(["all"], () => {
before(() => {
cy.login()
cy.deleteApp(templateName)
- cy.visit(`${Cypress.config().baseUrl}/builder`, {
+ // Template navigation
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`, {
onBeforeLoad(win) {
cy.stub(win, 'open')
}
})
cy.wait(2000)
- cy.templateNavigation()
})
it("should create and publish app with Job Application Tracker template", () => {
@@ -35,19 +35,10 @@ filterTests(["all"], () => {
cy.get(".spectrum-Button").contains("Create app").click({ force: true })
})
- // Publish App
+ // Publish App & Verify it opened
cy.wait(2000) // Wait for app to generate
- cy.get(".toprightnav").contains("Publish").click({ force: true })
- cy.get(".spectrum-Dialog-grid").within(() => {
- cy.get(".spectrum-Button").contains("Publish").click({ force: true })
- })
-
- // Verify Published app
- cy.wait(2000) // Wait for App to publish and modal to appear
- cy.get(".spectrum-Dialog-grid").within(() => {
- cy.get(".spectrum-Button").contains("View App").click({ force: true })
- cy.window().its('open').should('be.calledOnce')
- })
+ cy.publishApp(true)
+ cy.window().its('open').should('be.calledOnce')
})
it("should add active/inactive vacancies", () => {
diff --git a/packages/builder/cypress/integration/templates/IT/ITTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/IT/ITTemplateDetails.spec.js
index baebeef60a..84cbc5707e 100644
--- a/packages/builder/cypress/integration/templates/IT/ITTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/IT/ITTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter IT Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/IT/ITTicketingSystem.spec.js b/packages/builder/cypress/integration/templates/IT/ITTicketingSystem.spec.js
index 118625ac65..15628ab131 100644
--- a/packages/builder/cypress/integration/templates/IT/ITTicketingSystem.spec.js
+++ b/packages/builder/cypress/integration/templates/IT/ITTicketingSystem.spec.js
@@ -8,13 +8,13 @@ filterTests(["all"], () => {
before(() => {
cy.login()
cy.deleteApp(templateName)
- cy.visit(`${Cypress.config().baseUrl}/builder`, {
+ // Template navigation
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`, {
onBeforeLoad(win) {
cy.stub(win, 'open')
}
})
cy.wait(2000)
- cy.templateNavigation()
})
it("should create and publish app with IT Ticketing System template", () => {
@@ -35,19 +35,10 @@ filterTests(["all"], () => {
cy.get(".spectrum-Button").contains("Create app").click({ force: true })
})
- // Publish App
+ // Publish App & Verify it opened
cy.wait(2000) // Wait for app to generate
- cy.get(".toprightnav").contains("Publish").click({ force: true })
- cy.get(".spectrum-Dialog-grid").within(() => {
- cy.get(".spectrum-Button").contains("Publish").click({ force: true })
- })
-
- // Verify Published app
- cy.wait(2000) // Wait for App to publish and modal to appear
- cy.get(".spectrum-Dialog-grid").within(() => {
- cy.get(".spectrum-Button").contains("View App").click({ force: true })
- cy.window().its('open').should('be.calledOnce')
- })
+ cy.publishApp(true)
+ cy.window().its('open').should('be.calledOnce')
})
xit("should filter tickets by status", () => {
diff --git a/packages/builder/cypress/integration/templates/adminPanels/adminPanelsTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/adminPanels/adminPanelsTemplateDetails.spec.js
index 8c76a01126..2fa57b2c89 100644
--- a/packages/builder/cypress/integration/templates/adminPanels/adminPanelsTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/adminPanels/adminPanelsTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Admin Panels Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/approvalApps/approvalAppsTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/approvalApps/approvalAppsTemplateDetails.spec.js
index 8fa0d61390..322a17f6c2 100644
--- a/packages/builder/cypress/integration/templates/approvalApps/approvalAppsTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/approvalApps/approvalAppsTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Approval Apps Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/businessApps/businessAppsTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/businessApps/businessAppsTemplateDetails.spec.js
index 643c854168..734fb9a968 100644
--- a/packages/builder/cypress/integration/templates/businessApps/businessAppsTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/businessApps/businessAppsTemplateDetails.spec.js
@@ -7,14 +7,8 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
-
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
+
// Filter Business Apps Templates
cy.get(".template-category-filters").within(() => {
cy.get('[data-cy="Business Apps"]').click()
diff --git a/packages/builder/cypress/integration/templates/directories/directoriesTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/directories/directoriesTemplateDetails.spec.js
index d763dab9e7..dc874fcbaf 100644
--- a/packages/builder/cypress/integration/templates/directories/directoriesTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/directories/directoriesTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Directories Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/forms/formsTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/forms/formsTemplateDetails.spec.js
index a8cb30da85..3206a71f6e 100644
--- a/packages/builder/cypress/integration/templates/forms/formsTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/forms/formsTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Forms Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/healthcare/healthcareTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/healthcare/healthcareTemplateDetails.spec.js
index 02b8c7694e..b46bb46274 100644
--- a/packages/builder/cypress/integration/templates/healthcare/healthcareTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/healthcare/healthcareTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Healthcare Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/legal/legalTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/legal/legalTemplateDetails.spec.js
index 16715bb5d6..57485aee40 100644
--- a/packages/builder/cypress/integration/templates/legal/legalTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/legal/legalTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Legal Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/logistics/logisticsTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/logistics/logisticsTemplateDetails.spec.js
index a56cc80a67..e5d5745e4e 100644
--- a/packages/builder/cypress/integration/templates/logistics/logisticsTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/logistics/logisticsTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Logistics Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/manufacturing/manufacturingTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/manufacturing/manufacturingTemplateDetails.spec.js
index b2809d78ee..30019c87fd 100644
--- a/packages/builder/cypress/integration/templates/manufacturing/manufacturingTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/manufacturing/manufacturingTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Manufacturing Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/marketing/leadGenerationForm.spec.js b/packages/builder/cypress/integration/templates/marketing/leadGenerationForm.spec.js
new file mode 100644
index 0000000000..9f08b36d56
--- /dev/null
+++ b/packages/builder/cypress/integration/templates/marketing/leadGenerationForm.spec.js
@@ -0,0 +1,44 @@
+import filterTests from "../../../support/filterTests"
+
+filterTests(["all"], () => {
+ context("Lead Generation Form Template Functionality", () => {
+ const templateName = "Lead Generation Form"
+ const templateNameParsed = templateName.toLowerCase().replace(/\s+/g, '-')
+
+ before(() => {
+ cy.login()
+ cy.deleteApp(templateName)
+ // Template navigation
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`, {
+ onBeforeLoad(win) {
+ cy.stub(win, 'open')
+ }
+ })
+ cy.wait(2000)
+ })
+
+ it("should create and publish app with Lead Generation Form template", () => {
+ // Select Lead Generation Form template
+ cy.get(".template-thumbnail-text")
+ .contains(templateName).parentsUntil(".template-grid").within(() => {
+ cy.get(".spectrum-Button").contains("Use template").click({ force: true })
+ })
+
+ // Confirm URL matches template name
+ const appUrl = cy.get(".app-server")
+ appUrl.invoke('text').then(appUrlText => {
+ expect(appUrlText).to.equal(`${Cypress.config().baseUrl}/app/` + templateNameParsed)
+ })
+
+ // Create App
+ cy.get(".spectrum-Dialog-grid").within(() => {
+ cy.get(".spectrum-Button").contains("Create app").click({ force: true })
+ })
+
+ // Publish App & Verify it opened
+ cy.wait(2000) // Wait for app to generate
+ cy.publishApp(true)
+ cy.window().its('open').should('be.calledOnce')
+ })
+ })
+})
diff --git a/packages/builder/cypress/integration/templates/marketing/marketingTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/marketing/marketingTemplateDetails.spec.js
index 2c8840f321..66875e6939 100644
--- a/packages/builder/cypress/integration/templates/marketing/marketingTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/marketing/marketingTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Marketing Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/operations/operationsTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/operations/operationsTemplateDetails.spec.js
index 2665de9e81..1a2ee1703a 100644
--- a/packages/builder/cypress/integration/templates/operations/operationsTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/operations/operationsTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Operations Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/integration/templates/portals/portalsTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/portals/portalsTemplateDetails.spec.js
index 5e26948180..e81e12318d 100644
--- a/packages/builder/cypress/integration/templates/portals/portalsTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/portals/portalsTemplateDetails.spec.js
@@ -7,21 +7,15 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
+
+ // Filter Portal Templates
+ cy.get(".template-category-filters").within(() => {
+ cy.get('[data-cy="Portal"]').click()
+ })
})
it("should verify the details option for Portal templates", () => {
- // Filter Portal Templates
- cy.get(".template-category-filters").within(() => {
- cy.get('[data-cy="Portal"]').click()
- })
-
cy.get(".template-grid").find(".template-card").its('length')
.then(len => {
for (let i = 0; i < len; i++) {
diff --git a/packages/builder/cypress/integration/templates/professionalServices/professionalServicesTemplateDetails.spec.js b/packages/builder/cypress/integration/templates/professionalServices/professionalServicesTemplateDetails.spec.js
index e8ed18a19c..1267d8bd5c 100644
--- a/packages/builder/cypress/integration/templates/professionalServices/professionalServicesTemplateDetails.spec.js
+++ b/packages/builder/cypress/integration/templates/professionalServices/professionalServicesTemplateDetails.spec.js
@@ -7,13 +7,7 @@ filterTests(["all"], () => {
cy.login()
// Template navigation
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({force: true})
- }
- })
+ cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/templates`)
// Filter Professional Services Templates
cy.get(".template-category-filters").within(() => {
diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js
index 6655219cd7..d50364fd54 100644
--- a/packages/builder/cypress/support/commands.js
+++ b/packages/builder/cypress/support/commands.js
@@ -32,10 +32,19 @@ Cypress.Commands.add("login", () => {
})
})
+Cypress.Commands.add("logOut", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.get(".user-dropdown .avatar > .icon").click({ force: true })
+ cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => {
+ cy.get("li[data-cy='user-logout']").click({ force: true })
+ })
+ cy.wait(2000)
+})
+
Cypress.Commands.add("closeModal", () => {
cy.get(".spectrum-Modal").within(() => {
cy.get(".close-icon").click()
- cy.wait(500)
+ cy.wait(1000) // Wait for modal to close
})
})
@@ -209,6 +218,109 @@ Cypress.Commands.add("deleteAllApps", () => {
})
})
+Cypress.Commands.add("customiseAppIcon", () => {
+ // Select random icon
+ cy.get(".grid").within(() => {
+ cy.get(".icon-item")
+ .eq(Math.floor(Math.random() * 23) + 1)
+ .click()
+ })
+ // Select random colour
+ cy.get(".fill").click()
+ cy.get(".colors").within(() => {
+ cy.get(".color")
+ .eq(Math.floor(Math.random() * 33) + 1)
+ .click()
+ })
+ cy.intercept("**/applications/**").as("iconChange")
+ cy.get(".spectrum-Button").contains("Save").click({ force: true })
+ cy.wait("@iconChange")
+ cy.get("@iconChange").its("response.statusCode").should("eq", 200)
+ cy.wait(1000)
+})
+
+Cypress.Commands.add("alterAppVersion", (appId, version) => {
+ return cy
+ .request("put", `${Cypress.config().baseUrl}/api/applications/${appId}`, {
+ version: version || "0.0.1-alpha.0",
+ })
+ .then(resp => {
+ expect(resp.status).to.eq(200)
+ })
+})
+
+Cypress.Commands.add("updateAppName", (changedName, noName) => {
+ cy.get(".spectrum-Modal").within(() => {
+ if (noName == true) {
+ cy.get("input").clear()
+ cy.get(".spectrum-Dialog-grid")
+ .click()
+ .contains("App name must be letters, numbers and spaces only")
+ return cy
+ }
+ cy.get("input").clear()
+ cy.get("input")
+ .eq(0)
+ .type(changedName)
+ .should("have.value", changedName)
+ .blur()
+ cy.get(".spectrum-ButtonGroup").contains("Save").click({ force: true })
+ cy.wait(500)
+ })
+})
+
+Cypress.Commands.add("unlockApp", unlock_config => {
+ let config = { ...unlock_config }
+
+ cy.get(".spectrum-Modal .spectrum-Dialog[data-cy='app-lock-modal']")
+ .should("be.visible")
+ .within(() => {
+ if (config.owned) {
+ cy.get(".spectrum-Dialog-heading").contains("Locked by you")
+ cy.get(".lock-expiry-body").contains(
+ "This lock will expire in 10 minutes from now"
+ )
+
+ cy.intercept("**/lock").as("unlockApp")
+ cy.get(".spectrum-Button")
+ .contains("Release Lock")
+ .click({ force: true })
+ cy.wait("@unlockApp")
+ cy.get("@unlockApp").its("response.statusCode").should("eq", 200)
+ cy.get("@unlockApp").its("response.body").should("deep.equal", {
+ message: "Lock released successfully.",
+ })
+ } else {
+ //Show the name ?
+ cy.get(".lock-expiry-body").should("not.be.visible")
+ cy.get(".spectrum-Button").contains("Done")
+ }
+ })
+})
+
+Cypress.Commands.add("publishApp", resolvedAppPath => {
+ //Assumes you have navigated to an application first
+ cy.get(".toprightnav button.spectrum-Button")
+ .contains("Publish")
+ .click({ force: true })
+
+ cy.get(".spectrum-Modal [data-cy='deploy-app-modal']")
+ .should("be.visible")
+ .within(() => {
+ cy.get(".spectrum-Button").contains("Publish").click({ force: true })
+ cy.wait(1000)
+ })
+
+ //Verify that the app url is presented correctly to the user
+ cy.get(".spectrum-Modal [data-cy='deploy-app-success-modal']")
+ .should("be.visible")
+ .within(() => {
+ let appUrl = Cypress.config().baseUrl + "/app/" + resolvedAppPath
+ cy.get("[data-cy='deployed-app-url'] input").should("have.value", appUrl)
+ cy.get(".spectrum-Button").contains("Done").click({ force: true })
+ })
+})
+
Cypress.Commands.add("createTestApp", () => {
const appName = "Cypress Tests"
cy.deleteApp(appName)
@@ -222,6 +334,21 @@ Cypress.Commands.add("createTestTableWithData", () => {
cy.addColumn("dog", "age", "Number")
})
+Cypress.Commands.add("publishApp", (viewApp = false) => {
+ cy.get(".toprightnav").contains("Publish").click({ force: true })
+ cy.get(".spectrum-Dialog-grid").within(() => {
+ cy.get(".spectrum-Button").contains("Publish").click({ force: true })
+ })
+ cy.wait(2000) // Wait for App to publish and modal to appear
+ cy.get(".spectrum-Dialog-grid").within(() => {
+ if (viewApp) {
+ cy.get(".spectrum-Button").contains("View App").click({ force: true })
+ } else {
+ cy.get(".spectrum-Button").contains("Done").click({ force: true })
+ }
+ })
+})
+
Cypress.Commands.add("createTable", (tableName, initialTable) => {
if (!initialTable) {
cy.navigateToDataSection()
@@ -671,15 +798,3 @@ Cypress.Commands.add("createRestQuery", (method, restUrl, queryPrettyName) => {
.should("contain", method)
.and("contain", queryPrettyName)
})
-
-Cypress.Commands.add("templateNavigation", () => {
- // Navigates to templates section
- cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
- .its("body")
- .then(val => {
- // Templates button needs clicked if apps already exist
- if (val.length > 0) {
- cy.get(".spectrum-Button").contains("Templates").click({ force: true })
- }
- })
-})
diff --git a/packages/builder/package.json b/packages/builder/package.json
index 999de12d99..9dcda69a53 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
- "version": "1.0.167-alpha.8",
+ "version": "1.0.189",
"license": "GPL-3.0",
"private": true,
"scripts": {
@@ -69,10 +69,10 @@
}
},
"dependencies": {
- "@budibase/bbui": "^1.0.167-alpha.8",
- "@budibase/client": "^1.0.167-alpha.8",
- "@budibase/frontend-core": "^1.0.167-alpha.8",
- "@budibase/string-templates": "^1.0.167-alpha.8",
+ "@budibase/bbui": "^1.0.189",
+ "@budibase/client": "^1.0.189",
+ "@budibase/frontend-core": "^1.0.189",
+ "@budibase/string-templates": "^1.0.189",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js
index eac99335c9..2b57f212d4 100644
--- a/packages/builder/src/builderStore/store/automation/index.js
+++ b/packages/builder/src/builderStore/store/automation/index.js
@@ -105,9 +105,7 @@ const automationActions = store => ({
},
select: automation => {
store.update(state => {
- let testResults = state.selectedAutomation?.testResults
state.selectedAutomation = new Automation(cloneDeep(automation))
- state.selectedAutomation.testResults = testResults
state.selectedBlock = null
return state
})
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
index 505a0b9aca..3e58b25ff6 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
@@ -14,7 +14,6 @@
} from "@budibase/bbui"
export let automation
-
let testDataModal
let blocks
let confirmDeleteDialog
@@ -41,66 +40,70 @@
-
-
-
-
{automation.name}
-
-
+
+ {automation.name}
+
+
+
+
+
+
+
+
{
+ testDataModal.show()
+ }}
+ icon="MultipleCheck"
+ size="M">Run test
+
{
- testDataModal.show()
+ $automationStore.selectedAutomation.automation.showTestPanel = true
}}
- icon="MultipleCheck"
- size="M">Run testTest Details
- {#each blocks as block, idx (block.id)}
-
- {#if block.stepId !== "LOOP"}
-
- {/if}
-
- {/each}
-
- Are you sure you wish to delete the automation
- {automation.name}?
- This action cannot be undone.
-
-
-
-
-
+
+ {#each blocks as block, idx (block.id)}
+
+ {#if block.stepId !== "LOOP"}
+
+ {/if}
+
+ {/each}
+
+
+ Are you sure you wish to delete the automation
+ {automation.name}?
+ This action cannot be undone.
+
+
+
+
+
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ResultsModal.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ResultsModal.svelte
deleted file mode 100644
index 9662bc8ade..0000000000
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ResultsModal.svelte
+++ /dev/null
@@ -1,133 +0,0 @@
-
-
-
-
-
- {#if testResult[0].outputs.iterations}
-
-
-
-
-
-
- {/if}
-
- {
- inputToggled = !inputToggled
- }}
- class="toggle splitHeader"
- >
-
-
- {#if inputToggled}
-
- {:else}
-
- {/if}
-
-
- {#if inputToggled}
-
-
-
- {/if}
-
- {
- outputToggled = !outputToggled
- }}
- class="toggle splitHeader"
- >
-
-
- {#if outputToggled}
-
- {:else}
-
- {/if}
-
-
- {#if outputToggled}
-
-
-
- {/if}
-
-
-
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte
index ffd59b4e6a..fecd0fcc7e 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte
@@ -51,6 +51,7 @@
$automationStore.selectedAutomation?.automation,
testData
)
+ $automationStore.selectedAutomation.automation.showTestPanel = true
} catch (error) {
notifications.error("Error testing notification")
}
diff --git a/packages/builder/src/components/automation/AutomationBuilder/TestPanel.svelte b/packages/builder/src/components/automation/AutomationBuilder/TestPanel.svelte
new file mode 100644
index 0000000000..8c38d8a689
--- /dev/null
+++ b/packages/builder/src/components/automation/AutomationBuilder/TestPanel.svelte
@@ -0,0 +1,146 @@
+
+
+
+
+
+ {
+ $automationStore.selectedAutomation.automation.showTestPanel = false
+ }}
+ hoverable
+ name="Close"
+ />
+
+
+
+
+
+
+ {#each blocks as block, idx}
+
+ {#if block.stepId !== "LOOP"}
+
+ {#if showParameters && showParameters[block.id]}
+
+ {#if testResults?.[idx]?.outputs.iterations}
+
+
+
+
+
+
+ {/if}
+
+
+ {/if}
+ {/if}
+
+ {#if blocks.length - 1 !== idx}
+
+ {/if}
+ {/each}
+
+
+
diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte
index aa3915de58..62a367ea7d 100644
--- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte
+++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte
@@ -304,7 +304,9 @@
)
}
const newError = {}
- if (PROHIBITED_COLUMN_NAMES.some(name => fieldInfo.name === name)) {
+ if (!external && fieldInfo.name?.startsWith("_")) {
+ newError.name = `Column name cannot start with an underscore.`
+ } else if (PROHIBITED_COLUMN_NAMES.some(name => fieldInfo.name === name)) {
newError.name = `${PROHIBITED_COLUMN_NAMES.join(
", "
)} are not allowed as column names`
diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte
index 556ce7d3fc..5c96445c4c 100644
--- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte
+++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte
@@ -45,6 +45,8 @@
name,
schema: addAutoColumns(name, dataImport.schema || {}),
dataImport,
+ type: "internal",
+ sourceId: "bb_internal",
}
// Only set primary display if defined
diff --git a/packages/builder/src/components/common/AppLockModal.svelte b/packages/builder/src/components/common/AppLockModal.svelte
new file mode 100644
index 0000000000..75e2b15925
--- /dev/null
+++ b/packages/builder/src/components/common/AppLockModal.svelte
@@ -0,0 +1,144 @@
+
+
+
+ {#if lockedBy}
+
+ {/if}
+
+
+
+
+
+ Apps are locked to prevent work from being lost from overlapping changes
+ between your team.
+
+
+ {#if lockedByYou && getExpiryDuration(app) > 0}
+
+ {processStringSync(
+ "This lock will expire in {{ duration time 'millisecond' }} from now",
+ {
+ time: getExpiryDuration(app),
+ }
+ )}
+
+ {/if}
+
+
+
+ {#if lockedByYou}
+
+ {/if}
+
+
+
+
+
+
diff --git a/packages/builder/src/components/common/DashCard.svelte b/packages/builder/src/components/common/DashCard.svelte
new file mode 100644
index 0000000000..d5d9d2ff37
--- /dev/null
+++ b/packages/builder/src/components/common/DashCard.svelte
@@ -0,0 +1,55 @@
+
+
+
+
+
diff --git a/packages/builder/src/components/common/EditableIcon.svelte b/packages/builder/src/components/common/EditableIcon.svelte
new file mode 100644
index 0000000000..313d1b88b0
--- /dev/null
+++ b/packages/builder/src/components/common/EditableIcon.svelte
@@ -0,0 +1,47 @@
+
+
+
+
{
+ iconModal.show()
+ }}
+ >
+
+
+
+
+
+
+
+
+
diff --git a/packages/builder/src/components/deploy/VersionModal.svelte b/packages/builder/src/components/deploy/VersionModal.svelte
index 9707517c54..3ddc7a1115 100644
--- a/packages/builder/src/components/deploy/VersionModal.svelte
+++ b/packages/builder/src/components/deploy/VersionModal.svelte
@@ -11,6 +11,16 @@
import { API } from "api"
import clientPackage from "@budibase/client/package.json"
+ export function show() {
+ updateModal.show()
+ }
+
+ export function hide() {
+ updateModal.hide()
+ }
+
+ export let hideIcon = false
+
let updateModal
$: appId = $store.appId
@@ -57,9 +67,11 @@
}
-
-
-
+{#if !hideIcon}
+
+
+
+{/if}
- import {
- Heading,
- Button,
- Icon,
- ActionMenu,
- MenuItem,
- StatusLight,
- } from "@budibase/bbui"
+ import { Heading, Button, Icon, ActionMenu, MenuItem } from "@budibase/bbui"
+ import AppLockModal from "../common/AppLockModal.svelte"
import { processStringSync } from "@budibase/string-templates"
export let app
export let exportApp
- export let viewApp
export let editApp
export let updateApp
export let deleteApp
- export let previewApp
export let unpublishApp
+ export let appOverview
export let releaseLock
export let editIcon
export let copyAppId
-
+
-
editApp(app)}>
+
appOverview(app)}>
{app.name}
@@ -44,19 +37,7 @@
{/if}
-
- {#if app.lockedYou}
- Locked by you
- {:else if app.lockedOther}
- Locked by {app.lockedBy.email}
- {:else}
- Open
- {/if}
-
+
@@ -71,23 +52,15 @@
- {#if app.deployed}
-
- {:else}
-
- {/if}
+
@@ -123,6 +96,7 @@
}
.app-status {
display: grid;
+ grid-gap: var(--spacing-s);
grid-template-columns: 24px 100px;
}
.app-status span.disabled {
diff --git a/packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte
index 841acb22c0..a713067bbe 100644
--- a/packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte
@@ -4,7 +4,12 @@
import AutomationPanel from "components/automation/AutomationPanel/AutomationPanel.svelte"
import CreateAutomationModal from "components/automation/AutomationPanel/CreateAutomationModal.svelte"
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
- $: automation = $automationStore.automations[0]
+ import TestPanel from "components/automation/AutomationBuilder/TestPanel.svelte"
+
+ $: automation =
+ $automationStore.selectedAutomation?.automation ||
+ $automationStore.automations[0]
+
let modal
let webhookModal
@@ -39,6 +44,12 @@
{/if}
+
+ {#if automation?.showTestPanel}
+
+
+
+ {/if}
@@ -52,7 +63,9 @@
flex: 1 1 auto;
height: 0;
display: grid;
- grid-template-columns: 260px minmax(510px, 1fr);
+ grid-auto-flow: column dense;
+ grid-template-columns: 260px minmax(510px, 1fr) fit-content(500px);
+ overflow: hidden;
}
.nav {
@@ -64,17 +77,18 @@
border-right: var(--border-light);
background-color: var(--background);
padding-bottom: 60px;
+ overflow: hidden;
}
.content {
position: relative;
- padding: var(--spacing-l) 40px;
+ padding-top: var(--spacing-l);
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-l);
- overflow: hidden;
+ overflow: auto;
}
.centered {
top: 0;
@@ -92,4 +106,17 @@
.main {
width: 300px;
}
+
+ .setup {
+ padding-top: var(--spectrum-global-dimension-size-200);
+ border-left: var(--border-light);
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: stretch;
+ gap: var(--spacing-l);
+ background-color: var(--background);
+ grid-column: 3;
+ overflow: auto;
+ }
diff --git a/packages/builder/src/pages/builder/auth/login.svelte b/packages/builder/src/pages/builder/auth/login.svelte
index d9151b4342..c2ec547e38 100644
--- a/packages/builder/src/pages/builder/auth/login.svelte
+++ b/packages/builder/src/pages/builder/auth/login.svelte
@@ -28,7 +28,7 @@
async function login() {
try {
await auth.login({
- username,
+ username: username.trim(),
password,
})
if ($auth?.user?.forceResetPassword) {
@@ -80,7 +80,9 @@
/>
-
+
$goto("./forgot")}>
Forgot password?
diff --git a/packages/builder/src/pages/builder/portal/_layout.svelte b/packages/builder/src/pages/builder/portal/_layout.svelte
index 17100987d4..ae0362af72 100644
--- a/packages/builder/src/pages/builder/portal/_layout.svelte
+++ b/packages/builder/src/pages/builder/portal/_layout.svelte
@@ -203,7 +203,9 @@
-
+
@@ -332,7 +334,7 @@
.mobile-toggle,
.user-dropdown {
- flex: 1 1 0;
+ flex: 0 1 0;
}
/* Reduce BBUI page padding */
diff --git a/packages/builder/src/pages/builder/portal/apps/create.svelte b/packages/builder/src/pages/builder/portal/apps/create.svelte
index cd2d47ea4c..4fd69bc815 100644
--- a/packages/builder/src/pages/builder/portal/apps/create.svelte
+++ b/packages/builder/src/pages/builder/portal/apps/create.svelte
@@ -9,6 +9,7 @@
Body,
Modal,
Divider,
+ ActionButton,
} from "@budibase/bbui"
import CreateAppModal from "components/start/CreateAppModal.svelte"
import TemplateDisplay from "components/common/TemplateDisplay.svelte"
@@ -60,16 +61,15 @@
-
+
diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte
index dc7ce9c57a..946ae03446 100644
--- a/packages/builder/src/pages/builder/portal/apps/index.svelte
+++ b/packages/builder/src/pages/builder/portal/apps/index.svelte
@@ -2,7 +2,6 @@
import {
Heading,
Layout,
- Detail,
Button,
Input,
Select,
@@ -11,7 +10,6 @@
notifications,
Body,
Search,
- Divider,
Helpers,
} from "@budibase/bbui"
import TemplateDisplay from "components/common/TemplateDisplay.svelte"
@@ -67,6 +65,9 @@
app?.name?.toLowerCase().includes(searchTerm.toLowerCase())
)
+ $: lockedApps = filteredApps.filter(app => app?.lockedYou || app?.lockedOther)
+ $: unlocked = lockedApps?.length == 0
+
const enrichApps = (apps, user, sortBy) => {
const enrichedApps = apps.map(app => ({
...app,
@@ -180,8 +181,8 @@
}
}
- const previewApp = app => {
- window.open(`/${app.devId}`)
+ const appOverview = app => {
+ $goto(`../overview/${app.devId}`)
}
const editApp = app => {
@@ -305,7 +306,7 @@
-
+
{#if loaded}
@@ -315,29 +316,17 @@
{welcomeBody}