From 83a91a8943502748a233f22a83e32a1b93ff16ba Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 6 Jul 2021 15:53:52 +0100 Subject: [PATCH 01/20] Fix bug with tabs causing them to broadcast a change event on mount --- packages/bbui/src/Tabs/Tabs.svelte | 8 ++++++-- .../src/pages/builder/portal/manage/_layout.svelte | 2 -- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/bbui/src/Tabs/Tabs.svelte b/packages/bbui/src/Tabs/Tabs.svelte index 3e1080f2cd..77a8526a15 100644 --- a/packages/bbui/src/Tabs/Tabs.svelte +++ b/packages/bbui/src/Tabs/Tabs.svelte @@ -15,8 +15,12 @@ const dispatch = createEventDispatcher() - $: selected = $tab.title - $: selected = dispatch("select", selected) + $: { + if ($tab.title !== selected) { + selected = $tab.title + dispatch("select", selected) + } + } let top, left, width, height $: calculateIndicatorLength($tab) diff --git a/packages/builder/src/pages/builder/portal/manage/_layout.svelte b/packages/builder/src/pages/builder/portal/manage/_layout.svelte index 98ae140b25..e6c73bc596 100644 --- a/packages/builder/src/pages/builder/portal/manage/_layout.svelte +++ b/packages/builder/src/pages/builder/portal/manage/_layout.svelte @@ -9,8 +9,6 @@ $redirect("../") } } - - $: console.log($page) {#if $auth.isAdmin} From 4bc1938966bfa20ceed4e7482fdbec06530e58d2 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 6 Jul 2021 12:01:01 +0100 Subject: [PATCH 02/20] Fix data provider filtering on datetime fields --- packages/standard-components/src/lucene.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/standard-components/src/lucene.js b/packages/standard-components/src/lucene.js index 8cbd1ea0fe..91c69dfda2 100644 --- a/packages/standard-components/src/lucene.js +++ b/packages/standard-components/src/lucene.js @@ -13,9 +13,12 @@ export const buildLuceneQuery = filter => { notEmpty: {}, } if (Array.isArray(filter)) { - // Build up proper range filters filter.forEach(expression => { - const { operator, field, type, value } = expression + let { operator, field, type, value } = expression + // Ensure date fields are transformed into ISO strings + if (type === "datetime" && value) { + value = new Date(value).toISOString() + } if (operator.startsWith("range")) { if (!query.range[field]) { query.range[field] = { From c4b7428aedecba351b2023fb70aa9c27987ebb16 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 6 Jul 2021 15:22:01 +0100 Subject: [PATCH 03/20] Avoid filter editor sending a prop change when filter is not an array, which was causing extra 409s and causing issues with tables --- packages/builder/src/builderStore/store/frontend.js | 3 +++ .../PropertyControls/FilterEditor/FilterEditor.svelte | 11 +---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index a462167ce2..edad0fdff5 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -122,6 +122,9 @@ export const getFrontendStore = () => { save: async screen => { const creatingNewScreen = screen._id === undefined const response = await api.post(`/api/screens`, screen) + if (response.status !== 200) { + return + } screen = await response.json() await store.actions.routing.fetch() diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte index eee8fb113c..dcda6ac1f1 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte @@ -21,7 +21,7 @@ export let value = [] export let componentInstance let drawer - let tempValue = value + let tempValue = value || [] $: numFilters = Array.isArray(tempValue) ? tempValue.length @@ -31,15 +31,6 @@ $: schemaFields = Object.values(schema || {}) $: internalTable = dataSource?.type === "table" - // Reset value if value is wrong type for the datasource. - // Lucene editor needs an array, and simple editor needs an object. - $: { - if (!Array.isArray(value)) { - tempValue = [] - dispatch("change", []) - } - } - const saveFilter = async () => { dispatch("change", tempValue) notifications.success("Filters saved.") From bd6de7fe31988d15a185602c9910d641778d8e63 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 6 Jul 2021 17:00:44 +0100 Subject: [PATCH 04/20] Remove redirect when user only has access to one app --- packages/builder/src/pages/builder/apps/index.svelte | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/builder/src/pages/builder/apps/index.svelte b/packages/builder/src/pages/builder/apps/index.svelte index 0296f2e5b7..69b6e770f6 100644 --- a/packages/builder/src/pages/builder/apps/index.svelte +++ b/packages/builder/src/pages/builder/apps/index.svelte @@ -28,12 +28,7 @@ onMount(async () => { await organisation.init() await apps.load() - // Skip the portal if you only have one app - if (!$auth.isBuilder && $apps.filter(publishedAppsOnly).length === 1) { - window.location = `/${publishedApps[0].prodId}` - } else { - loaded = true - } + loaded = true }) const publishedAppsOnly = app => app.status === AppStatus.DEPLOYED From 4e8b140c524c0cbd88aed35f5a110eef2762ab28 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 6 Jul 2021 17:13:02 +0100 Subject: [PATCH 05/20] default users without roles to public role --- packages/auth/src/security/roles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auth/src/security/roles.js b/packages/auth/src/security/roles.js index 53e1b90d73..e7055c9147 100644 --- a/packages/auth/src/security/roles.js +++ b/packages/auth/src/security/roles.js @@ -226,7 +226,7 @@ exports.getAllRoles = async appId => { dbRole => exports.getExternalRoleID(dbRole._id) === builtinRoleId )[0] if (dbBuiltin == null) { - roles.push(builtinRole) + roles.push(builtinRole || builtinRoles.PUBLIC) } else { // remove role and all back after combining with the builtin roles = roles.filter(role => role._id !== dbBuiltin._id) From 561f8246060b8f9f2cfe0ed7d251c55426227f38 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 6 Jul 2021 17:19:03 +0100 Subject: [PATCH 06/20] default to public in UI --- packages/auth/src/security/roles.js | 2 +- .../src/pages/builder/portal/manage/users/[userId].svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/auth/src/security/roles.js b/packages/auth/src/security/roles.js index e7055c9147..53e1b90d73 100644 --- a/packages/auth/src/security/roles.js +++ b/packages/auth/src/security/roles.js @@ -226,7 +226,7 @@ exports.getAllRoles = async appId => { dbRole => exports.getExternalRoleID(dbRole._id) === builtinRoleId )[0] if (dbBuiltin == null) { - roles.push(builtinRole || builtinRoles.PUBLIC) + roles.push(builtinRole) } else { // remove role and all back after combining with the builtin roles = roles.filter(role => role._id !== dbBuiltin._id) diff --git a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte index ac5b569411..8b0591f575 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte @@ -33,7 +33,7 @@ role: {}, } - $: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : "" + $: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : "PUBLIC" // Merge the Apps list and the roles response to get something that makes sense for the table $: appList = Object.keys($apps?.data).map(id => { const role = $userFetch?.data?.roles?.[id] || defaultRoleId From 6af078a54682f5f565f34ed5319b1d74dd0f085e Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 6 Jul 2021 17:19:53 +0100 Subject: [PATCH 07/20] public role in builder backend UI --- packages/auth/src/security/roles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auth/src/security/roles.js b/packages/auth/src/security/roles.js index 53e1b90d73..e7055c9147 100644 --- a/packages/auth/src/security/roles.js +++ b/packages/auth/src/security/roles.js @@ -226,7 +226,7 @@ exports.getAllRoles = async appId => { dbRole => exports.getExternalRoleID(dbRole._id) === builtinRoleId )[0] if (dbBuiltin == null) { - roles.push(builtinRole) + roles.push(builtinRole || builtinRoles.PUBLIC) } else { // remove role and all back after combining with the builtin roles = roles.filter(role => role._id !== dbBuiltin._id) From 038120485559ddb6277d5bc96a22223f2568150c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 6 Jul 2021 18:37:06 +0100 Subject: [PATCH 08/20] Don't reload client app on 403, just show an error instead --- packages/client/src/api/api.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/client/src/api/api.js b/packages/client/src/api/api.js index fcdd9dbd5b..280b580164 100644 --- a/packages/client/src/api/api.js +++ b/packages/client/src/api/api.js @@ -43,10 +43,9 @@ const makeApiCall = async ({ method, url, body, json = true }) => { case 400: return handleError(`${url}: Bad Request`) case 403: - // reload the page incase the token has expired - if (!url.includes("self")) { - location.reload() - } + notificationStore.danger( + "Your session has expired, or you don't have permission to access that data" + ) return handleError(`${url}: Forbidden`) default: if (response.status >= 200 && response.status < 400) { From eb620ea3ce44eaae27ffcbf08913cedb90e73af2 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 6 Jul 2021 18:37:21 +0100 Subject: [PATCH 09/20] Add basic fallback error to real apps when client library fails to load --- .../static/templates/BudibaseApp.svelte | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte b/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte index 670a0f22e2..4943051328 100644 --- a/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte +++ b/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte @@ -34,13 +34,55 @@ *:after { box-sizing: border-box; } + + #error { + position: absolute; + top: 0; + left: 0; + height: 100vh; + width: 100vw; + display: none; + font-family: "Source Sans Pro", sans-serif; + flex-direction: column; + justify-content: center; + align-items: center; + background: #222; + text-align: center; + padding: 2rem; + gap: 2rem; + } + #error h1, + #error h2 { + margin: 0; + } + #error h1 { + color: #ccc; + font-size: 3rem; + font-weight: 600; + } + #error h2 { + color: #888; + font-weight: 400; + } +
+

There was an error loading your app

+

+ The Budibase client library could not be loaded. Try republishing your + app. +

+
From 93b562e2df5a507c3bdca127c16b97fdfb20e828 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 6 Jul 2021 18:43:04 +0100 Subject: [PATCH 10/20] make logged in users basic by default, prevent allowing users to be assigned as default in the UI --- packages/auth/src/security/roles.js | 4 ++-- .../src/pages/builder/portal/manage/users/[userId].svelte | 2 +- .../portal/manage/users/_components/UpdateRolesModal.svelte | 2 +- packages/server/src/middleware/currentapp.js | 4 ++-- packages/server/src/utilities/global.js | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/auth/src/security/roles.js b/packages/auth/src/security/roles.js index e7055c9147..baa8fc40dc 100644 --- a/packages/auth/src/security/roles.js +++ b/packages/auth/src/security/roles.js @@ -147,7 +147,7 @@ exports.getRole = async (appId, roleId) => { */ async function getAllUserRoles(appId, userRoleId) { if (!userRoleId) { - return [BUILTIN_IDS.PUBLIC] + return [BUILTIN_IDS.BASIC] } let currentRole = await exports.getRole(appId, userRoleId) let roles = currentRole ? [currentRole] : [] @@ -226,7 +226,7 @@ exports.getAllRoles = async appId => { dbRole => exports.getExternalRoleID(dbRole._id) === builtinRoleId )[0] if (dbBuiltin == null) { - roles.push(builtinRole || builtinRoles.PUBLIC) + roles.push(builtinRole || builtinRoles.BASIC) } else { // remove role and all back after combining with the builtin roles = roles.filter(role => role._id !== dbBuiltin._id) diff --git a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte index 8b0591f575..27cf266c5d 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte @@ -33,7 +33,7 @@ role: {}, } - $: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : "PUBLIC" + $: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : "BASIC" // Merge the Apps list and the roles response to get something that makes sense for the table $: appList = Object.keys($apps?.data).map(id => { const role = $userFetch?.data?.roles?.[id] || defaultRoleId diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte index 08e4a2ec8b..436ba28bba 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte @@ -9,7 +9,7 @@ const dispatch = createEventDispatcher() const roles = app.roles - let options = roles.map(role => role._id) + let options = roles.map(role => role._id).filter(id => id !== "PUBLIC") let selectedRole = user?.roles?.[app?._id] async function updateUserRoles() { diff --git a/packages/server/src/middleware/currentapp.js b/packages/server/src/middleware/currentapp.js index 683b7f8ef3..0e9591456c 100644 --- a/packages/server/src/middleware/currentapp.js +++ b/packages/server/src/middleware/currentapp.js @@ -45,10 +45,10 @@ module.exports = async (ctx, next) => { updateCookie = true appId = requestAppId // retrieving global user gets the right role - roleId = globalUser.roleId || BUILTIN_ROLE_IDS.PUBLIC + roleId = globalUser.roleId || BUILTIN_ROLE_IDS.BASIC } else if (appCookie != null) { appId = appCookie.appId - roleId = appCookie.roleId || BUILTIN_ROLE_IDS.PUBLIC + roleId = appCookie.roleId || BUILTIN_ROLE_IDS.BASIC } // nothing more to do if (!appId) { diff --git a/packages/server/src/utilities/global.js b/packages/server/src/utilities/global.js index 17ce066551..eddbd63cd7 100644 --- a/packages/server/src/utilities/global.js +++ b/packages/server/src/utilities/global.js @@ -19,7 +19,7 @@ exports.updateAppRole = (appId, user) => { if (!user.roleId && user.builder && user.builder.global) { user.roleId = BUILTIN_ROLE_IDS.ADMIN } else if (!user.roleId) { - user.roleId = BUILTIN_ROLE_IDS.PUBLIC + user.roleId = BUILTIN_ROLE_IDS.BASIC } delete user.roles return user From da800dd3fc72d62a3a39b97a75512bc307a5742c Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 6 Jul 2021 18:54:08 +0100 Subject: [PATCH 11/20] prevent editing public role in apps --- .../src/components/backend/DataTable/modals/EditRoles.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte index f58b9f197f..0ee4553f37 100644 --- a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte @@ -10,6 +10,8 @@ let selectedRole = {} let errors = [] let builtInRoles = ["Admin", "Power", "Basic", "Public"] + // Don't allow editing of public role + $: allRoles = $roles.filter(role => role._id !== "PUBLIC") $: selectedRoleId = selectedRole._id $: otherRoles = $roles.filter(role => role._id !== selectedRoleId) $: isCreating = selectedRoleId == null || selectedRoleId === "" @@ -96,7 +98,7 @@ label="Role" value={selectedRoleId} on:change={changeRole} - options={$roles} + options={allRoles} placeholder="Create new role" getOptionValue={role => role._id} getOptionLabel={role => role.name} From dbef8077f0d2a769cfcc8739924e1cba8096a538 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 6 Jul 2021 18:55:34 +0100 Subject: [PATCH 12/20] better naming, filter out public from inheritable roles --- .../components/backend/DataTable/modals/EditRoles.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte index 0ee4553f37..059bd1105d 100644 --- a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte @@ -11,9 +11,9 @@ let errors = [] let builtInRoles = ["Admin", "Power", "Basic", "Public"] // Don't allow editing of public role - $: allRoles = $roles.filter(role => role._id !== "PUBLIC") + $: editableRoles = $roles.filter(role => role._id !== "PUBLIC") $: selectedRoleId = selectedRole._id - $: otherRoles = $roles.filter(role => role._id !== selectedRoleId) + $: otherRoles = $editableRoles.filter(role => role._id !== selectedRoleId) $: isCreating = selectedRoleId == null || selectedRoleId === "" const fetchBasePermissions = async () => { @@ -98,7 +98,7 @@ label="Role" value={selectedRoleId} on:change={changeRole} - options={allRoles} + options={editableRoles} placeholder="Create new role" getOptionValue={role => role._id} getOptionLabel={role => role.name} From e9d6ddb57135f6237a61a97dc34b7014cc1ed3f4 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 6 Jul 2021 18:56:41 +0100 Subject: [PATCH 13/20] typo --- .../src/components/backend/DataTable/modals/EditRoles.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte index 059bd1105d..8b7417c41f 100644 --- a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte @@ -13,7 +13,7 @@ // Don't allow editing of public role $: editableRoles = $roles.filter(role => role._id !== "PUBLIC") $: selectedRoleId = selectedRole._id - $: otherRoles = $editableRoles.filter(role => role._id !== selectedRoleId) + $: otherRoles = editableRoles.filter(role => role._id !== selectedRoleId) $: isCreating = selectedRoleId == null || selectedRoleId === "" const fetchBasePermissions = async () => { From 8f97ea37710f30c0514fbb3c9a4c7bf25c753657 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 7 Jul 2021 09:28:11 +0100 Subject: [PATCH 14/20] Swap row and column icons --- packages/standard-components/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/standard-components/manifest.json b/packages/standard-components/manifest.json index 28b82fe67d..1fb470dfc3 100644 --- a/packages/standard-components/manifest.json +++ b/packages/standard-components/manifest.json @@ -73,13 +73,13 @@ { "label": "Column", "value": "column", - "barIcon": "ViewRow", + "barIcon": "ViewColumn", "barTitle": "Column layout" }, { "label": "Row", "value": "row", - "barIcon": "ViewColumn", + "barIcon": "ViewRow", "barTitle": "Row layout" } ], From e00a37d707862abf766c34ec61c73fe2a0a36e56 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 7 Jul 2021 09:34:40 +0100 Subject: [PATCH 15/20] Only clean up app tmp dir if it exists during testing --- packages/server/src/utilities/fileSystem/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/server/src/utilities/fileSystem/index.js b/packages/server/src/utilities/fileSystem/index.js index ddda274ef5..afacbf8cdf 100644 --- a/packages/server/src/utilities/fileSystem/index.js +++ b/packages/server/src/utilities/fileSystem/index.js @@ -238,7 +238,10 @@ exports.readFileSync = (filepath, options = "utf8") => { */ exports.cleanup = appIds => { for (let appId of appIds) { - fs.rmdirSync(join(budibaseTempDir(), appId), { recursive: true }) + const path = join(budibaseTempDir(), appId) + if (fs.existsSync(path)) { + fs.rmdirSync(path, { recursive: true }) + } } } From 7b841e709a0ab793050ff21f948aa6f53921f0eb Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 7 Jul 2021 09:34:56 +0100 Subject: [PATCH 16/20] Mock redis shutdown method to avoid errors --- .../src/api/routes/tests/application.spec.js | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js index 1930d0a4ec..2333787e6e 100644 --- a/packages/server/src/api/routes/tests/application.spec.js +++ b/packages/server/src/api/routes/tests/application.spec.js @@ -9,9 +9,13 @@ jest.mock("../../../utilities/redis", () => ({ updateLock: jest.fn(), setDebounce: jest.fn(), checkDebounce: jest.fn(), + shutdown: jest.fn(), })) -const { clearAllApps, checkBuilderEndpoint } = require("./utilities/TestFunctions") +const { + clearAllApps, + checkBuilderEndpoint, +} = require("./utilities/TestFunctions") const setup = require("./utilities") const { AppStatus } = require("../../../db/utils") @@ -32,7 +36,7 @@ describe("/applications", () => { .post("/api/applications") .send({ name: "My App" }) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(res.body._id).toBeDefined() }) @@ -42,7 +46,7 @@ describe("/applications", () => { config, method: "POST", url: `/api/applications`, - body: { name: "My App" } + body: { name: "My App" }, }) }) }) @@ -55,7 +59,7 @@ describe("/applications", () => { const res = await request .get(`/api/applications?status=${AppStatus.DEV}`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) // two created apps + the inited app @@ -68,7 +72,7 @@ describe("/applications", () => { const res = await request .get(`/api/applications/${config.getAppId()}/definition`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) // should have empty packages expect(res.body.screens.length).toEqual(1) @@ -81,7 +85,7 @@ describe("/applications", () => { const res = await request .get(`/api/applications/${config.getAppId()}/appPackage`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(res.body.application).toBeDefined() expect(res.body.screens.length).toEqual(1) @@ -94,10 +98,10 @@ describe("/applications", () => { const res = await request .put(`/api/applications/${config.getAppId()}`) .send({ - name: "TEST_APP" + name: "TEST_APP", }) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(res.body.rev).toBeDefined() }) @@ -113,14 +117,14 @@ describe("/applications", () => { name: "UPDATED_NAME", }) .set(headers) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(res.body.rev).toBeDefined() // retrieve the app to check it const getRes = await request .get(`/api/applications/${config.getAppId()}/appPackage`) .set(headers) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(getRes.body.application.updatedAt).toBeDefined() }) From 8c39c4792dd22441107744b0e62408d57ec39988 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 30 Jun 2021 19:37:03 +0100 Subject: [PATCH 17/20] Add error handling to builder preview and loading indicator --- packages/builder/assets/error.svg | 34 ++++++++++ .../AppPreview/CurrentItemPreview.svelte | 62 ++++++++++++++++++- .../design/AppPreview/iframeTemplate.js | 17 +++-- .../client/src/components/ClientApp.svelte | 3 + packages/client/src/store/builder.js | 5 +- 5 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 packages/builder/assets/error.svg diff --git a/packages/builder/assets/error.svg b/packages/builder/assets/error.svg new file mode 100644 index 0000000000..4cc1d753c9 --- /dev/null +++ b/packages/builder/assets/error.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte index 5ae4ac0ddf..12f5280b83 100644 --- a/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte +++ b/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte @@ -5,12 +5,16 @@ import { Screen } from "builderStore/store/screenTemplates/utils/Screen" import { FrontendTypes } from "constants" import ConfirmDialog from "components/common/ConfirmDialog.svelte" + import { ProgressCircle, Layout, Heading, Body } from "@budibase/bbui" + import ErrorSVG from "assets/error.svg?raw" let iframe let layout let screen let confirmDeleteDialog let idToDelete + let loading = true + let error // Create screen slot placeholder for use when a page is selected rather // than a screen @@ -68,11 +72,21 @@ onMount(() => { // Initialise the app when mounted iframe.contentWindow.addEventListener( - "bb-ready", + "ready", () => refreshContent(strippedJson), { once: true } ) + // Catch any app errors + iframe.contentWindow.addEventListener( + "error", + event => { + loading = false + error = event.detail || "An unknown error occurred" + }, + { once: true } + ) + // Add listener for events sent by cliebt library in preview iframe.contentWindow.addEventListener("bb-event", event => { const { type, data } = event.detail @@ -83,8 +97,10 @@ } else if (type === "delete-component" && data.id) { idToDelete = data.id confirmDeleteDialog.show() + } else if (type === "preview-loaded") { + loading = false } else { - console.log(data) + console.warning(`Client sent unknown event type: ${type}`) } }) }) @@ -99,11 +115,25 @@
+ {#if loading} +
+ +
+ {:else if error} +
+ + {@html ErrorSVG} + App preview failed to load + {error} + +
+ {/if}