Add more work on API refactor in builder
This commit is contained in:
parent
bab0bc4266
commit
1f22b4ecfe
|
@ -14,16 +14,25 @@ export const API = createAPIClient({
|
||||||
},
|
},
|
||||||
|
|
||||||
onError: error => {
|
onError: error => {
|
||||||
const { url, message, status } = error
|
const { url, message, status, method, handled } = error || {}
|
||||||
|
|
||||||
// Log all API errors to Sentry
|
// Log all API errors to Sentry
|
||||||
// analytics.captureException(error)
|
// analytics.captureException(error)
|
||||||
|
|
||||||
|
// Log any errors that we haven't manually handled
|
||||||
|
if (!handled) {
|
||||||
|
console.error("Unhandled error from API client", error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Show a notification for any errors
|
// Show a notification for any errors
|
||||||
if (message) {
|
if (message) {
|
||||||
notifications.error(`Error fetching ${url}: ${message}`)
|
notifications.error(`Error fetching ${url}: ${message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log all errors to console
|
||||||
|
console.error(`HTTP ${status} on ${method}:${url}:\n\t${message}`)
|
||||||
|
|
||||||
// Logout on 403's
|
// Logout on 403's
|
||||||
if (status === 403) {
|
if (status === 403) {
|
||||||
// Don't do anything if fetching templates.
|
// Don't do anything if fetching templates.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { Dropzone, notifications } from "@budibase/bbui"
|
import { Dropzone, notifications } from "@budibase/bbui"
|
||||||
import api from "builderStore/api"
|
import { API } from "api"
|
||||||
|
|
||||||
export let value = []
|
export let value = []
|
||||||
export let label
|
export let label
|
||||||
|
@ -20,8 +20,12 @@
|
||||||
for (let i = 0; i < fileList.length; i++) {
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
data.append("file", fileList[i])
|
data.append("file", fileList[i])
|
||||||
}
|
}
|
||||||
const response = await api.post(`/api/attachments/process`, data, {})
|
try {
|
||||||
return await response.json()
|
return await API.uploadBuilderAttachment(data)
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error("Failed to upload attachment")
|
||||||
|
return []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
async function deployApp() {
|
async function deployApp() {
|
||||||
try {
|
try {
|
||||||
await API.deployApp()
|
await API.deployAppChanges()
|
||||||
analytics.captureEvent(Events.APP.PUBLISHED, {
|
analytics.captureEvent(Events.APP.PUBLISHED, {
|
||||||
appId: $store.appId,
|
appId: $store.appId,
|
||||||
})
|
})
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
const revert = async () => {
|
const revert = async () => {
|
||||||
try {
|
try {
|
||||||
await API.revertApp(appId)
|
await API.revertAppChanges(appId)
|
||||||
|
|
||||||
// Reset frontend state after revert
|
// Reset frontend state after revert
|
||||||
const applicationPkg = await API.fetchAppPackage(appId)
|
const applicationPkg = await API.fetchAppPackage(appId)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
Button,
|
Button,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import api from "builderStore/api"
|
import { API } from "api"
|
||||||
import clientPackage from "@budibase/client/package.json"
|
import clientPackage from "@budibase/client/package.json"
|
||||||
|
|
||||||
let updateModal
|
let updateModal
|
||||||
|
@ -18,26 +18,17 @@
|
||||||
$: revertAvailable = $store.revertableVersion != null
|
$: revertAvailable = $store.revertableVersion != null
|
||||||
|
|
||||||
const refreshAppPackage = async () => {
|
const refreshAppPackage = async () => {
|
||||||
const applicationPkg = await api.get(
|
try {
|
||||||
`/api/applications/${appId}/appPackage`
|
const pkg = await API.fetchAppPackage(appId)
|
||||||
)
|
|
||||||
const pkg = await applicationPkg.json()
|
|
||||||
if (applicationPkg.ok) {
|
|
||||||
await store.actions.initialise(pkg)
|
await store.actions.initialise(pkg)
|
||||||
} else {
|
} catch (error) {
|
||||||
throw new Error(pkg)
|
notifications.error("Error fetching app package")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const update = async () => {
|
const update = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await api.post(
|
await API.updateAppClientVersion(appId)
|
||||||
`/api/applications/${appId}/client/update`
|
|
||||||
)
|
|
||||||
const json = await response.json()
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw json.message
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't wait for the async refresh, since this causes modal flashing
|
// Don't wait for the async refresh, since this causes modal flashing
|
||||||
refreshAppPackage()
|
refreshAppPackage()
|
||||||
|
@ -47,23 +38,17 @@
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifications.error(`Error updating app: ${err}`)
|
notifications.error(`Error updating app: ${err}`)
|
||||||
}
|
}
|
||||||
|
updateModal.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
const revert = async () => {
|
const revert = async () => {
|
||||||
try {
|
try {
|
||||||
const revertableVersion = $store.revertableVersion
|
await API.revertAppClientVersion(appId)
|
||||||
const response = await api.post(
|
|
||||||
`/api/applications/${appId}/client/revert`
|
|
||||||
)
|
|
||||||
const json = await response.json()
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw json.message
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't wait for the async refresh, since this causes modal flashing
|
// Don't wait for the async refresh, since this causes modal flashing
|
||||||
refreshAppPackage()
|
refreshAppPackage()
|
||||||
notifications.success(
|
notifications.success(
|
||||||
`App reverted successfully to version ${revertableVersion}`
|
`App reverted successfully to version ${$store.revertableVersion}`
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifications.error(`Error reverting app: ${err}`)
|
notifications.error(`Error reverting app: ${err}`)
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
import { store, automationStore, hostingStore } from "builderStore"
|
import { store, automationStore, hostingStore } from "builderStore"
|
||||||
import { admin, auth } from "stores/portal"
|
import { admin, auth } from "stores/portal"
|
||||||
import { string, mixed, object } from "yup"
|
import { string, mixed, object } from "yup"
|
||||||
import api, { get, post } from "builderStore/api"
|
import { API } from "api"
|
||||||
import analytics, { Events } from "analytics"
|
import analytics, { Events } from "analytics"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { capitalise } from "helpers"
|
import { capitalise } from "helpers"
|
||||||
|
@ -99,40 +99,24 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create App
|
// Create App
|
||||||
const appResp = await post("/api/applications", data, {})
|
const createdApp = await API.createApp(data)
|
||||||
const appJson = await appResp.json()
|
|
||||||
if (!appResp.ok) {
|
|
||||||
throw new Error(appJson.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
analytics.captureEvent(Events.APP.CREATED, {
|
analytics.captureEvent(Events.APP.CREATED, {
|
||||||
name: $values.name,
|
name: $values.name,
|
||||||
appId: appJson.instance._id,
|
appId: createdApp.instance._id,
|
||||||
templateToUse,
|
templateToUse,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Select Correct Application/DB in prep for creating user
|
// Select Correct Application/DB in prep for creating user
|
||||||
const applicationPkg = await get(
|
const pkg = await API.fetchAppPackage(createdApp.instance._id)
|
||||||
`/api/applications/${appJson.instance._id}/appPackage`
|
|
||||||
)
|
|
||||||
const pkg = await applicationPkg.json()
|
|
||||||
if (applicationPkg.ok) {
|
|
||||||
await store.actions.initialise(pkg)
|
await store.actions.initialise(pkg)
|
||||||
await automationStore.actions.fetch()
|
await automationStore.actions.fetch()
|
||||||
// update checklist - incase first app
|
// Update checklist - in case first app
|
||||||
await admin.init()
|
await admin.init()
|
||||||
} else {
|
|
||||||
throw new Error(pkg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
const user = {
|
await API.updateOwnMetadata({ roleId: $values.roleId })
|
||||||
roleId: $values.roleId,
|
|
||||||
}
|
|
||||||
const userResp = await api.post(`/api/users/metadata/self`, user)
|
|
||||||
await userResp.json()
|
|
||||||
await auth.setInitInfo({})
|
await auth.setInitInfo({})
|
||||||
$goto(`/builder/app/${appJson.instance._id}`)
|
$goto(`/builder/app/${createdApp.instance._id}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
notifications.error(error)
|
notifications.error(error)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { notifications, ModalContent, Dropzone, Body } from "@budibase/bbui"
|
import { notifications, ModalContent, Dropzone, Body } from "@budibase/bbui"
|
||||||
import { post } from "builderStore/api"
|
import { API } from "api"
|
||||||
import { admin } from "stores/portal"
|
import { admin } from "stores/portal"
|
||||||
|
|
||||||
let submitting = false
|
let submitting = false
|
||||||
|
@ -9,24 +9,19 @@
|
||||||
|
|
||||||
async function importApps() {
|
async function importApps() {
|
||||||
submitting = true
|
submitting = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create form data to create app
|
// Create form data to create app
|
||||||
let data = new FormData()
|
let data = new FormData()
|
||||||
data.append("importFile", value.file)
|
data.append("importFile", value.file)
|
||||||
|
|
||||||
// Create App
|
// Create App
|
||||||
const importResp = await post("/api/cloud/import", data, {})
|
await API.importApps(data)
|
||||||
const importJson = await importResp.json()
|
|
||||||
if (!importResp.ok) {
|
|
||||||
throw new Error(importJson.message)
|
|
||||||
}
|
|
||||||
await admin.checkImportComplete()
|
await admin.checkImportComplete()
|
||||||
notifications.success("Import complete, please finish registration!")
|
notifications.success("Import complete, please finish registration!")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error(error)
|
notifications.error("Failed to import apps")
|
||||||
submitting = false
|
|
||||||
}
|
}
|
||||||
|
submitting = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -36,10 +31,10 @@
|
||||||
onConfirm={importApps}
|
onConfirm={importApps}
|
||||||
disabled={!value.file}
|
disabled={!value.file}
|
||||||
>
|
>
|
||||||
<Body
|
<Body>
|
||||||
>Please upload the file that was exported from your Cloud environment to get
|
Please upload the file that was exported from your Cloud environment to get
|
||||||
started</Body
|
started
|
||||||
>
|
</Body>
|
||||||
<Dropzone
|
<Dropzone
|
||||||
gallery={false}
|
gallery={false}
|
||||||
label="File to import"
|
label="File to import"
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
Modal,
|
Modal,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
import api from "builderStore/api"
|
import { API } from "api"
|
||||||
import { admin, auth } from "stores/portal"
|
import { admin, auth } from "stores/portal"
|
||||||
import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte"
|
import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte"
|
||||||
import ImportAppsModal from "./_components/ImportAppsModal.svelte"
|
import ImportAppsModal from "./_components/ImportAppsModal.svelte"
|
||||||
|
@ -30,11 +30,7 @@
|
||||||
try {
|
try {
|
||||||
adminUser.tenantId = tenantId
|
adminUser.tenantId = tenantId
|
||||||
// Save the admin user
|
// Save the admin user
|
||||||
const response = await api.post(`/api/global/users/init`, adminUser)
|
await API.createAdminUser(adminUser)
|
||||||
const json = await response.json()
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(json.message)
|
|
||||||
}
|
|
||||||
notifications.success(`Admin user created`)
|
notifications.success(`Admin user created`)
|
||||||
await admin.init()
|
await admin.init()
|
||||||
$goto("../portal")
|
$goto("../portal")
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import ChooseIconModal from "components/start/ChooseIconModal.svelte"
|
import ChooseIconModal from "components/start/ChooseIconModal.svelte"
|
||||||
|
|
||||||
import { store, automationStore } from "builderStore"
|
import { store, automationStore } from "builderStore"
|
||||||
import api, { del, post, get } from "builderStore/api"
|
import { API } from "api"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { apps, auth, admin, templates } from "stores/portal"
|
import { apps, auth, admin, templates } from "stores/portal"
|
||||||
import download from "downloadjs"
|
import download from "downloadjs"
|
||||||
|
@ -116,43 +116,30 @@
|
||||||
data.append("templateKey", template.key)
|
data.append("templateKey", template.key)
|
||||||
|
|
||||||
// Create App
|
// Create App
|
||||||
const appResp = await post("/api/applications", data, {})
|
const createdApp = await API.createApp(data)
|
||||||
const appJson = await appResp.json()
|
|
||||||
if (!appResp.ok) {
|
|
||||||
throw new Error(appJson.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
analytics.captureEvent(Events.APP.CREATED, {
|
analytics.captureEvent(Events.APP.CREATED, {
|
||||||
name: appName,
|
name: appName,
|
||||||
appId: appJson.instance._id,
|
appId: createdApp.instance._id,
|
||||||
template,
|
template,
|
||||||
fromTemplateMarketplace: true,
|
fromTemplateMarketplace: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Select Correct Application/DB in prep for creating user
|
// Select Correct Application/DB in prep for creating user
|
||||||
const applicationPkg = await get(
|
const pkg = await API.fetchAppPackage(createdApp.instance._id)
|
||||||
`/api/applications/${appJson.instance._id}/appPackage`
|
|
||||||
)
|
|
||||||
const pkg = await applicationPkg.json()
|
|
||||||
if (applicationPkg.ok) {
|
|
||||||
await store.actions.initialise(pkg)
|
await store.actions.initialise(pkg)
|
||||||
await automationStore.actions.fetch()
|
await automationStore.actions.fetch()
|
||||||
// update checklist - incase first app
|
// Update checklist - in case first app
|
||||||
await admin.init()
|
await admin.init()
|
||||||
} else {
|
|
||||||
throw new Error(pkg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
const userResp = await api.post(`/api/users/metadata/self`, {
|
await API.updateOwnMetadata({
|
||||||
roleId: "BASIC",
|
roleId: "BASIC",
|
||||||
})
|
})
|
||||||
await userResp.json()
|
|
||||||
await auth.setInitInfo({})
|
await auth.setInitInfo({})
|
||||||
$goto(`/builder/app/${appJson.instance._id}`)
|
$goto(`/builder/app/${createdApp.instance._id}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
notifications.error("Error creating app")
|
||||||
console.error(error)
|
console.error(error)
|
||||||
notifications.error(error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,17 +189,11 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await del(
|
await API.unpublishApp(selectedApp.prodId)
|
||||||
`/api/applications/${selectedApp.prodId}?unpublish=1`
|
|
||||||
)
|
|
||||||
if (response.status !== 200) {
|
|
||||||
const json = await response.json()
|
|
||||||
throw json.message
|
|
||||||
}
|
|
||||||
await apps.load()
|
await apps.load()
|
||||||
notifications.success("App unpublished successfully")
|
notifications.success("App unpublished successfully")
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifications.error(`Error unpublishing app: ${err}`)
|
notifications.error("Error unpublishing app")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,17 +207,13 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await del(`/api/applications/${selectedApp?.devId}`)
|
await API.deleteApp(selectedApp?.devId)
|
||||||
if (response.status !== 200) {
|
|
||||||
const json = await response.json()
|
|
||||||
throw json.message
|
|
||||||
}
|
|
||||||
await apps.load()
|
await apps.load()
|
||||||
// get checklist, just in case that was the last app
|
// Get checklist, just in case that was the last app
|
||||||
await admin.init()
|
await admin.init()
|
||||||
notifications.success("App deleted successfully")
|
notifications.success("App deleted successfully")
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifications.error(`Error deleting app: ${err}`)
|
notifications.error("Error deleting app")
|
||||||
}
|
}
|
||||||
selectedApp = null
|
selectedApp = null
|
||||||
appName = null
|
appName = null
|
||||||
|
@ -249,15 +226,11 @@
|
||||||
|
|
||||||
const releaseLock = async app => {
|
const releaseLock = async app => {
|
||||||
try {
|
try {
|
||||||
const response = await del(`/api/dev/${app.devId}/lock`)
|
await API.releaseAppLock(app.devId)
|
||||||
if (response.status !== 200) {
|
|
||||||
const json = await response.json()
|
|
||||||
throw json.message
|
|
||||||
}
|
|
||||||
await apps.load()
|
await apps.load()
|
||||||
notifications.success("Lock released successfully")
|
notifications.success("Lock released successfully")
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifications.error(`Error releasing lock: ${err}`)
|
notifications.error("Error releasing lock")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
Checkbox,
|
Checkbox,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { email } from "stores/portal"
|
import { email } from "stores/portal"
|
||||||
import api from "builderStore/api"
|
import { API } from "api"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import analytics, { Events } from "analytics"
|
import analytics, { Events } from "analytics"
|
||||||
|
|
||||||
|
@ -54,34 +54,24 @@
|
||||||
delete smtp.config.auth
|
delete smtp.config.auth
|
||||||
}
|
}
|
||||||
// Save your SMTP config
|
// Save your SMTP config
|
||||||
const response = await api.post(`/api/global/configs`, smtp)
|
|
||||||
|
|
||||||
if (response.status !== 200) {
|
|
||||||
const error = await response.text()
|
|
||||||
let message
|
|
||||||
try {
|
try {
|
||||||
message = JSON.parse(error).message
|
const savedConfig = await API.saveConfig(smtp)
|
||||||
} catch (err) {
|
smtpConfig._rev = savedConfig._rev
|
||||||
message = error
|
smtpConfig._id = savedConfig._id
|
||||||
}
|
notifications.success(`Settings saved`)
|
||||||
notifications.error(`Failed to save email settings, reason: ${message}`)
|
|
||||||
} else {
|
|
||||||
const json = await response.json()
|
|
||||||
smtpConfig._rev = json._rev
|
|
||||||
smtpConfig._id = json._id
|
|
||||||
notifications.success(`Settings saved.`)
|
|
||||||
analytics.captureEvent(Events.SMTP.SAVED)
|
analytics.captureEvent(Events.SMTP.SAVED)
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error(
|
||||||
|
`Failed to save email settings, reason: ${error?.message || "Unknown"}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchSmtp() {
|
async function fetchSmtp() {
|
||||||
loading = true
|
loading = true
|
||||||
// fetch the configs for smtp
|
try {
|
||||||
const smtpResponse = await api.get(
|
// Fetch the configs for smtp
|
||||||
`/api/global/configs/${ConfigTypes.SMTP}`
|
const smtpDoc = await API.getConfig(ConfigTypes.SMTP)
|
||||||
)
|
|
||||||
const smtpDoc = await smtpResponse.json()
|
|
||||||
|
|
||||||
if (!smtpDoc._id) {
|
if (!smtpDoc._id) {
|
||||||
smtpConfig = {
|
smtpConfig = {
|
||||||
type: ConfigTypes.SMTP,
|
type: ConfigTypes.SMTP,
|
||||||
|
@ -94,7 +84,7 @@
|
||||||
}
|
}
|
||||||
loading = false
|
loading = false
|
||||||
requireAuth = smtpConfig.config.auth != null
|
requireAuth = smtpConfig.config.auth != null
|
||||||
// always attach the auth for the forms purpose -
|
// Always attach the auth for the forms purpose -
|
||||||
// this will be removed later if required
|
// this will be removed later if required
|
||||||
if (!smtpDoc.config) {
|
if (!smtpDoc.config) {
|
||||||
smtpDoc.config = {}
|
smtpDoc.config = {}
|
||||||
|
@ -104,6 +94,9 @@
|
||||||
type: "login",
|
type: "login",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error("Error fetching SMTP config")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchSmtp()
|
fetchSmtp()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import api from "builderStore/api"
|
import { API } from "api"
|
||||||
import { auth } from "stores/portal"
|
import { auth } from "stores/portal"
|
||||||
|
|
||||||
export function createAdminStore() {
|
export function createAdminStore() {
|
||||||
|
@ -25,21 +25,19 @@ export function createAdminStore() {
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
const tenantId = get(auth).tenantId
|
const tenantId = get(auth).tenantId
|
||||||
const response = await api.get(
|
const checklist = await API.getChecklist(tenantId)
|
||||||
`/api/global/configs/checklist?tenantId=${tenantId}`
|
const totalSteps = Object.keys(checklist).length
|
||||||
)
|
const completedSteps = Object.values(checklist).filter(
|
||||||
const json = await response.json()
|
x => x?.checked
|
||||||
const totalSteps = Object.keys(json).length
|
).length
|
||||||
const completedSteps = Object.values(json).filter(x => x?.checked).length
|
|
||||||
|
|
||||||
await getEnvironment()
|
await getEnvironment()
|
||||||
admin.update(store => {
|
admin.update(store => {
|
||||||
store.loaded = true
|
store.loaded = true
|
||||||
store.checklist = json
|
store.checklist = checklist
|
||||||
store.onboardingProgress = (completedSteps / totalSteps) * 100
|
store.onboardingProgress = (completedSteps / totalSteps) * 100
|
||||||
return store
|
return store
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
admin.update(store => {
|
admin.update(store => {
|
||||||
store.checklist = null
|
store.checklist = null
|
||||||
return store
|
return store
|
||||||
|
@ -48,11 +46,15 @@ export function createAdminStore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkImportComplete() {
|
async function checkImportComplete() {
|
||||||
const response = await api.get(`/api/cloud/import/complete`)
|
try {
|
||||||
if (response.status === 200) {
|
const result = await API.checkImportComplete()
|
||||||
const json = await response.json()
|
|
||||||
admin.update(store => {
|
admin.update(store => {
|
||||||
store.importComplete = json ? json.imported : false
|
store.importComplete = result ? result.imported : false
|
||||||
|
return store
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
admin.update(store => {
|
||||||
|
store.importComplete = false
|
||||||
return store
|
return store
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -65,15 +67,14 @@ export function createAdminStore() {
|
||||||
let accountPortalUrl = ""
|
let accountPortalUrl = ""
|
||||||
let isDev = false
|
let isDev = false
|
||||||
try {
|
try {
|
||||||
const response = await api.get(`/api/system/environment`)
|
const environment = await API.getEnvironment()
|
||||||
const json = await response.json()
|
multiTenancyEnabled = environment.multiTenancy
|
||||||
multiTenancyEnabled = json.multiTenancy
|
cloud = environment.cloud
|
||||||
cloud = json.cloud
|
disableAccountPortal = environment.disableAccountPortal
|
||||||
disableAccountPortal = json.disableAccountPortal
|
accountPortalUrl = environment.accountPortalUrl
|
||||||
accountPortalUrl = json.accountPortalUrl
|
isDev = environment.isDev
|
||||||
isDev = json.isDev
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// just let it stay disabled
|
// Just let it stay disabled
|
||||||
}
|
}
|
||||||
admin.update(store => {
|
admin.update(store => {
|
||||||
store.multiTenancy = multiTenancyEnabled
|
store.multiTenancy = multiTenancyEnabled
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import api from "builderStore/api"
|
import { API } from "api"
|
||||||
import { auth } from "stores/portal"
|
import { auth } from "stores/portal"
|
||||||
|
|
||||||
const OIDC_CONFIG = {
|
const OIDC_CONFIG = {
|
||||||
|
@ -14,16 +14,17 @@ export function createOidcStore() {
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const tenantId = get(auth).tenantId
|
const tenantId = get(auth).tenantId
|
||||||
const res = await api.get(
|
try {
|
||||||
`/api/global/configs/public/oidc?tenantId=${tenantId}`
|
const config = await API.getOIDCConfig(tenantId)
|
||||||
)
|
if (Object.keys(config || {}).length) {
|
||||||
const json = await res.json()
|
// Just use the first config for now.
|
||||||
|
// We will be support multiple logins buttons later on.
|
||||||
if (json.status === 400 || Object.keys(json).length === 0) {
|
set(...config)
|
||||||
set(OIDC_CONFIG)
|
|
||||||
} else {
|
} else {
|
||||||
// Just use the first config for now. We will be support multiple logins buttons later on.
|
set(OIDC_CONFIG)
|
||||||
set(...json)
|
}
|
||||||
|
} catch (error) {
|
||||||
|
set(OIDC_CONFIG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,21 @@ export const API = createAPIClient({
|
||||||
// We could also log these to sentry.
|
// We could also log these to sentry.
|
||||||
// Or we could check error.status and redirect to login on a 403 etc.
|
// Or we could check error.status and redirect to login on a 403 etc.
|
||||||
onError: error => {
|
onError: error => {
|
||||||
if (error.message) {
|
const { status, method, url, message, handled } = error || {}
|
||||||
notificationStore.actions.error(error.message)
|
|
||||||
|
// Log any errors that we haven't manually handled
|
||||||
|
if (!handled) {
|
||||||
|
console.error("Unhandled error from API client", error)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify all errors
|
||||||
|
if (message) {
|
||||||
|
notificationStore.actions.error(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log all errors to console
|
||||||
|
console.error(`HTTP ${status} on ${method}:${url}:\n\t${message}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Patch certain endpoints with functionality specific to client apps
|
// Patch certain endpoints with functionality specific to client apps
|
||||||
|
|
|
@ -25,7 +25,7 @@ export const buildAppEndpoints = API => ({
|
||||||
/**
|
/**
|
||||||
* Deploys the current app.
|
* Deploys the current app.
|
||||||
*/
|
*/
|
||||||
deployApp: async () => {
|
deployAppChanges: async () => {
|
||||||
return await API.post({
|
return await API.post({
|
||||||
url: "/api/deploy",
|
url: "/api/deploy",
|
||||||
})
|
})
|
||||||
|
@ -35,12 +35,32 @@ export const buildAppEndpoints = API => ({
|
||||||
* Reverts an app to a previous version.
|
* Reverts an app to a previous version.
|
||||||
* @param appId the app ID to revert
|
* @param appId the app ID to revert
|
||||||
*/
|
*/
|
||||||
revertApp: async appId => {
|
revertAppChanges: async appId => {
|
||||||
return await API.post({
|
return await API.post({
|
||||||
url: `/api/dev/${appId}/revert`,
|
url: `/api/dev/${appId}/revert`,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an app's version of the client library.
|
||||||
|
* @param appId the app ID to update
|
||||||
|
*/
|
||||||
|
updateAppClientVersion: async appId => {
|
||||||
|
return await API.post({
|
||||||
|
url: `/api/applications/${appId}/client/update`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts an app's version of the client library to the previous version.
|
||||||
|
* @param appId the app ID to revert
|
||||||
|
*/
|
||||||
|
revertAppClientVersion: async appId => {
|
||||||
|
return await API.post({
|
||||||
|
url: `/api/applications/${appId}/client/revert`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a list of app deployments.
|
* Gets a list of app deployments.
|
||||||
*/
|
*/
|
||||||
|
@ -49,4 +69,57 @@ export const buildAppEndpoints = API => ({
|
||||||
url: "/api/deployments",
|
url: "/api/deployments",
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an app.
|
||||||
|
* @param app the app to create
|
||||||
|
*/
|
||||||
|
createApp: async app => {
|
||||||
|
return await API.post({
|
||||||
|
url: "/api/applications",
|
||||||
|
body: app,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports an export of all apps.
|
||||||
|
* @param apps the FormData containing the apps to import
|
||||||
|
*/
|
||||||
|
importApps: async apps => {
|
||||||
|
return await API.post({
|
||||||
|
url: "/api/cloud/import",
|
||||||
|
body: apps,
|
||||||
|
json: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unpublishes a published app.
|
||||||
|
* @param appId the production ID of the app to unpublish
|
||||||
|
*/
|
||||||
|
unpublishApp: async appId => {
|
||||||
|
return await API.delete({
|
||||||
|
url: `/api/applications/${appId}?unpublish=1`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a dev app.
|
||||||
|
* @param appId the dev app ID to delete
|
||||||
|
*/
|
||||||
|
deleteApp: async appId => {
|
||||||
|
return await API.delete({
|
||||||
|
url: `/api/applications/${appId}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the lock on a dev app.
|
||||||
|
* @param appId the dev app ID to unlock
|
||||||
|
*/
|
||||||
|
releaseAppLock: async appId => {
|
||||||
|
return await API.delete({
|
||||||
|
url: `/api/dev/${appId}/lock`,
|
||||||
|
})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
export const buildAttachmentEndpoints = API => ({
|
export const buildAttachmentEndpoints = API => ({
|
||||||
/**
|
/**
|
||||||
* Uploads an attachment to the server.
|
* Uploads an attachment to the server.
|
||||||
|
* @param data the attachment to upload
|
||||||
|
* @param tableId the table ID to upload to
|
||||||
*/
|
*/
|
||||||
uploadAttachment: async ({ data, tableId }) => {
|
uploadAttachment: async ({ data, tableId }) => {
|
||||||
return await API.post({
|
return await API.post({
|
||||||
|
@ -9,4 +11,16 @@ export const buildAttachmentEndpoints = API => ({
|
||||||
json: false,
|
json: false,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads an attachment to the server as a builder user from the builder.
|
||||||
|
* @param data the data to upload
|
||||||
|
*/
|
||||||
|
uploadBuilderAttachment: async data => {
|
||||||
|
return await API.post({
|
||||||
|
url: "/api/attachments/process",
|
||||||
|
body: data,
|
||||||
|
json: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -31,6 +31,89 @@ export const buildAuthEndpoints = API => ({
|
||||||
* Fetches the currently logged in user object
|
* Fetches the currently logged in user object
|
||||||
*/
|
*/
|
||||||
fetchSelf: async () => {
|
fetchSelf: async () => {
|
||||||
return await API.get({ url: "/api/self" })
|
return await API.get({
|
||||||
|
url: "/api/self",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the current user metadata.
|
||||||
|
* @param metadata the metadata to save
|
||||||
|
*/
|
||||||
|
updateOwnMetadata: async metadata => {
|
||||||
|
return await API.post({
|
||||||
|
url: "/api/users/metadata/self",
|
||||||
|
body: metadata,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an admin user.
|
||||||
|
* @param adminUser the admin user to create
|
||||||
|
*/
|
||||||
|
createAdminUser: async adminUser => {
|
||||||
|
return await API.post({
|
||||||
|
url: "/api/global/users/init",
|
||||||
|
body: adminUser,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a global config.
|
||||||
|
* @param config the config to save
|
||||||
|
*/
|
||||||
|
saveConfig: async config => {
|
||||||
|
return await API.post({
|
||||||
|
url: "/api/global/configs",
|
||||||
|
body: config,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a global config of a certain type.
|
||||||
|
* @param type the type to fetch
|
||||||
|
*/
|
||||||
|
getConfig: async type => {
|
||||||
|
return await API.get({
|
||||||
|
url: `/api/global/configs/${type}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the OIDC config for a certain tenant.
|
||||||
|
* @param tenantId the tenant ID to get the config for
|
||||||
|
*/
|
||||||
|
getOIDCConfig: async tenantId => {
|
||||||
|
return await API.get({
|
||||||
|
url: `/api/global/configs/public/oidc?tenantId=${tenantId}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the checklist for a specific tenant.
|
||||||
|
* @param tenantId the tenant ID to get the checklist for
|
||||||
|
*/
|
||||||
|
getChecklist: async tenantId => {
|
||||||
|
return await API.get({
|
||||||
|
url: `/api/global/configs/checklist?tenantId=${tenantId}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: find out what this is
|
||||||
|
*/
|
||||||
|
checkImportComplete: async () => {
|
||||||
|
return await API.get({
|
||||||
|
url: "/api/cloud/import/complete",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current environment details.
|
||||||
|
*/
|
||||||
|
getEnvironment: async () => {
|
||||||
|
return await API.get({
|
||||||
|
url: "/api/system/environment",
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -30,7 +30,9 @@ export const createAPIClient = config => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates an error object from an API response
|
// Generates an error object from an API response
|
||||||
const makeErrorFromResponse = async response => {
|
const makeErrorFromResponse = async (response, method) => {
|
||||||
|
console.log("making error from", response)
|
||||||
|
|
||||||
// Try to read a message from the error
|
// Try to read a message from the error
|
||||||
let message = response.statusText
|
let message = response.statusText
|
||||||
try {
|
try {
|
||||||
|
@ -47,6 +49,8 @@ export const createAPIClient = config => {
|
||||||
message,
|
message,
|
||||||
status: response.status,
|
status: response.status,
|
||||||
url: response.url,
|
url: response.url,
|
||||||
|
method,
|
||||||
|
handled: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +60,8 @@ export const createAPIClient = config => {
|
||||||
message,
|
message,
|
||||||
status: 400,
|
status: 400,
|
||||||
url: "",
|
url: "",
|
||||||
|
method: "",
|
||||||
|
handled: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,11 +116,7 @@ export const createAPIClient = config => {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const error = await makeErrorFromResponse(response)
|
throw await makeErrorFromResponse(response, method)
|
||||||
if (config?.onError) {
|
|
||||||
config.onError(error)
|
|
||||||
}
|
|
||||||
throw error
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,14 +136,21 @@ export const createAPIClient = config => {
|
||||||
return await cache[identifier]
|
return await cache[identifier]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructs an API call function for a particular HTTP method.
|
// Constructs an API call function for a particular HTTP method
|
||||||
const requestApiCall = method => async params => {
|
const requestApiCall = method => async params => {
|
||||||
|
try {
|
||||||
let { url, cache = false, external = false } = params
|
let { url, cache = false, external = false } = params
|
||||||
if (!external) {
|
if (!external) {
|
||||||
url = `/${url}`.replace("//", "/")
|
url = `/${url}`.replace("//", "/")
|
||||||
}
|
}
|
||||||
const enrichedParams = { ...params, method, url }
|
const enrichedParams = { ...params, method, url }
|
||||||
return await (cache ? makeCachedApiCall : makeApiCall)(enrichedParams)
|
return await (cache ? makeCachedApiCall : makeApiCall)(enrichedParams)
|
||||||
|
} catch (error) {
|
||||||
|
if (config?.onError) {
|
||||||
|
config.onError(error)
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the underlying core API methods
|
// Build the underlying core API methods
|
||||||
|
|
Loading…
Reference in New Issue