Add more work on API refactor in builder

This commit is contained in:
Andrew Kingston 2022-01-21 09:10:59 +00:00
parent e6cddcca3e
commit f100ae42fd
17 changed files with 343 additions and 211 deletions

View File

@ -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.

View File

@ -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>

View File

@ -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,
}) })

View File

@ -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)

View File

@ -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}`)

View File

@ -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)

View File

@ -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"

View File

@ -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")

View File

@ -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")
} }
} }

View File

@ -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()

View File

@ -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

View File

@ -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)
} }
} }

View File

@ -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

View File

@ -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`,
})
},
}) })

View File

@ -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,
})
},
}) })

View File

@ -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",
})
}, },
}) })

View File

@ -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