From 99bf0ca03b245ac7861f5bb5fbb47611eede9e29 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 4 Jul 2023 13:18:38 +0100 Subject: [PATCH] Sync publish and unpublish events between all users --- packages/builder/src/builderStore/index.js | 6 + .../src/builderStore/store/deployments.js | 22 + .../src/builderStore/store/frontend.js | 9 + .../builder/src/builderStore/websocket.js | 19 +- .../src/components/deploy/AppActions.svelte | 391 ++++++++---------- .../src/components/deploy/DeployModal.svelte | 118 ------ .../deploy/DeploymentHistory.svelte | 236 ----------- .../builder/src/components/deploy/utils.js | 25 -- .../builder/app/[application]/_layout.svelte | 26 +- .../settings/name-and-url.svelte | 2 +- .../server/src/api/controllers/application.ts | 10 + .../src/api/controllers/deploy/index.ts | 2 + packages/server/src/websockets/builder.ts | 21 + packages/shared-core/src/constants.ts | 1 + 14 files changed, 271 insertions(+), 617 deletions(-) create mode 100644 packages/builder/src/builderStore/store/deployments.js delete mode 100644 packages/builder/src/components/deploy/DeployModal.svelte delete mode 100644 packages/builder/src/components/deploy/DeploymentHistory.svelte delete mode 100644 packages/builder/src/components/deploy/utils.js diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index 1022f917d0..8dbee49327 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -3,6 +3,7 @@ import { getAutomationStore } from "./store/automation" import { getTemporalStore } from "./store/temporal" import { getThemeStore } from "./store/theme" import { getUserStore } from "./store/users" +import { getDeploymentStore } from "./store/deployments" import { derived } from "svelte/store" import { findComponent, findComponentPath } from "./componentUtils" import { RoleUtils } from "@budibase/frontend-core" @@ -14,6 +15,7 @@ export const automationStore = getAutomationStore() export const themeStore = getThemeStore() export const temporalStore = getTemporalStore() export const userStore = getUserStore() +export const deploymentStore = getDeploymentStore() // Setup history for screens export const screenHistoryStore = createHistoryStore({ @@ -131,3 +133,7 @@ export const userSelectedResourceMap = derived(userStore, $userStore => { }) return map }) + +export const isOnlyUser = derived(userStore, $userStore => { + return $userStore.length === 1 +}) diff --git a/packages/builder/src/builderStore/store/deployments.js b/packages/builder/src/builderStore/store/deployments.js new file mode 100644 index 0000000000..9f7a68cb46 --- /dev/null +++ b/packages/builder/src/builderStore/store/deployments.js @@ -0,0 +1,22 @@ +import { writable } from "svelte/store" +import { API } from "api" +import { notifications } from "@budibase/bbui" + +export const getDeploymentStore = () => { + let store = writable([]) + + const load = async () => { + try { + store.set(await API.getAppDeployments()) + } catch (err) { + notifications.error("Error fetching deployments") + } + } + + return { + subscribe: store.subscribe, + actions: { + load, + }, + } +} diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index a3e499e336..aa3ae3a90b 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -1402,6 +1402,15 @@ export const getFrontendStore = () => { }) }, }, + metadata: { + replace: metadata => { + console.log("NEW METADATA", metadata) + store.update(state => ({ + ...state, + ...metadata, + })) + }, + }, } return store diff --git a/packages/builder/src/builderStore/websocket.js b/packages/builder/src/builderStore/websocket.js index 8562c8024c..87195bed25 100644 --- a/packages/builder/src/builderStore/websocket.js +++ b/packages/builder/src/builderStore/websocket.js @@ -1,10 +1,12 @@ import { createWebsocket } from "@budibase/frontend-core" -import { userStore, store } from "builderStore" +import { userStore, store, deploymentStore } from "builderStore" import { datasources, tables } from "stores/backend" import { get } from "svelte/store" import { auth } from "stores/portal" import { SocketEvent, BuilderSocketEvent } from "@budibase/shared-core" +import { apps } from "stores/portal" import { notifications } from "@budibase/bbui" +import { helpers } from "@budibase/shared-core" export const createBuilderWebsocket = appId => { const socket = createWebsocket("/socket/builder") @@ -31,7 +33,6 @@ export const createBuilderWebsocket = appId => { }) socket.onOther(BuilderSocketEvent.LockTransfer, ({ userId }) => { if (userId === get(auth)?.user?._id) { - notifications.success("You can now edit automations") store.update(state => ({ ...state, hasLock: true, @@ -51,6 +52,20 @@ export const createBuilderWebsocket = appId => { socket.onOther(BuilderSocketEvent.ScreenChange, ({ id, screen }) => { store.actions.screens.replace(id, screen) }) + socket.onOther(BuilderSocketEvent.AppMetadataChange, ({ metadata }) => { + store.actions.metadata.replace(metadata) + }) + socket.onOther( + BuilderSocketEvent.AppPublishChange, + async ({ user, published }) => { + await apps.load() + if (published) { + await deploymentStore.actions.load() + } + const verb = published ? "published" : "unpublished" + notifications.success(`${helpers.getUserLabel(user)} ${verb} this app`) + } + ) return socket } diff --git a/packages/builder/src/components/deploy/AppActions.svelte b/packages/builder/src/components/deploy/AppActions.svelte index a9aa3ff506..0975803795 100644 --- a/packages/builder/src/components/deploy/AppActions.svelte +++ b/packages/builder/src/components/deploy/AppActions.svelte @@ -18,11 +18,9 @@ import { processStringSync } from "@budibase/string-templates" import ConfirmDialog from "components/common/ConfirmDialog.svelte" import analytics, { Events, EventSource } from "analytics" - import { checkIncomingDeploymentStatus } from "components/deploy/utils" import { API } from "api" - import { onMount } from "svelte" import { apps } from "stores/portal" - import { store } from "builderStore" + import { deploymentStore, store } from "builderStore" import TourWrap from "components/portal/onboarding/TourWrap.svelte" import { TOUR_STEP_KEYS } from "components/portal/onboarding/tours.js" import { goto } from "@roxi/routify" @@ -34,37 +32,31 @@ let updateAppModal let revertModal let versionModal - let appActionPopover let appActionPopoverOpen = false let appActionPopoverAnchor - let publishing = false $: filteredApps = $apps.filter(app => app.devId === application) $: selectedApp = filteredApps?.length ? filteredApps[0] : null - - $: deployments = [] - $: latestDeployments = deployments + $: latestDeployments = $deploymentStore .filter(deployment => deployment.status === "SUCCESS") .sort((a, b) => a.updatedAt > b.updatedAt) - $: isPublished = selectedApp?.status === "published" && latestDeployments?.length > 0 - $: updateAvailable = $store.upgradableVersion && $store.version && $store.upgradableVersion !== $store.version - $: canPublish = !publishing && loaded + $: lastDeployed = getLastDeployedString($deploymentStore) const initialiseApp = async () => { const applicationPkg = await API.fetchAppPackage($store.devId) await store.actions.initialise(applicationPkg) } - const updateDeploymentString = () => { + const getLastDeployedString = deployments => { return deployments?.length ? processStringSync("Published {{ duration time 'millisecond' }} ago", { time: @@ -73,27 +65,6 @@ : "" } - const reviewPendingDeployments = (deployments, newDeployments) => { - if (deployments.length > 0) { - const pending = checkIncomingDeploymentStatus(deployments, newDeployments) - if (pending.length) { - notifications.warning( - "Deployment has been queued and will be processed shortly" - ) - } - } - } - - async function fetchDeployments() { - try { - const newDeployments = await API.getAppDeployments() - reviewPendingDeployments(deployments, newDeployments) - return newDeployments - } catch (err) { - notifications.error("Error fetching deployment overview") - } - } - const previewApp = () => { store.update(state => ({ ...state, @@ -116,14 +87,11 @@ async function publishApp() { try { publishing = true - await API.publishAppChanges($store.appId) - notifications.send("App published", { type: "success", icon: "GlobeCheck", }) - await completePublish() } catch (error) { console.error(error) @@ -163,210 +131,191 @@ const completePublish = async () => { try { await apps.load() - deployments = await fetchDeployments() + await deploymentStore.actions.load() } catch (err) { notifications.error("Error refreshing app") } } - - onMount(async () => { - if (!$apps.length) { - await apps.load() - } - deployments = await fetchDeployments() - }) -{#if $store.hasLock} -
-
- - {#if updateAvailable} -
-
- - - Update - -
-
- {/if} - -
-
- { - store.update(state => { - state.builderSidePanel = true - return state - }) - }} - > - Users - -
-
-
- -
+
+
+ + {#if updateAvailable} +
- - Preview + + + Update
- - -
{ - if (!appActionPopoverOpen) { - appActionPopover.show() - } else { - appActionPopover.hide() - } - }} - > -
-
- - - - Publish - - - -
+ {/if} + +
+
+ { + store.update(state => { + state.builderSidePanel = true + return state + }) + }} + > + Users +
- { - appActionPopoverOpen = false - }} - on:open={() => { - appActionPopoverOpen = true - }} - > -
- - - - { - if (isPublished) { - viewApp() - } else { - appActionPopover.hide() - updateAppModal.show() - } - }} - > - {$store.url} - {#if isPublished} - - {:else} - - {/if} - - +
+ - - - {#if isPublished} - - {updateDeploymentString(deployments)} - - - Unpublish - - - Revert - - {:else} - Not published - {/if} - - -
- {#if $store.hasLock} - {#if isPublished} - { - $goto("./settings/embed") - appActionPopover.hide() - }} - > - Embed - - {/if} - - {/if} -
- -
- +
+
+ + Preview +
-
- - - Are you sure you want to unpublish the app {selectedApp?.name}? - - - - +
{ + if (!appActionPopoverOpen) { + appActionPopover.show() + } else { + appActionPopover.hide() + } }} - onUpdateComplete={async () => { - await initialiseApp() - }} - /> - + > +
+
+ + + + Publish + + + +
+
+ { + appActionPopoverOpen = false + }} + on:open={() => { + appActionPopoverOpen = true + }} + > +
+ + + + { + if (isPublished) { + viewApp() + } else { + appActionPopover.hide() + updateAppModal.show() + } + }} + > + {$store.url} + {#if isPublished} + + {:else} + + {/if} + + - - -{:else} -
-
- - Preview - + + + {#if isPublished} + + {lastDeployed} + + + Unpublish + + + Revert + + {:else} + Not published + {/if} + + +
+ {#if isPublished} + { + $goto("./settings/embed") + appActionPopover.hide() + }} + > + Embed + + {/if} + +
+ +
+
-{/if} +
+ + + + Are you sure you want to unpublish the app {selectedApp?.name}? + + + + { + await initialiseApp() + }} + /> + + + + diff --git a/packages/builder/src/components/deploy/DeploymentHistory.svelte b/packages/builder/src/components/deploy/DeploymentHistory.svelte deleted file mode 100644 index e025abf1c7..0000000000 --- a/packages/builder/src/components/deploy/DeploymentHistory.svelte +++ /dev/null @@ -1,236 +0,0 @@ - - -{#if deployments.length > 0} -
-
- Deployment History -
- {#if deployments.some(deployment => deployment.status === DeploymentStatus.SUCCESS)} - View Your Deployed App → - - {/if} -
-
-
- {#each deployments as deployment} -
-
- - {formatDate(deployment.updatedAt, "fullDate")} - - - {formatDate(deployment.updatedAt, "timeOnly")} - -
-
- {#if deployment.status.toLowerCase() === "pending"} - - {/if} -
showErrorReasonModal(deployment.err)} - class={`deployment-status ${deployment.status}`} - > - - {deployment.status} - {#if deployment.status === DeploymentStatus.FAILURE} - - {/if} - -
-
-
- {/each} -
-
-{/if} - - - - - - {errorReason} - - - - diff --git a/packages/builder/src/components/deploy/utils.js b/packages/builder/src/components/deploy/utils.js deleted file mode 100644 index cb254f0dbf..0000000000 --- a/packages/builder/src/components/deploy/utils.js +++ /dev/null @@ -1,25 +0,0 @@ -export const DeploymentStatus = { - SUCCESS: "SUCCESS", - PENDING: "PENDING", - FAILURE: "FAILURE", -} - -// Required to check any updated deployment statuses between polls -export function checkIncomingDeploymentStatus(current, incoming) { - return incoming.reduce((acc, incomingDeployment) => { - if (incomingDeployment.status === DeploymentStatus.FAILURE) { - const currentDeployment = current.find( - deployment => deployment._id === incomingDeployment._id - ) - - //We have just been notified of an ongoing deployments failure - if ( - !currentDeployment || - currentDeployment.status === DeploymentStatus.PENDING - ) { - acc.push(incomingDeployment) - } - } - return acc - }, []) -} diff --git a/packages/builder/src/pages/builder/app/[application]/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/_layout.svelte index 3703279044..8581200578 100644 --- a/packages/builder/src/pages/builder/app/[application]/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_layout.svelte @@ -1,7 +1,12 @@