diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js
index 7bd0ccca22..73aa2757ec 100644
--- a/packages/builder/src/builderStore/store/automation/index.js
+++ b/packages/builder/src/builderStore/store/automation/index.js
@@ -1,95 +1,130 @@
import { writable } from "svelte/store"
-import api from "../../api"
+import { API } from "api"
import Automation from "./Automation"
import { cloneDeep } from "lodash/fp"
import analytics, { Events } from "analytics"
+import { notifications } from "@budibase/bbui"
+
+const initialAutomationState = {
+ automations: [],
+ blockDefinitions: {
+ TRIGGER: [],
+ ACTION: [],
+ },
+ selectedAutomation: null,
+}
+
+export const getAutomationStore = () => {
+ const store = writable(initialAutomationState)
+ store.actions = automationActions(store)
+ return store
+}
const automationActions = store => ({
fetch: async () => {
- const responses = await Promise.all([
- api.get(`/api/automations`),
- api.get(`/api/automations/definitions/list`),
- ])
- const jsonResponses = await Promise.all(responses.map(x => x.json()))
- store.update(state => {
- let selected = state.selectedAutomation?.automation
- state.automations = jsonResponses[0]
- state.blockDefinitions = {
- TRIGGER: jsonResponses[1].trigger,
- ACTION: jsonResponses[1].action,
- }
- // if previously selected find the new obj and select it
- if (selected) {
- selected = jsonResponses[0].filter(
- automation => automation._id === selected._id
- )
- state.selectedAutomation = new Automation(selected[0])
- }
- return state
- })
+ try {
+ const responses = await Promise.all([
+ API.getAutomations(),
+ API.getAutomationDefinitions(),
+ ])
+ store.update(state => {
+ let selected = state.selectedAutomation?.automation
+ state.automations = responses[0]
+ state.blockDefinitions = {
+ TRIGGER: responses[1].trigger,
+ ACTION: responses[1].action,
+ }
+ // If previously selected find the new obj and select it
+ if (selected) {
+ selected = responses[0].filter(
+ automation => automation._id === selected._id
+ )
+ state.selectedAutomation = new Automation(selected[0])
+ }
+ return state
+ })
+ } catch (error) {
+ notifications.error("Error fetching automations")
+ store.set(initialAutomationState)
+ }
},
create: async ({ name }) => {
- const automation = {
- name,
- type: "automation",
- definition: {
- steps: [],
- },
+ try {
+ const automation = {
+ name,
+ type: "automation",
+ definition: {
+ steps: [],
+ },
+ }
+ const response = await API.createAutomation(automation)
+ store.update(state => {
+ state.automations = [...state.automations, response.automation]
+ store.actions.select(response.automation)
+ return state
+ })
+ } catch (error) {
+ notifications.error("Error creating automation")
}
- const CREATE_AUTOMATION_URL = `/api/automations`
- const response = await api.post(CREATE_AUTOMATION_URL, automation)
- const json = await response.json()
- store.update(state => {
- state.automations = [...state.automations, json.automation]
- store.actions.select(json.automation)
- return state
- })
},
save: async automation => {
- const UPDATE_AUTOMATION_URL = `/api/automations`
- const response = await api.put(UPDATE_AUTOMATION_URL, automation)
- const json = await response.json()
- store.update(state => {
- const newAutomation = json.automation
- const existingIdx = state.automations.findIndex(
- existing => existing._id === automation._id
- )
- if (existingIdx !== -1) {
- state.automations.splice(existingIdx, 1, newAutomation)
- state.automations = [...state.automations]
- store.actions.select(newAutomation)
- return state
- }
- })
+ try {
+ const response = await API.updateAutomation(automation)
+ store.update(state => {
+ const updatedAutomation = response.automation
+ const existingIdx = state.automations.findIndex(
+ existing => existing._id === automation._id
+ )
+ if (existingIdx !== -1) {
+ state.automations.splice(existingIdx, 1, updatedAutomation)
+ state.automations = [...state.automations]
+ store.actions.select(updatedAutomation)
+ return state
+ }
+ })
+ notifications.success("Automation saved successfully")
+ } catch (error) {
+ notifications.error("Error saving automation")
+ }
},
delete: async automation => {
- const { _id, _rev } = automation
- const DELETE_AUTOMATION_URL = `/api/automations/${_id}/${_rev}`
- await api.delete(DELETE_AUTOMATION_URL)
-
- store.update(state => {
- const existingIdx = state.automations.findIndex(
- existing => existing._id === _id
- )
- state.automations.splice(existingIdx, 1)
- state.automations = [...state.automations]
- state.selectedAutomation = null
- state.selectedBlock = null
- return state
- })
- },
- trigger: async automation => {
- const { _id } = automation
- return await api.post(`/api/automations/${_id}/trigger`)
+ try {
+ await API.deleteAutomation({
+ automationId: automation?._id,
+ automationRev: automation?._rev,
+ })
+ store.update(state => {
+ const existingIdx = state.automations.findIndex(
+ existing => existing._id === automation?._id
+ )
+ state.automations.splice(existingIdx, 1)
+ state.automations = [...state.automations]
+ state.selectedAutomation = null
+ state.selectedBlock = null
+ return state
+ })
+ notifications.success("Automation deleted successfully")
+ } catch (error) {
+ notifications.error("Error deleting automation")
+ }
},
test: async (automation, testData) => {
- const { _id } = automation
- const response = await api.post(`/api/automations/${_id}/test`, testData)
- const json = await response.json()
- store.update(state => {
- state.selectedAutomation.testResults = json
- return state
- })
+ try {
+ const result = await API.testAutomation({
+ automationId: automation?._id,
+ testData,
+ })
+ store.update(state => {
+ state.selectedAutomation.testResults = result
+ return state
+ })
+ } catch (error) {
+ notifications.error("Error testing automation")
+ store.update(state => {
+ state.selectedAutomation.testResults = null
+ return state
+ })
+ }
},
select: automation => {
store.update(state => {
@@ -143,17 +178,3 @@ const automationActions = store => ({
})
},
})
-
-export const getAutomationStore = () => {
- const INITIAL_AUTOMATION_STATE = {
- automations: [],
- blockDefinitions: {
- TRIGGER: [],
- ACTION: [],
- },
- selectedAutomation: null,
- }
- const store = writable(INITIAL_AUTOMATION_STATE)
- store.actions = automationActions(store)
- return store
-}
diff --git a/packages/builder/src/builderStore/store/hosting.js b/packages/builder/src/builderStore/store/hosting.js
index fb174c2663..980130b147 100644
--- a/packages/builder/src/builderStore/store/hosting.js
+++ b/packages/builder/src/builderStore/store/hosting.js
@@ -1,5 +1,6 @@
import { writable } from "svelte/store"
-import api, { get } from "../api"
+import { API } from "api"
+import { notifications } from "@budibase/bbui"
const INITIAL_HOSTING_UI_STATE = {
appUrl: "",
@@ -12,22 +13,40 @@ export const getHostingStore = () => {
const store = writable({ ...INITIAL_HOSTING_UI_STATE })
store.actions = {
fetch: async () => {
- const response = await api.get("/api/hosting/urls")
- const urls = await response.json()
- store.update(state => {
- state.appUrl = urls.app
- return state
- })
+ try {
+ const urls = await API.getHostingURLs()
+ store.update(state => {
+ state.appUrl = urls.app
+ return state
+ })
+ } catch (error) {
+ store.update(state => {
+ state.appUrl = ""
+ return state
+ })
+ notifications.error("Error fetching hosting URLs")
+ }
},
fetchDeployedApps: async () => {
- let deployments = await (await get("/api/hosting/apps")).json()
- store.update(state => {
- state.deployedApps = deployments
- state.deployedAppNames = Object.values(deployments).map(app => app.name)
- state.deployedAppUrls = Object.values(deployments).map(app => app.url)
- return state
- })
- return deployments
+ try {
+ const deployments = await API.getDeployedApps()
+ store.update(state => {
+ state.deployedApps = deployments
+ state.deployedAppNames = Object.values(deployments).map(
+ app => app.name
+ )
+ state.deployedAppUrls = Object.values(deployments).map(app => app.url)
+ return state
+ })
+ } catch (error) {
+ store.update(state => {
+ state.deployedApps = {}
+ state.deployedAppNames = []
+ state.deployedAppUrls = []
+ return state
+ })
+ notifications.error("Failed detching deployed apps")
+ }
},
}
return store
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
index 2d6881d652..5bf625940a 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
@@ -33,23 +33,6 @@
await automationStore.actions.delete(
$automationStore.selectedAutomation?.automation
)
- notifications.success("Automation deleted.")
- }
-
- async function testAutomation() {
- const result = await automationStore.actions.trigger(
- $automationStore.selectedAutomation.automation
- )
- if (result.status === 200) {
- notifications.success(
- `Automation ${$automationStore.selectedAutomation.automation.name} triggered successfully.`
- )
- } else {
- notifications.error(
- `Failed to trigger automation ${$automationStore.selectedAutomation.automation.name}.`
- )
- }
- return result
}
@@ -85,7 +68,7 @@
animate:flip={{ duration: 500 }}
in:fly|local={{ x: 500, duration: 1500 }}
>
-
+
{/each}
@@ -101,7 +84,7 @@
-
+
diff --git a/packages/builder/src/components/backend/DataTable/api.js b/packages/builder/src/components/backend/DataTable/api.js
deleted file mode 100644
index b461c70c4b..0000000000
--- a/packages/builder/src/components/backend/DataTable/api.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import api from "builderStore/api"
-
-export async function createUser(user) {
- const CREATE_USER_URL = `/api/users/metadata`
- const response = await api.post(CREATE_USER_URL, user)
- return await response.json()
-}
-
-export async function saveRow(row, tableId) {
- const SAVE_ROW_URL = `/api/${tableId}/rows`
- const response = await api.post(SAVE_ROW_URL, row)
-
- return await response.json()
-}
-
-export async function deleteRow(row) {
- const DELETE_ROWS_URL = `/api/${row.tableId}/rows`
- return api.delete(DELETE_ROWS_URL, {
- _id: row._id,
- _rev: row._rev,
- })
-}
-
-export async function fetchDataForTable(tableId) {
- const FETCH_ROWS_URL = `/api/${tableId}/rows`
-
- const response = await api.get(FETCH_ROWS_URL)
- const json = await response.json()
-
- if (response.status !== 200) {
- throw new Error(json.message)
- }
- return json
-}
diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte
index 559e8275db..965458e297 100644
--- a/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte
+++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte
@@ -3,7 +3,7 @@
import { tables, rows } from "stores/backend"
import { notifications } from "@budibase/bbui"
import RowFieldControl from "../RowFieldControl.svelte"
- import * as api from "../api"
+ import { API } from "api"
import { ModalContent } from "@budibase/bbui"
import ErrorsBox from "components/common/ErrorsBox.svelte"
import { FIELDS } from "constants/backend"
@@ -22,30 +22,30 @@
$: tableSchema = Object.entries(table?.schema ?? {})
async function saveRow() {
- const rowResponse = await api.saveRow(
- { ...row, tableId: table._id },
- table._id
- )
-
- if (rowResponse.errors) {
- errors = Object.entries(rowResponse.errors)
- .map(([key, error]) => ({ dataPath: key, message: error }))
- .flat()
+ errors = []
+ try {
+ await API.saveRow({ ...row, tableId: table._id })
+ notifications.success("Row saved successfully")
+ rows.save()
+ dispatch("updaterows")
+ } catch (error) {
+ if (error.handled) {
+ const response = error.json
+ if (response?.errors) {
+ errors = Object.entries(response.errors)
+ .map(([key, error]) => ({ dataPath: key, message: error }))
+ .flat()
+ } else if (error.status === 400 && response?.validationErrors) {
+ errors = Object.keys(response.validationErrors).map(field => ({
+ message: `${field} ${response.validationErrors[field][0]}`,
+ }))
+ }
+ } else {
+ notifications.error("Failed to save row")
+ }
// Prevent modal closing if there were errors
return false
- } else if (rowResponse.status === 400 && rowResponse.validationErrors) {
- errors = Object.keys(rowResponse.validationErrors).map(field => ({
- message: `${field} ${rowResponse.validationErrors[field][0]}`,
- }))
- return false
- } else if (rowResponse.status >= 400) {
- errors = [{ message: rowResponse.message }]
- return false
}
-
- notifications.success("Row saved successfully.")
- rows.save(rowResponse)
- dispatch("updaterows")
}
diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte
index f1de23fb97..088280e266 100644
--- a/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte
+++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte
@@ -4,7 +4,7 @@
import { roles } from "stores/backend"
import { notifications } from "@budibase/bbui"
import RowFieldControl from "../RowFieldControl.svelte"
- import * as backendApi from "../api"
+ import { API } from "api"
import { ModalContent, Select } from "@budibase/bbui"
import ErrorsBox from "components/common/ErrorsBox.svelte"
@@ -53,27 +53,31 @@
return false
}
- const rowResponse = await backendApi.saveRow(
- { ...row, tableId: table._id },
- table._id
- )
- if (rowResponse.errors) {
- if (Array.isArray(rowResponse.errors)) {
- errors = rowResponse.errors.map(error => ({ message: error }))
+ try {
+ await API.saveRow({ ...row, tableId: table._id })
+ notifications.success("User saved successfully")
+ rows.save()
+ dispatch("updaterows")
+ } catch (error) {
+ if (error.handled) {
+ const response = error.json
+ if (response?.errors) {
+ if (Array.isArray(response.errors)) {
+ errors = response.errors.map(error => ({ message: error }))
+ } else {
+ errors = Object.entries(response.errors)
+ .map(([key, error]) => ({ dataPath: key, message: error }))
+ .flat()
+ }
+ } else if (error.status === 400) {
+ errors = [{ message: response?.message || "Unknown error" }]
+ }
} else {
- errors = Object.entries(rowResponse.errors)
- .map(([key, error]) => ({ dataPath: key, message: error }))
- .flat()
+ notifications.error("Error saving user")
}
- return false
- } else if (rowResponse.status === 400 || rowResponse.status === 500) {
- errors = [{ message: rowResponse.message }]
+ // Prevent closing the modal on errors
return false
}
-
- notifications.success("User saved successfully")
- rows.save(rowResponse)
- dispatch("updaterows")
}
diff --git a/packages/builder/src/pages/builder/app/[application]/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/_layout.svelte
index 0478b46f73..62a7c91d3f 100644
--- a/packages/builder/src/pages/builder/app/[application]/_layout.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_layout.svelte
@@ -6,25 +6,26 @@
import RevertModal from "components/deploy/RevertModal.svelte"
import VersionModal from "components/deploy/VersionModal.svelte"
import NPSFeedbackForm from "components/feedback/NPSFeedbackForm.svelte"
- import { get, post } from "builderStore/api"
+ import { API } from "api"
import { auth, admin } from "stores/portal"
import { isActive, goto, layout, redirect } from "@roxi/routify"
import Logo from "assets/bb-emblem.svg"
import { capitalise } from "helpers"
- import UpgradeModal from "../../../../components/upgrade/UpgradeModal.svelte"
+ import UpgradeModal from "components/upgrade/UpgradeModal.svelte"
import { onMount } from "svelte"
- // Get Package and set store
export let application
+
+ // Get Package and set store
let promise = getPackage()
- // sync once when you load the app
+
+ // Sync once when you load the app
let hasSynced = false
+ let userShouldPostFeedback = false
$: selected = capitalise(
$layout.children.find(layout => $isActive(layout.path))?.title ?? "data"
)
- let userShouldPostFeedback = false
-
function previewApp() {
if (!$auth?.user?.flags?.feedbackSubmitted) {
userShouldPostFeedback = true
@@ -33,34 +34,25 @@
}
async function getPackage() {
- const res = await get(`/api/applications/${application}/appPackage`)
- const pkg = await res.json()
-
- if (res.ok) {
- try {
- await store.actions.initialise(pkg)
- // edge case, lock wasn't known to client when it re-directed, or user went directly
- } catch (err) {
- if (!err.ok && err.reason === "locked") {
- $redirect("../../")
- } else {
- throw err
- }
- }
+ try {
+ const pkg = await API.fetchAppPackage(application)
+ await store.actions.initialise(pkg)
await automationStore.actions.fetch()
await roles.fetch()
await flags.fetch()
return pkg
- } else {
- throw new Error(pkg)
+ } catch (error) {
+ // TODO: some stuff about redirecting if locked
+ // $redirect("../../")
+ notifications.error("Error initialising app")
}
}
- // handles navigation between frontend, backend, automation.
- // this remembers your last place on each of the sections
+ // Handles navigation between frontend, backend, automation.
+ // This remembers your last place on each of the sections
// e.g. if one of your screens is selected on front end, then
// you browse to backend, when you click frontend, you will be
- // brought back to the same screen
+ // brought back to the same screen.
const topItemNavigate = path => () => {
const activeTopNav = $layout.children.find(c => $isActive(c.path))
if (!activeTopNav) return
@@ -74,8 +66,9 @@
onMount(async () => {
if (!hasSynced && application) {
- const res = await post(`/api/applications/${application}/sync`)
- if (res.status !== 200) {
+ try {
+ await API.syncApp(application)
+ } catch (error) {
notifications.error("Failed to sync with production database")
}
hasSynced = true
diff --git a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte
index 896e1d53e1..bdbf884c99 100644
--- a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte
+++ b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte
@@ -20,7 +20,7 @@
Toggle,
} from "@budibase/bbui"
import { onMount } from "svelte"
- import api from "builderStore/api"
+ import { API } from "api"
import { organisation, admin } from "stores/portal"
import { Helpers } from "@budibase/bbui"
import analytics, { Events } from "analytics"
@@ -137,17 +137,6 @@
providers.oidc?.config?.configs[0].clientID &&
providers.oidc?.config?.configs[0].clientSecret
- async function uploadLogo(file) {
- let data = new FormData()
- data.append("file", file)
- const res = await api.post(
- `/api/global/configs/upload/logos_oidc/${file.name}`,
- data,
- {}
- )
- return await res.json()
- }
-
const onFileSelected = e => {
let fileName = e.target.files[0].name
image = e.target.files[0]
@@ -156,17 +145,28 @@
}
async function save(docs) {
- // only if the user has provided an image, upload it.
- image && uploadLogo(image)
let calls = []
+
+ // Only if the user has provided an image, upload it
+ if (image) {
+ let data = new FormData()
+ data.append("file", image)
+ calls.push(
+ API.uploadOIDCLogo({
+ name: image.name,
+ data,
+ })
+ )
+ }
+
docs.forEach(element => {
if (element.type === ConfigTypes.OIDC) {
- //Add a UUID here so each config is distinguishable when it arrives at the login page
+ // Add a UUID here so each config is distinguishable when it arrives at the login page
for (let config of element.config.configs) {
if (!config.uuid) {
config.uuid = Helpers.uuid()
}
- // callback urls shouldn't be included
+ // Callback urls shouldn't be included
delete config.callbackURL
}
if (partialOidc) {
@@ -175,8 +175,8 @@
`Please fill in all required ${ConfigTypes.OIDC} fields`
)
} else {
- calls.push(api.post(`/api/global/configs`, element))
- // turn the save button grey when clicked
+ calls.push(API.saveConfig(element))
+ // Turn the save button grey when clicked
oidcSaveButtonDisabled = true
originalOidcDoc = cloneDeep(providers.oidc)
}
@@ -189,71 +189,70 @@
`Please fill in all required ${ConfigTypes.Google} fields`
)
} else {
- calls.push(api.post(`/api/global/configs`, element))
+ calls.push(API.saveConfig(element))
googleSaveButtonDisabled = true
originalGoogleDoc = cloneDeep(providers.google)
}
}
}
})
- calls.length &&
+
+ if (calls.length) {
Promise.all(calls)
- .then(responses => {
- return Promise.all(
- responses.map(response => {
- return response.json()
- })
- )
- })
.then(data => {
data.forEach(res => {
providers[res.type]._rev = res._rev
providers[res.type]._id = res._id
})
- notifications.success(`Settings saved.`)
+ notifications.success(`Settings saved`)
analytics.captureEvent(Events.SSO.SAVED)
})
- .catch(err => {
- notifications.error(`Failed to update auth settings. ${err}`)
- throw new Error(err.message)
+ .catch(error => {
+ notifications.error(`Failed to update auth settings`)
+ console.error(error.message)
})
+ }
}
onMount(async () => {
await organisation.init()
- // fetch the configs for oauth
- const googleResponse = await api.get(
- `/api/global/configs/${ConfigTypes.Google}`
- )
- const googleDoc = await googleResponse.json()
- if (!googleDoc._id) {
+ // Fetch Google config
+ let googleDoc
+ try {
+ googleDoc = await API.getConfig(ConfigTypes.Google)
+ } catch (error) {
+ notifications.error("Error fetching Google OAuth config")
+ }
+ if (!googleDoc?._id) {
providers.google = {
type: ConfigTypes.Google,
config: { activated: true },
}
originalGoogleDoc = cloneDeep(googleDoc)
} else {
- // default activated to true for older configs
+ // Default activated to true for older configs
if (googleDoc.config.activated === undefined) {
googleDoc.config.activated = true
}
originalGoogleDoc = cloneDeep(googleDoc)
providers.google = googleDoc
}
-
googleCallbackUrl = providers?.google?.config?.callbackURL
- //Get the list of user uploaded logos and push it to the dropdown options.
- //This needs to be done before the config call so they're available when the dropdown renders
- const res = await api.get(`/api/global/configs/logos_oidc`)
- const configSettings = await res.json()
-
- if (configSettings.config) {
- const logoKeys = Object.keys(configSettings.config)
-
+ // Get the list of user uploaded logos and push it to the dropdown options.
+ // This needs to be done before the config call so they're available when
+ // the dropdown renders.
+ let oidcLogos
+ try {
+ oidcLogos = await API.getOIDCLogos()
+ } catch (error) {
+ notifications.error("Error fetching OIDC logos")
+ }
+ if (oidcLogos?.config) {
+ const logoKeys = Object.keys(oidcLogos.config)
logoKeys.map(logoKey => {
- const logoUrl = configSettings.config[logoKey]
+ const logoUrl = oidcLogos.config[logoKey]
iconDropdownOptions.unshift({
label: logoKey,
value: logoKey,
@@ -261,11 +260,15 @@
})
})
}
- const oidcResponse = await api.get(
- `/api/global/configs/${ConfigTypes.OIDC}`
- )
- const oidcDoc = await oidcResponse.json()
- if (!oidcDoc._id) {
+
+ // Fetch OIDC config
+ let oidcDoc
+ try {
+ oidcDoc = await API.getConfig(ConfigTypes.OIDC)
+ } catch (error) {
+ notifications.error("Error fetching OIDC config")
+ }
+ if (!oidcDoc?._id) {
providers.oidc = {
type: ConfigTypes.OIDC,
config: { configs: [{ activated: true }] },
diff --git a/packages/builder/src/pages/builder/portal/settings/organisation.svelte b/packages/builder/src/pages/builder/portal/settings/organisation.svelte
index 6903854922..c9086b2b45 100644
--- a/packages/builder/src/pages/builder/portal/settings/organisation.svelte
+++ b/packages/builder/src/pages/builder/portal/settings/organisation.svelte
@@ -11,7 +11,7 @@
notifications,
} from "@budibase/bbui"
import { auth, organisation, admin } from "stores/portal"
- import { post } from "builderStore/api"
+ import { API } from "api"
import { writable } from "svelte/store"
import { redirect } from "@roxi/routify"
@@ -32,14 +32,13 @@
let loading = false
async function uploadLogo(file) {
- let data = new FormData()
- data.append("file", file)
- const res = await post(
- "/api/global/configs/upload/settings/logoUrl",
- data,
- {}
- )
- return await res.json()
+ try {
+ let data = new FormData()
+ data.append("file", file)
+ await API.uploadLogo(data)
+ } catch (error) {
+ notifications.error("Error uploading logo")
+ }
}
async function saveConfig() {
@@ -55,19 +54,14 @@
company: $values.company ?? "",
platformUrl: $values.platformUrl ?? "",
}
- // remove logo if required
+
+ // Remove logo if required
if (!$values.logo) {
config.logoUrl = ""
}
// Update settings
- const res = await organisation.save(config)
- if (res.status === 200) {
- notifications.success("Settings saved successfully")
- } else {
- notifications.error(res.message)
- }
-
+ await organisation.save(config)
loading = false
}
diff --git a/packages/builder/src/pages/builder/portal/settings/update.svelte b/packages/builder/src/pages/builder/portal/settings/update.svelte
index 5deb724a7c..d87736144d 100644
--- a/packages/builder/src/pages/builder/portal/settings/update.svelte
+++ b/packages/builder/src/pages/builder/portal/settings/update.svelte
@@ -9,7 +9,7 @@
notifications,
Label,
} from "@budibase/bbui"
- import api from "builderStore/api"
+ import { API } from "api"
import { auth, admin } from "stores/portal"
import { redirect } from "@roxi/routify"
@@ -38,8 +38,12 @@
}
async function getVersion() {
- const response = await api.get("/api/dev/version")
- version = await response.text()
+ try {
+ version = await API.getBudibaseVersion()
+ } catch (error) {
+ notifications.error("Error getting Budibase version")
+ version = null
+ }
}
onMount(() => {
diff --git a/packages/frontend-core/src/api/app.js b/packages/frontend-core/src/api/app.js
index c1c4204c9d..543dcd58ee 100644
--- a/packages/frontend-core/src/api/app.js
+++ b/packages/frontend-core/src/api/app.js
@@ -122,4 +122,14 @@ export const buildAppEndpoints = API => ({
url: `/api/dev/${appId}/lock`,
})
},
+
+ /**
+ * Syncs an app with the production database.
+ * @param appId the ID of the app to sync
+ */
+ syncApp: async appId => {
+ return await API.post({
+ url: `/api/applications/${appId}/sync`,
+ })
+ },
})
diff --git a/packages/frontend-core/src/api/auth.js b/packages/frontend-core/src/api/auth.js
index 4f61c155f0..5f280b50da 100644
--- a/packages/frontend-core/src/api/auth.js
+++ b/packages/frontend-core/src/api/auth.js
@@ -36,6 +36,17 @@ export const buildAuthEndpoints = API => ({
})
},
+ /**
+ * Creates a user for an app.
+ * @param user the user to create
+ */
+ createAppUser: async user => {
+ return await API.post({
+ url: "/api/users/metadata",
+ body: user,
+ })
+ },
+
/**
* Updates the current user metadata.
* @param metadata the metadata to save
@@ -116,4 +127,38 @@ export const buildAuthEndpoints = API => ({
url: "/api/system/environment",
})
},
+
+ /**
+ * Updates the company logo for the environment.
+ * @param data the logo form data
+ */
+ uploadLogo: async data => {
+ return await API.post({
+ url: "/api/global/configs/upload/settings/logoUrl",
+ body: data,
+ json: false,
+ })
+ },
+
+ /**
+ * Uploads a logo for an OIDC provider.
+ * @param name the name of the OIDC provider
+ * @param data the logo form data to upload
+ */
+ uploadOIDCLogo: async ({ name, data }) => {
+ return await API.post({
+ url: `/api/global/configs/upload/logos_oidc/${name}`,
+ body: data,
+ json: false,
+ })
+ },
+
+ /**
+ * Gets the list of OIDC logos.
+ */
+ getOIDCLogos: async () => {
+ return await API.get({
+ url: "/api/global/configs/logos_oidc",
+ })
+ },
})
diff --git a/packages/frontend-core/src/api/automations.js b/packages/frontend-core/src/api/automations.js
index 95605a91a5..2843599e78 100644
--- a/packages/frontend-core/src/api/automations.js
+++ b/packages/frontend-core/src/api/automations.js
@@ -8,4 +8,67 @@ export const buildAutomationEndpoints = API => ({
body: { fields },
})
},
+
+ /**
+ * Tests an automation with data.
+ * @param automationId the ID of the automation to test
+ * @param testData the test data to run against the automation
+ */
+ testAutomation: async ({ automationId, testData }) => {
+ return await API.post({
+ url: `/api/automations/${automationId}/test`,
+ body: testData,
+ })
+ },
+
+ /**
+ * Gets a list of all automations.
+ */
+ getAutomations: async () => {
+ return await API.get({
+ url: "/api/automations",
+ })
+ },
+
+ /**
+ * Gets a list of all the definitions for blocks in automations.
+ */
+ getAutomationDefinitions: async () => {
+ return await API.get({
+ url: "/api/automations/definitions/list",
+ })
+ },
+
+ /**
+ * Creates an automation.
+ * @param automation the automation to create
+ */
+ createAutomation: async automation => {
+ return await API.post({
+ url: "/api/automations",
+ body: automation,
+ })
+ },
+
+ /**
+ * Updates an automation.
+ * @param automation the automation to update
+ */
+ updateAutomation: async automation => {
+ return await API.put({
+ url: "/api/automations",
+ body: automation,
+ })
+ },
+
+ /**
+ * Deletes an automation
+ * @param automationId the ID of the automation to delete
+ * @param automationRev the rev of the automation to delete
+ */
+ deleteAutomation: async ({ automationId, automationRev }) => {
+ return await API.delete({
+ url: `/api/automations/${automationId}/${automationRev}`,
+ })
+ },
})
diff --git a/packages/frontend-core/src/api/builder.js b/packages/frontend-core/src/api/builder.js
index dbe64e611e..dbb34a6a7b 100644
--- a/packages/frontend-core/src/api/builder.js
+++ b/packages/frontend-core/src/api/builder.js
@@ -5,13 +5,26 @@ export const buildBuilderEndpoints = API => ({
* @param {string} appId - ID of the currently running app
*/
fetchComponentLibDefinitions: async appId => {
- return await API.get({ url: `/api/${appId}/components/definitions` })
+ return await API.get({
+ url: `/api/${appId}/components/definitions`,
+ })
},
/**
* Gets the list of available integrations.
*/
getIntegrations: async () => {
- return await API.get({ url: "/api/integrations" })
+ return await API.get({
+ url: "/api/integrations",
+ })
+ },
+
+ /**
+ * Gets the version of the installed Budibase environment.
+ */
+ getBudibaseVersion: async () => {
+ return await API.get({
+ url: "/api/dev/version",
+ })
},
})
diff --git a/packages/frontend-core/src/api/hosting.js b/packages/frontend-core/src/api/hosting.js
new file mode 100644
index 0000000000..8c398f9ae7
--- /dev/null
+++ b/packages/frontend-core/src/api/hosting.js
@@ -0,0 +1,19 @@
+export const buildHostingEndpoints = API => ({
+ /**
+ * Gets the hosting URLs of the environment.
+ */
+ getHostingURLs: async () => {
+ return await API.get({
+ url: "/api/hosting/urls",
+ })
+ },
+
+ /**
+ * Gets the list of deployed apps.
+ */
+ getDeployedApps: async () => {
+ return await API.get({
+ url: "/api/hosting/apps",
+ })
+ },
+})
diff --git a/packages/frontend-core/src/api/index.js b/packages/frontend-core/src/api/index.js
index e1cf2f3619..1f7a703dcf 100644
--- a/packages/frontend-core/src/api/index.js
+++ b/packages/frontend-core/src/api/index.js
@@ -4,6 +4,7 @@ import { buildAppEndpoints } from "./app"
import { buildAttachmentEndpoints } from "./attachments"
import { buildAuthEndpoints } from "./auth"
import { buildAutomationEndpoints } from "./automations"
+import { buildHostingEndpoints } from "./hosting"
import { buildQueryEndpoints } from "./queries"
import { buildRelationshipEndpoints } from "./relationships"
import { buildRouteEndpoints } from "./routes"
@@ -35,6 +36,7 @@ export const createAPIClient = config => {
// Try to read a message from the error
let message = response.statusText
+ let json = null
try {
const json = await response.json()
if (json?.message) {
@@ -47,6 +49,7 @@ export const createAPIClient = config => {
}
return {
message,
+ json,
status: response.status,
url: response.url,
method,
@@ -58,6 +61,7 @@ export const createAPIClient = config => {
const makeError = message => {
return {
message,
+ json: null,
status: 400,
url: "",
method: "",
@@ -173,6 +177,7 @@ export const createAPIClient = config => {
...buildAttachmentEndpoints(API),
...buildAuthEndpoints(API),
...buildAutomationEndpoints(API),
+ ...buildHostingEndpoints(API),
...buildQueryEndpoints(API),
...buildRelationshipEndpoints(API),
...buildRouteEndpoints(API),