diff --git a/packages/builder/src/api.ts b/packages/builder/src/api.ts index 5d1a0beaeb..907354499f 100644 --- a/packages/builder/src/api.ts +++ b/packages/builder/src/api.ts @@ -8,7 +8,7 @@ import { get } from "svelte/store" import { auth, navigation } from "./stores/portal" export const API = createAPIClient({ - attachHeaders: (headers: Record) => { + attachHeaders: headers => { // Attach app ID header from store let appId = get(appStore).appId if (appId) { @@ -22,7 +22,7 @@ export const API = createAPIClient({ } }, - onError: (error: any) => { + onError: error => { const { url, message, status, method, handled } = error || {} // Log any errors that we haven't manually handled @@ -45,7 +45,7 @@ export const API = createAPIClient({ } } }, - onMigrationDetected: (appId: string) => { + onMigrationDetected: appId => { const updatingUrl = `/builder/app/updating/${appId}` if (window.location.pathname === updatingUrl) { diff --git a/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte b/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte index fd235a70f2..133d5c9eb1 100644 --- a/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte +++ b/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte @@ -98,9 +98,7 @@ async function generateAICronExpression() { loadingAICronExpression = true try { - const response = await API.generateCronExpression({ - prompt: aiCronPrompt, - }) + const response = await API.generateCronExpression(aiCronPrompt) cronExpression = response.message dispatch("change", response.message) } catch (err) { diff --git a/packages/builder/src/components/backend/DataTable/buttons/ExportButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/ExportButton.svelte index e9352045ea..fadf6d83da 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/ExportButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/ExportButton.svelte @@ -56,28 +56,19 @@ } const exportAllData = async () => { - return await API.exportView({ - viewName: view, - format: exportFormat, - }) + return await API.exportView(view, exportFormat) } const exportFilteredData = async () => { - let payload = { - tableId: view, - format: exportFormat, - search: { - paginate: false, - }, - } + let payload = {} if (selectedRows?.length) { payload.rows = selectedRows.map(row => row._id) } if (sorting) { - payload.search.sort = sorting.sortColumn - payload.search.sortOrder = sorting.sortOrder + payload.sort = sorting.sortColumn + payload.sortOrder = sorting.sortOrder } - return await API.exportRows(payload) + return await API.exportRows(view, exportFormat, payload) } const exportData = async () => { diff --git a/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte index a2e7a2a194..f96492a570 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte @@ -30,11 +30,7 @@ const importData = async () => { try { loading = true - await API.importTableData({ - tableId, - rows, - identifierFields, - }) + await API.importTableData(tableId, rows, identifierFields) notifications.success("Rows successfully imported") popover.hide() } catch (error) { diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridRowActionsButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridRowActionsButton.svelte index f61e19c19d..496f367c01 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridRowActionsButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridRowActionsButton.svelte @@ -39,9 +39,9 @@ const toggleAction = async (action, enabled) => { if (enabled) { - await rowActions.enableView(tableId, viewId, action.id) + await rowActions.enableView(tableId, action.id, viewId) } else { - await rowActions.disableView(tableId, viewId, action.id) + await rowActions.disableView(tableId, action.id, viewId) } } diff --git a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte index ce966d0daa..e4601b5372 100644 --- a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte @@ -128,11 +128,7 @@ allValid = false if (rows.length > 0) { - const response = await API.validateExistingTableImport({ - rows, - tableId, - }) - + const response = await API.validateExistingTableImport(rows, tableId) validation = response.schemaValidation invalidColumns = response.invalidColumns allValid = response.allValid diff --git a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte index 1694ec8b0e..80957a90b3 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte @@ -147,7 +147,7 @@ loading = true try { if (rows.length > 0) { - const response = await API.validateNewTableImport({ rows, schema }) + const response = await API.validateNewTableImport(rows, schema) validation = response.schemaValidation allValid = response.allValid errors = response.errors diff --git a/packages/builder/src/components/start/DuplicateAppModal.svelte b/packages/builder/src/components/start/DuplicateAppModal.svelte index 0db92ff76d..79d91e35bb 100644 --- a/packages/builder/src/components/start/DuplicateAppModal.svelte +++ b/packages/builder/src/components/start/DuplicateAppModal.svelte @@ -68,7 +68,7 @@ } try { - const app = await API.duplicateApp(data, appId) + const app = await API.duplicateApp(appId, data) appsStore.load() if (!sdk.users.isBuilder($auth.user, app?.duplicateAppId)) { // Refresh for access to created applications diff --git a/packages/builder/src/index.d.ts b/packages/builder/src/index.d.ts deleted file mode 100644 index 1cbad4f12c..0000000000 --- a/packages/builder/src/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -declare module "api" { - const API: { - getPlugins: () => Promise - createPlugin: (plugin: object) => Promise - uploadPlugin: (plugin: FormData) => Promise - deletePlugin: (id: string) => Promise - } -} diff --git a/packages/builder/src/pages/builder/app/[application]/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/_layout.svelte index 8e109d3145..bad28f89f7 100644 --- a/packages/builder/src/pages/builder/app/[application]/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_layout.svelte @@ -105,9 +105,6 @@ if (!hasSynced && application) { try { await API.syncApp(application) - // check if user has beta access - // const betaResponse = await API.checkBetaAccess($auth?.user?.email) - // betaAccess = betaResponse.access } catch (error) { notifications.error("Failed to sync with production database") } diff --git a/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/v1/[viewName]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/v1/[viewName]/index.svelte index 2c822569b7..093999559b 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/v1/[viewName]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/v1/[viewName]/index.svelte @@ -43,8 +43,7 @@ return } try { - data = await API.fetchViewData({ - name, + data = await API.fetchViewData(name, { calculation, field, groupBy, diff --git a/packages/builder/src/pages/builder/app/[application]/settings/backups/index.svelte b/packages/builder/src/pages/builder/app/[application]/settings/backups/index.svelte index e8a67b2ae1..ecdd3ee419 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/backups/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/backups/index.svelte @@ -99,21 +99,18 @@ } async function fetchBackups(filters, page, dateRange = []) { - const body = { - appId: $appStore.appId, + const opts = { ...filters, page, } - const [startDate, endDate] = dateRange if (startDate) { - body.startDate = startDate + opts.startDate = startDate } if (endDate) { - body.endDate = endDate + opts.endDate = endDate } - - const response = await backups.searchBackups(body) + const response = await backups.searchBackups($appStore.appId, opts) pageInfo.fetched(response.hasNextPage, response.nextPage) // flatten so we have an easier structure to use for the table schema @@ -123,9 +120,7 @@ async function createManualBackup() { try { loading = true - let response = await backups.createManualBackup({ - appId: $appStore.appId, - }) + let response = await backups.createManualBackup($appStore.appId) await fetchBackups(filterOpt, page) notifications.success(response.message) } catch (err) { @@ -149,24 +144,14 @@ async function handleButtonClick({ detail }) { if (detail.type === "backupDelete") { - await backups.deleteBackup({ - appId: $appStore.appId, - backupId: detail.backupId, - }) + await backups.deleteBackup($appStore.appId, detail.backupId) await fetchBackups(filterOpt, page) } else if (detail.type === "backupRestore") { - await backups.restoreBackup({ - appId: $appStore.appId, - backupId: detail.backupId, - name: detail.restoreBackupName, - }) - await fetchBackups(filterOpt, page) - } else if (detail.type === "backupUpdate") { - await backups.updateBackup({ - appId: $appStore.appId, - backupId: detail.backupId, - name: detail.name, - }) + await backups.restoreBackup( + $appStore.appId, + detail.backupId, + detail.restoreBackupName + ) await fetchBackups(filterOpt, page) } } diff --git a/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte b/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte index 41254af970..4759f8d8ce 100644 --- a/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte +++ b/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte @@ -152,8 +152,8 @@ logsPageInfo.loading() await auditLogs.search({ bookmark: logsPage, - startDate: dateRange[0], - endDate: dateRange[1], + startDate: dateRange[0] || undefined, + endDate: dateRange[1] || undefined, fullSearch: logSearchTerm, userIds: selectedUsers, appIds: selectedApps, diff --git a/packages/builder/src/pages/builder/portal/account/upgrade.svelte b/packages/builder/src/pages/builder/portal/account/upgrade.svelte index dbdedb6805..98af01ccf6 100644 --- a/packages/builder/src/pages/builder/portal/account/upgrade.svelte +++ b/packages/builder/src/pages/builder/portal/account/upgrade.svelte @@ -64,7 +64,7 @@ const activateLicenseKey = async () => { try { - await API.activateLicenseKey({ licenseKey }) + await API.activateLicenseKey(licenseKey) await auth.getSelf() await getLicenseKey() notifications.success("Successfully activated") @@ -119,7 +119,7 @@ async function activateOfflineLicense(offlineLicenseToken) { try { - await API.activateOfflineLicense({ offlineLicenseToken }) + await API.activateOfflineLicense(offlineLicenseToken) await auth.getSelf() await getOfflineLicense() notifications.success("Successfully activated") diff --git a/packages/builder/src/pages/builder/portal/settings/auth/index.svelte b/packages/builder/src/pages/builder/portal/settings/auth/index.svelte index 3ef3737249..e90cd5440e 100644 --- a/packages/builder/src/pages/builder/portal/settings/auth/index.svelte +++ b/packages/builder/src/pages/builder/portal/settings/auth/index.svelte @@ -140,10 +140,7 @@ if (image) { let data = new FormData() data.append("file", image) - await API.uploadOIDCLogo({ - name: image.name, - data, - }) + await API.uploadOIDCLogo(image.name, data) } } diff --git a/packages/builder/src/pages/builder/portal/settings/email/index.svelte b/packages/builder/src/pages/builder/portal/settings/email/index.svelte index 71687bcc70..0b9fb44bac 100644 --- a/packages/builder/src/pages/builder/portal/settings/email/index.svelte +++ b/packages/builder/src/pages/builder/portal/settings/email/index.svelte @@ -69,10 +69,7 @@ async function deleteSmtp() { // Delete the SMTP config try { - await API.deleteConfig({ - id: smtpConfig._id, - rev: smtpConfig._rev, - }) + await API.deleteConfig(smtpConfig._id, smtpConfig._rev) smtpConfig = { type: ConfigTypes.SMTP, config: { diff --git a/packages/builder/src/stores/builder/automations.js b/packages/builder/src/stores/builder/automations.js index 771ae97cf8..365f5a8e03 100644 --- a/packages/builder/src/stores/builder/automations.js +++ b/packages/builder/src/stores/builder/automations.js @@ -751,10 +751,7 @@ const automationActions = store => ({ automation.definition.trigger.inputs.rowActionId ) } else { - await API.deleteAutomation({ - automationId: automation?._id, - automationRev: automation?._rev, - }) + await API.deleteAutomation(automation?._id, automation?._rev) } store.update(state => { @@ -836,10 +833,7 @@ const automationActions = store => ({ test: async (automation, testData) => { let result try { - result = await API.testAutomation({ - automationId: automation?._id, - testData, - }) + result = await API.testAutomation(automation?._id, testData) } catch (err) { const message = err.message || err.status || JSON.stringify(err) throw `Automation test failed - ${message}` @@ -893,10 +887,7 @@ const automationActions = store => ({ }) }, clearLogErrors: async ({ automationId, appId } = {}) => { - return await API.clearAutomationLogErrors({ - automationId, - appId, - }) + return await API.clearAutomationLogErrors(automationId, appId) }, addTestDataToAutomation: data => { let newAutomation = cloneDeep(get(selectedAutomation).data) diff --git a/packages/builder/src/stores/builder/flags.js b/packages/builder/src/stores/builder/flags.js index 9f1652676c..23f41808ff 100644 --- a/packages/builder/src/stores/builder/flags.js +++ b/packages/builder/src/stores/builder/flags.js @@ -10,14 +10,11 @@ export function createFlagsStore() { set(flags) }, updateFlag: async (flag, value) => { - await API.updateFlag({ - flag, - value, - }) + await API.updateFlag(flag, value) await actions.fetch() }, toggleUiFeature: async feature => { - await API.toggleUiFeature({ value: feature }) + await API.toggleUiFeature(feature) }, } diff --git a/packages/builder/src/stores/builder/layouts.js b/packages/builder/src/stores/builder/layouts.js index 30d6935b19..b105989746 100644 --- a/packages/builder/src/stores/builder/layouts.js +++ b/packages/builder/src/stores/builder/layouts.js @@ -59,10 +59,7 @@ export class LayoutStore extends BudiStore { if (!layout?._id) { return } - await API.deleteLayout({ - layoutId: layout._id, - layoutRev: layout._rev, - }) + await API.deleteLayout(layout._id, layout._rev) this.update(state => { state.layouts = state.layouts.filter(x => x._id !== layout._id) return state diff --git a/packages/builder/src/stores/builder/navigation.js b/packages/builder/src/stores/builder/navigation.js index 86e484b0a6..c3e19a4327 100644 --- a/packages/builder/src/stores/builder/navigation.js +++ b/packages/builder/src/stores/builder/navigation.js @@ -35,10 +35,7 @@ export class NavigationStore extends BudiStore { async save(navigation) { const appId = get(appStore).appId - const app = await API.saveAppMetadata({ - appId, - metadata: { navigation }, - }) + const app = await API.saveAppMetadata(appId, { navigation }) this.syncAppNavigation(app.navigation) } diff --git a/packages/builder/src/stores/builder/permissions.js b/packages/builder/src/stores/builder/permissions.js index 0113f221c0..2fd94e35b0 100644 --- a/packages/builder/src/stores/builder/permissions.js +++ b/packages/builder/src/stores/builder/permissions.js @@ -7,18 +7,10 @@ export function createPermissionStore() { return { subscribe, save: async ({ level, role, resource }) => { - return await API.updatePermissionForResource({ - resourceId: resource, - roleId: role, - level, - }) + return await API.updatePermissionForResource(resource, role, level) }, remove: async ({ level, role, resource }) => { - return await API.removePermissionFromResource({ - resourceId: resource, - roleId: role, - level, - }) + return await API.removePermissionFromResource(resource, role, level) }, forResource: async resourceId => { return (await API.getPermissionForResource(resourceId)).permissions diff --git a/packages/builder/src/stores/builder/queries.js b/packages/builder/src/stores/builder/queries.js index b717a17f97..27607deff5 100644 --- a/packages/builder/src/stores/builder/queries.js +++ b/packages/builder/src/stores/builder/queries.js @@ -62,10 +62,7 @@ export function createQueriesStore() { } const importQueries = async ({ data, datasourceId }) => { - return await API.importQueries({ - datasourceId, - data, - }) + return await API.importQueries(datasourceId, data) } const select = id => { @@ -87,10 +84,7 @@ export function createQueriesStore() { } const deleteQuery = async query => { - await API.deleteQuery({ - queryId: query?._id, - queryRev: query?._rev, - }) + await API.deleteQuery(query._id, query._rev) store.update(state => { state.list = state.list.filter(existing => existing._id !== query._id) return state diff --git a/packages/builder/src/stores/builder/roles.js b/packages/builder/src/stores/builder/roles.js index fd3581f1d4..4d5e37434a 100644 --- a/packages/builder/src/stores/builder/roles.js +++ b/packages/builder/src/stores/builder/roles.js @@ -43,10 +43,7 @@ export function createRolesStore() { setRoles(roles) }, delete: async role => { - await API.deleteRole({ - roleId: role?._id, - roleRev: role?._rev, - }) + await API.deleteRole(role._id, role._rev) await actions.fetch() }, save: async role => { diff --git a/packages/builder/src/stores/builder/rowActions.js b/packages/builder/src/stores/builder/rowActions.js index b1f4e7067f..a7ed45e707 100644 --- a/packages/builder/src/stores/builder/rowActions.js +++ b/packages/builder/src/stores/builder/rowActions.js @@ -55,15 +55,12 @@ export class RowActionStore extends BudiStore { } // Create the action - const res = await API.rowActions.create({ - name, - tableId, - }) + const res = await API.rowActions.create(tableId, name) // Enable action on this view if adding via a view if (viewId) { await Promise.all([ - this.enableView(tableId, viewId, res.id), + this.enableView(tableId, res.id, viewId), automationStore.actions.fetch(), ]) } else { @@ -76,21 +73,13 @@ export class RowActionStore extends BudiStore { return res } - enableView = async (tableId, viewId, rowActionId) => { - await API.rowActions.enableView({ - tableId, - viewId, - rowActionId, - }) + enableView = async (tableId, rowActionId, viewId) => { + await API.rowActions.enableView(tableId, rowActionId, viewId) await this.refreshRowActions(tableId) } - disableView = async (tableId, viewId, rowActionId) => { - await API.rowActions.disableView({ - tableId, - viewId, - rowActionId, - }) + disableView = async (tableId, rowActionId, viewId) => { + await API.rowActions.disableView(tableId, rowActionId, viewId) await this.refreshRowActions(tableId) } @@ -105,21 +94,14 @@ export class RowActionStore extends BudiStore { } delete = async (tableId, rowActionId) => { - await API.rowActions.delete({ - tableId, - rowActionId, - }) + await API.rowActions.delete(tableId, rowActionId) await this.refreshRowActions(tableId) // We don't need to refresh automations as we can only delete row actions // from the automations store, so we already handle the state update there } trigger = async (sourceId, rowActionId, rowId) => { - await API.rowActions.trigger({ - sourceId, - rowActionId, - rowId, - }) + await API.rowActions.trigger(sourceId, rowActionId, rowId) } } diff --git a/packages/builder/src/stores/builder/screens.js b/packages/builder/src/stores/builder/screens.js index 10c4265e73..55fa3d5433 100644 --- a/packages/builder/src/stores/builder/screens.js +++ b/packages/builder/src/stores/builder/screens.js @@ -344,12 +344,7 @@ export class ScreenStore extends BudiStore { let deleteUrls = [] screensToDelete.forEach(screen => { // Delete the screen - promises.push( - API.deleteScreen({ - screenId: screen._id, - screenRev: screen._rev, - }) - ) + promises.push(API.deleteScreen(screen._id, screen._rev)) // Remove links to this screen deleteUrls.push(screen.routing.route) }) diff --git a/packages/builder/src/stores/builder/snippets.js b/packages/builder/src/stores/builder/snippets.js index 72ab274730..d5f9a0b2a8 100644 --- a/packages/builder/src/stores/builder/snippets.js +++ b/packages/builder/src/stores/builder/snippets.js @@ -14,19 +14,13 @@ const createsnippets = () => { ...get(store).filter(snippet => snippet.name !== updatedSnippet.name), updatedSnippet, ] - const app = await API.saveAppMetadata({ - appId: get(appStore).appId, - metadata: { snippets }, - }) + const app = await API.saveAppMetadata(get(appStore).appId, { snippets }) syncMetadata(app) } const deleteSnippet = async snippetName => { const snippets = get(store).filter(snippet => snippet.name !== snippetName) - const app = await API.saveAppMetadata({ - appId: get(appStore).appId, - metadata: { snippets }, - }) + const app = await API.saveAppMetadata(get(appStore).appId, { snippets }) syncMetadata(app) } diff --git a/packages/builder/src/stores/builder/tables.js b/packages/builder/src/stores/builder/tables.js index 88b26929ad..8b3424f507 100644 --- a/packages/builder/src/stores/builder/tables.js +++ b/packages/builder/src/stores/builder/tables.js @@ -110,10 +110,7 @@ export function createTablesStore() { if (!table?._id) { return } - await API.deleteTable({ - tableId: table._id, - tableRev: table._rev || "rev", - }) + await API.deleteTable(table._id, table._rev || "rev") replaceTable(table._id, null) } diff --git a/packages/builder/src/stores/builder/tests/navigation.test.js b/packages/builder/src/stores/builder/tests/navigation.test.js index 365b7f497b..f3775e1ed5 100644 --- a/packages/builder/src/stores/builder/tests/navigation.test.js +++ b/packages/builder/src/stores/builder/tests/navigation.test.js @@ -264,10 +264,7 @@ describe("Navigation store", () => { await ctx.test.navigationStore.save(update) - expect(saveSpy).toHaveBeenCalledWith({ - appId: "testing_123", - metadata: { navigation: update }, - }) + expect(saveSpy).toHaveBeenCalledWith("testing_123", { navigation: update }) expect(ctx.test.store.links.length).toBe(3) diff --git a/packages/builder/src/stores/builder/theme.js b/packages/builder/src/stores/builder/theme.js index 8e03e856b3..d6ec643e07 100644 --- a/packages/builder/src/stores/builder/theme.js +++ b/packages/builder/src/stores/builder/theme.js @@ -20,10 +20,7 @@ export const createThemeStore = () => { } const save = async (theme, appId) => { - const app = await API.saveAppMetadata({ - appId, - metadata: { theme }, - }) + const app = await API.saveAppMetadata(appId, { theme }) store.update(state => { state.theme = app.theme return state @@ -32,10 +29,7 @@ export const createThemeStore = () => { const saveCustom = async (theme, appId) => { const updated = { ...get(store).customTheme, ...theme } - const app = await API.saveAppMetadata({ - appId, - metadata: { customTheme: updated }, - }) + const app = await API.saveAppMetadata(appId, { customTheme: updated }) store.update(state => { state.customTheme = app.customTheme return state diff --git a/packages/builder/src/stores/portal/apps.js b/packages/builder/src/stores/portal/apps.js index 60f17da481..c543722604 100644 --- a/packages/builder/src/stores/portal/apps.js +++ b/packages/builder/src/stores/portal/apps.js @@ -128,10 +128,7 @@ export class AppsStore extends BudiStore { } async save(appId, value) { - await API.saveAppMetadata({ - appId, - metadata: value, - }) + await API.saveAppMetadata(appId, value) this.update(state => { const updatedAppIndex = state.apps.findIndex( app => app.instance._id === appId diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index 2d2b455b37..71f4841053 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -113,11 +113,7 @@ export function createAuthStore() { }, login: async creds => { const tenantId = get(store).tenantId - await API.logIn({ - username: creds.username, - password: creds.password, - tenantId, - }) + await API.logIn(tenantId, creds.username, creds.password) await actions.getSelf() }, logout: async () => { @@ -138,18 +134,11 @@ export function createAuthStore() { }, forgotPassword: async email => { const tenantId = get(store).tenantId - await API.requestForgotPassword({ - tenantId, - email, - }) + await API.requestForgotPassword(tenantId, email) }, resetPassword: async (password, resetCode) => { const tenantId = get(store).tenantId - await API.resetPassword({ - tenantId, - password, - resetCode, - }) + await API.resetPassword(tenantId, password, resetCode) }, generateAPIKey: async () => { return API.generateAPIKey() diff --git a/packages/builder/src/stores/portal/backups.js b/packages/builder/src/stores/portal/backups.js index 6ac70ef36c..e8378afc0f 100644 --- a/packages/builder/src/stores/portal/backups.js +++ b/packages/builder/src/stores/portal/backups.js @@ -11,40 +11,28 @@ export function createBackupsStore() { }) } - async function searchBackups({ - appId, - trigger, - type, - page, - startDate, - endDate, - }) { - return API.searchBackups({ appId, trigger, type, page, startDate, endDate }) + async function searchBackups(appId, opts) { + return API.searchBackups(appId, opts) } - async function restoreBackup({ appId, backupId, name }) { - return API.restoreBackup({ appId, backupId, name }) + async function restoreBackup(appId, backupId, name) { + return API.restoreBackup(appId, backupId, name) } - async function deleteBackup({ appId, backupId }) { - return API.deleteBackup({ appId, backupId }) + async function deleteBackup(appId, backupId) { + return API.deleteBackup(appId, backupId) } async function createManualBackup(appId) { return API.createManualBackup(appId) } - async function updateBackup({ appId, backupId, name }) { - return API.updateBackup({ appId, backupId, name }) - } - return { createManualBackup, searchBackups, selectBackup, deleteBackup, restoreBackup, - updateBackup, subscribe: store.subscribe, } } diff --git a/packages/builder/src/stores/portal/backups.test.js b/packages/builder/src/stores/portal/backups.test.js index 18feb01938..16c487e6e7 100644 --- a/packages/builder/src/stores/portal/backups.test.js +++ b/packages/builder/src/stores/portal/backups.test.js @@ -20,7 +20,6 @@ vi.mock("api", () => { restoreBackup: vi.fn(() => "restoreBackupReturn"), deleteBackup: vi.fn(() => "deleteBackupReturn"), createManualBackup: vi.fn(() => "createManualBackupReturn"), - updateBackup: vi.fn(() => "updateBackupReturn"), }, } }) @@ -61,8 +60,7 @@ describe("backups store", () => { ctx.page = "page" ctx.startDate = "startDate" ctx.endDate = "endDate" - ctx.value = await ctx.returnedStore.searchBackups({ - appId: ctx.appId, + ctx.value = await ctx.returnedStore.searchBackups(ctx.appId, { trigger: ctx.trigger, type: ctx.type, page: ctx.page, @@ -73,8 +71,7 @@ describe("backups store", () => { it("calls and returns the API searchBackups method", ctx => { expect(API.searchBackups).toHaveBeenCalledTimes(1) - expect(API.searchBackups).toHaveBeenCalledWith({ - appId: ctx.appId, + expect(API.searchBackups).toHaveBeenCalledWith(ctx.appId, { trigger: ctx.trigger, type: ctx.type, page: ctx.page, @@ -103,18 +100,12 @@ describe("backups store", () => { beforeEach(async ctx => { ctx.appId = "appId" ctx.backupId = "backupId" - ctx.value = await ctx.returnedStore.deleteBackup({ - appId: ctx.appId, - backupId: ctx.backupId, - }) + ctx.value = await ctx.returnedStore.deleteBackup(ctx.appId, ctx.backupId) }) it("calls and returns the API deleteBackup method", ctx => { expect(API.deleteBackup).toHaveBeenCalledTimes(1) - expect(API.deleteBackup).toHaveBeenCalledWith({ - appId: ctx.appId, - backupId: ctx.backupId, - }) + expect(API.deleteBackup).toHaveBeenCalledWith(ctx.appId, ctx.backupId) expect(ctx.value).toBe("deleteBackupReturn") }) }) @@ -124,47 +115,24 @@ describe("backups store", () => { ctx.appId = "appId" ctx.backupId = "backupId" ctx.$name = "name" // `name` is used by some sort of internal ctx thing and is readonly - ctx.value = await ctx.returnedStore.restoreBackup({ - appId: ctx.appId, - backupId: ctx.backupId, - name: ctx.$name, - }) + ctx.value = await ctx.returnedStore.restoreBackup( + ctx.appId, + ctx.backupId, + ctx.$name + ) }) it("calls and returns the API restoreBackup method", ctx => { expect(API.restoreBackup).toHaveBeenCalledTimes(1) - expect(API.restoreBackup).toHaveBeenCalledWith({ - appId: ctx.appId, - backupId: ctx.backupId, - name: ctx.$name, - }) + expect(API.restoreBackup).toHaveBeenCalledWith( + ctx.appId, + ctx.backupId, + ctx.$name + ) expect(ctx.value).toBe("restoreBackupReturn") }) }) - describe("updateBackup", () => { - beforeEach(async ctx => { - ctx.appId = "appId" - ctx.backupId = "backupId" - ctx.$name = "name" // `name` is used by some sort of internal ctx thing and is readonly - ctx.value = await ctx.returnedStore.updateBackup({ - appId: ctx.appId, - backupId: ctx.backupId, - name: ctx.$name, - }) - }) - - it("calls and returns the API updateBackup method", ctx => { - expect(API.updateBackup).toHaveBeenCalledTimes(1) - expect(API.updateBackup).toHaveBeenCalledWith({ - appId: ctx.appId, - backupId: ctx.backupId, - name: ctx.$name, - }) - expect(ctx.value).toBe("updateBackupReturn") - }) - }) - describe("subscribe", () => { it("calls and returns the API updateBackup method", ctx => { expect(ctx.returnedStore.subscribe).toBe(ctx.writableReturn.subscribe) diff --git a/packages/builder/src/stores/portal/groups.js b/packages/builder/src/stores/portal/groups.js index 1edc8a461c..f81dee068b 100644 --- a/packages/builder/src/stores/portal/groups.js +++ b/packages/builder/src/stores/portal/groups.js @@ -46,10 +46,7 @@ export function createGroupsStore() { }, delete: async group => { - await API.deleteGroup({ - id: group._id, - rev: group._rev, - }) + await API.deleteGroup(group._id, group._rev) store.update(state => { state = state.filter(state => state._id !== group._id) return state @@ -89,11 +86,11 @@ export function createGroupsStore() { }, addGroupAppBuilder: async (groupId, appId) => { - return await API.addGroupAppBuilder({ groupId, appId }) + return await API.addGroupAppBuilder(groupId, appId) }, removeGroupAppBuilder: async (groupId, appId) => { - return await API.removeGroupAppBuilder({ groupId, appId }) + return await API.removeGroupAppBuilder(groupId, appId) }, } diff --git a/packages/builder/src/stores/portal/navigation.ts b/packages/builder/src/stores/portal/navigation.ts index 2b230622f6..4eb50bc84f 100644 --- a/packages/builder/src/stores/portal/navigation.ts +++ b/packages/builder/src/stores/portal/navigation.ts @@ -2,13 +2,13 @@ import { writable } from "svelte/store" type GotoFuncType = (path: string) => void -interface Store { +interface PortalNavigationStore { initialisated: boolean goto: GotoFuncType } export function createNavigationStore() { - const store = writable({ + const store = writable({ initialisated: false, goto: undefined as any, }) diff --git a/packages/builder/src/stores/portal/plugins.ts b/packages/builder/src/stores/portal/plugins.ts index 15110a852b..0794e563eb 100644 --- a/packages/builder/src/stores/portal/plugins.ts +++ b/packages/builder/src/stores/portal/plugins.ts @@ -1,17 +1,13 @@ import { writable } from "svelte/store" import { PluginSource } from "constants/index" - +import { Plugin } from "@budibase/types" import { API } from "api" -interface Plugin { - _id: string -} - export function createPluginsStore() { const { subscribe, set, update } = writable([]) async function load() { - const plugins = await API.getPlugins() + const plugins: Plugin[] = await API.getPlugins() set(plugins) } diff --git a/packages/builder/src/stores/portal/users.js b/packages/builder/src/stores/portal/users.js index 747dc5144d..88467ac612 100644 --- a/packages/builder/src/stores/portal/users.js +++ b/packages/builder/src/stores/portal/users.js @@ -35,7 +35,24 @@ export function createUsersStore() { } async function invite(payload) { - return API.inviteUsers(payload) + const users = payload.map(user => { + let builder = undefined + if (user.admin || user.builder) { + builder = { global: true } + } else if (user.creator) { + builder = { creator: true } + } + return { + email: user.email, + userInfo: { + admin: user.admin ? { global: true } : undefined, + builder, + userGroups: user.groups, + roles: user.apps ? user.apps : undefined, + }, + } + }) + return API.inviteUsers(users) } async function removeInvites(payload) { @@ -60,7 +77,7 @@ export function createUsersStore() { } async function updateInvite(invite) { - return API.updateUserInvite(invite) + return API.updateUserInvite(invite.code, invite) } async function create(data) { @@ -93,10 +110,7 @@ export function createUsersStore() { return body }) - const response = await API.createUsers({ - users: mappedUsers, - groups: data.groups, - }) + const response = await API.createUsers(mappedUsers, data.groups) // re-search from first page await search() @@ -108,8 +122,8 @@ export function createUsersStore() { update(users => users.filter(user => user._id !== id)) } - async function getUserCountByApp({ appId }) { - return await API.getUserCountByApp({ appId }) + async function getUserCountByApp(appId) { + return await API.getUserCountByApp(appId) } async function bulkDelete(users) { @@ -121,11 +135,11 @@ export function createUsersStore() { } async function addAppBuilder(userId, appId) { - return await API.addAppBuilder({ userId, appId }) + return await API.addAppBuilder(userId, appId) } async function removeAppBuilder(userId, appId) { - return await API.removeAppBuilder({ userId, appId }) + return await API.removeAppBuilder(userId, appId) } async function getAccountHolder() { diff --git a/packages/client/src/api/patches.js b/packages/client/src/api/patches.js index faad9c81ec..5413379b72 100644 --- a/packages/client/src/api/patches.js +++ b/packages/client/src/api/patches.js @@ -77,12 +77,11 @@ export const patchAPI = API => { return await enrichRows(rows, tableId) } const searchTable = API.searchTable - API.searchTable = async params => { - const tableId = params?.tableId - const output = await searchTable(params) + API.searchTable = async (sourceId, opts) => { + const output = await searchTable(sourceId, opts) return { ...output, - rows: await enrichRows(output?.rows, tableId), + rows: await enrichRows(output.rows, sourceId), } } const fetchViewData = API.fetchViewData diff --git a/packages/client/src/components/app/forms/AttachmentField.svelte b/packages/client/src/components/app/forms/AttachmentField.svelte index 27286a8666..d9a91016cb 100644 --- a/packages/client/src/components/app/forms/AttachmentField.svelte +++ b/packages/client/src/components/app/forms/AttachmentField.svelte @@ -49,10 +49,7 @@ data.append("file", fileList[i]) } try { - return await API.uploadAttachment({ - data, - tableId: formContext?.dataSource?.tableId, - }) + return await API.uploadAttachment(formContext?.dataSource?.tableId, data) } catch (error) { return [] } diff --git a/packages/client/src/components/app/forms/S3Upload.svelte b/packages/client/src/components/app/forms/S3Upload.svelte index 0147cbca6e..936eb14f91 100644 --- a/packages/client/src/components/app/forms/S3Upload.svelte +++ b/packages/client/src/components/app/forms/S3Upload.svelte @@ -80,12 +80,7 @@ const upload = async () => { loading = true try { - const res = await API.externalUpload({ - datasourceId, - bucket, - key, - data, - }) + const res = await API.externalUpload(datasourceId, bucket, key, data) notificationStore.actions.success("File uploaded successfully") loading = false return res diff --git a/packages/client/src/components/app/forms/SignatureField.svelte b/packages/client/src/components/app/forms/SignatureField.svelte index bdae148368..a7a7dc3206 100644 --- a/packages/client/src/components/app/forms/SignatureField.svelte +++ b/packages/client/src/components/app/forms/SignatureField.svelte @@ -31,10 +31,10 @@ let attachRequest = new FormData() attachRequest.append("file", signatureFile) - const resp = await API.uploadAttachment({ - data: attachRequest, - tableId: formContext?.dataSource?.tableId, - }) + const resp = await API.uploadAttachment( + formContext?.dataSource?.tableId, + attachRequest + ) const [signatureAttachment] = resp updateValue = signatureAttachment } else { diff --git a/packages/client/src/utils/blocks.js b/packages/client/src/utils/blocks.js index f9452e47a9..88cf4b095b 100644 --- a/packages/client/src/utils/blocks.js +++ b/packages/client/src/utils/blocks.js @@ -1,7 +1,7 @@ import { makePropSafe as safe } from "@budibase/string-templates" import { API } from "../api/index.js" import { UILogicalOperator } from "@budibase/types" -import { OnEmptyFilter } from "@budibase/frontend-core/src/constants.js" +import { Constants } from "@budibase/frontend-core" // Map of data types to component types for search fields inside blocks const schemaComponentMap = { @@ -108,7 +108,7 @@ export const enrichFilter = (filter, columns, formId) => { return { logicalOperator: UILogicalOperator.ALL, - onEmptyFilter: OnEmptyFilter.RETURN_ALL, + onEmptyFilter: Constants.OnEmptyFilter.RETURN_ALL, groups: [ ...(filter?.groups || []), { diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index e2b0071042..9d0bddcc92 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -147,7 +147,7 @@ const fetchRowHandler = async action => { if (tableId && rowId) { try { - const row = await API.fetchRow({ tableId, rowId }) + const row = await API.fetchRow(tableId, rowId) return { row } } catch (error) { @@ -192,7 +192,7 @@ const deleteRowHandler = async action => { return false } - const resp = await API.deleteRows({ tableId, rows: requestConfig }) + const resp = await API.deleteRows(tableId, requestConfig) if (!notificationOverride) { notificationStore.actions.success( @@ -251,17 +251,14 @@ const navigationHandler = action => { } const queryExecutionHandler = async action => { - const { datasourceId, queryId, queryParams, notificationOverride } = - action.parameters + const { queryId, queryParams, notificationOverride } = action.parameters try { const query = await API.fetchQueryDefinition(queryId) if (query?.datasourceId == null) { notificationStore.actions.error("That query couldn't be found") return false } - const result = await API.executeQuery({ - datasourceId, - queryId, + const result = await API.executeQuery(queryId, { parameters: queryParams, }) @@ -381,10 +378,8 @@ const exportDataHandler = async action => { if (typeof rows[0] !== "string") { rows = rows.map(row => row._id) } - const data = await API.exportRows({ - tableId, + const data = await API.exportRows(tableId, type, { rows, - format: type, columns: columns?.map(column => column.name || column), delimiter, customHeaders, @@ -454,12 +449,7 @@ const downloadFileHandler = async action => { const { type } = action.parameters if (type === "attachment") { const { tableId, rowId, attachmentColumn } = action.parameters - const res = await API.downloadAttachment( - tableId, - rowId, - attachmentColumn, - { suppressErrors: true } - ) + const res = await API.downloadAttachment(tableId, rowId, attachmentColumn) await downloadStream(res) return } @@ -495,11 +485,7 @@ const downloadFileHandler = async action => { const rowActionHandler = async action => { const { resourceId, rowId, rowActionId } = action.parameters - await API.rowActions.trigger({ - rowActionId, - sourceId: resourceId, - rowId, - }) + await API.rowActions.trigger(resourceId, rowActionId, rowId) // Refresh related datasources await dataSourceStore.actions.invalidateDataSource(resourceId, { invalidateRelationships: true, diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index 44ffd6580f..8377b13ea2 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -4,7 +4,7 @@ "description": "Budibase frontend core libraries used in builder and client", "author": "Budibase", "license": "MPL-2.0", - "svelte": "src/index.js", + "svelte": "./src/index.ts", "dependencies": { "@budibase/bbui": "*", "@budibase/shared-core": "*", diff --git a/packages/frontend-core/src/api/ai.js b/packages/frontend-core/src/api/ai.js deleted file mode 100644 index 7fa756a19e..0000000000 --- a/packages/frontend-core/src/api/ai.js +++ /dev/null @@ -1,11 +0,0 @@ -export const buildAIEndpoints = API => ({ - /** - * Generates a cron expression from a prompt - */ - generateCronExpression: async ({ prompt }) => { - return await API.post({ - url: "/api/ai/cron", - body: { prompt }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/ai.ts b/packages/frontend-core/src/api/ai.ts new file mode 100644 index 0000000000..3160273301 --- /dev/null +++ b/packages/frontend-core/src/api/ai.ts @@ -0,0 +1,17 @@ +import { BaseAPIClient } from "./types" + +export interface AIEndpoints { + generateCronExpression: (prompt: string) => Promise<{ message: string }> +} + +export const buildAIEndpoints = (API: BaseAPIClient): AIEndpoints => ({ + /** + * Generates a cron expression from a prompt + */ + generateCronExpression: async prompt => { + return await API.post({ + url: "/api/ai/cron", + body: { prompt }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/analytics.js b/packages/frontend-core/src/api/analytics.js deleted file mode 100644 index df56bc938e..0000000000 --- a/packages/frontend-core/src/api/analytics.js +++ /dev/null @@ -1,17 +0,0 @@ -export const buildAnalyticsEndpoints = API => ({ - /** - * Gets the current status of analytics for this environment - */ - getAnalyticsStatus: async () => { - return await API.get({ - url: "/api/bbtel", - }) - }, - analyticsPing: async ({ source, embedded }) => { - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone - return await API.post({ - url: "/api/bbtel/ping", - body: { source, timezone, embedded }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/analytics.ts b/packages/frontend-core/src/api/analytics.ts new file mode 100644 index 0000000000..b57a8e5925 --- /dev/null +++ b/packages/frontend-core/src/api/analytics.ts @@ -0,0 +1,39 @@ +import { BaseAPIClient } from "./types" +import { + AnalyticsEnabledResponse, + AnalyticsPingRequest, + AnalyticsPingResponse, +} from "@budibase/types" + +export interface AnalyticsEndpoints { + getAnalyticsStatus: () => Promise + analyticsPing: ( + payload: Omit + ) => Promise +} + +export const buildAnalyticsEndpoints = ( + API: BaseAPIClient +): AnalyticsEndpoints => ({ + /** + * Gets the current status of analytics for this environment + */ + getAnalyticsStatus: async () => { + return await API.get({ + url: "/api/bbtel", + }) + }, + + /** + * Notifies analytics of a certain environment + */ + analyticsPing: async request => { + return await API.post({ + url: "/api/bbtel/ping", + body: { + ...request, + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/app.js b/packages/frontend-core/src/api/app.ts similarity index 61% rename from packages/frontend-core/src/api/app.js rename to packages/frontend-core/src/api/app.ts index de1703373b..eab5d33567 100644 --- a/packages/frontend-core/src/api/app.js +++ b/packages/frontend-core/src/api/app.ts @@ -1,6 +1,72 @@ import { sdk } from "@budibase/shared-core" +import { BaseAPIClient } from "./types" +import { + AddAppSampleDataResponse, + ClearDevLockResponse, + CreateAppRequest, + CreateAppResponse, + DeleteAppResponse, + DuplicateAppRequest, + DuplicateAppResponse, + FetchAppDefinitionResponse, + FetchAppPackageResponse, + FetchAppsResponse, + FetchDeploymentResponse, + GetDiagnosticsResponse, + ImportToUpdateAppRequest, + ImportToUpdateAppResponse, + PublishAppResponse, + RevertAppClientResponse, + RevertAppResponse, + SetRevertableAppVersionRequest, + SetRevertableAppVersionResponse, + SyncAppResponse, + UnpublishAppResponse, + UpdateAppClientResponse, + UpdateAppRequest, + UpdateAppResponse, +} from "@budibase/types" -export const buildAppEndpoints = API => ({ +export interface AppEndpoints { + fetchAppPackage: (appId: string) => Promise + saveAppMetadata: ( + appId: string, + metadata: UpdateAppRequest + ) => Promise + unpublishApp: (appId: string) => Promise + publishAppChanges: (appId: string) => Promise + revertAppChanges: (appId: string) => Promise + updateAppClientVersion: (appId: string) => Promise + revertAppClientVersion: (appId: string) => Promise + releaseAppLock: (appId: string) => Promise + getAppDeployments: () => Promise + createApp: (app: CreateAppRequest) => Promise + deleteApp: (appId: string) => Promise + duplicateApp: ( + appId: string, + app: DuplicateAppRequest + ) => Promise + updateAppFromExport: ( + appId: string, + body: ImportToUpdateAppRequest + ) => Promise + fetchSystemDebugInfo: () => Promise + syncApp: (appId: string) => Promise + getApps: () => Promise + fetchComponentLibDefinitions: ( + appId: string + ) => Promise + setRevertableVersion: ( + appId: string, + revertableVersion: string + ) => Promise + addSampleData: (appId: string) => Promise + + // Missing request or response types + importApps: (apps: any) => Promise +} + +export const buildAppEndpoints = (API: BaseAPIClient): AppEndpoints => ({ /** * Fetches screen definition for an app. * @param appId the ID of the app to fetch from @@ -16,7 +82,7 @@ export const buildAppEndpoints = API => ({ * @param appId the ID of the app to update * @param metadata the app metadata to save */ - saveAppMetadata: async ({ appId, metadata }) => { + saveAppMetadata: async (appId, metadata) => { return await API.put({ url: `/api/applications/${appId}`, body: metadata, @@ -87,7 +153,7 @@ export const buildAppEndpoints = API => ({ * Duplicate an existing app * @param app the app to dupe */ - duplicateApp: async (app, appId) => { + duplicateApp: async (appId, app) => { return await API.post({ url: `/api/applications/${appId}/duplicate`, body: app, @@ -184,7 +250,7 @@ export const buildAppEndpoints = API => ({ /** * Fetches the definitions for component library components. This includes * their props and other metadata from components.json. - * @param {string} appId - ID of the currently running app + * @param appId ID of the currently running app */ fetchComponentLibDefinitions: async appId => { return await API.get({ @@ -192,14 +258,27 @@ export const buildAppEndpoints = API => ({ }) }, + /** + * Adds sample data to an app + * @param appId the app ID + */ addSampleData: async appId => { return await API.post({ url: `/api/applications/${appId}/sample`, }) }, + /** + * Sets the revertable version of an app. + * Used when manually reverting to older client versions. + * @param appId the app ID + * @param revertableVersion the version number + */ setRevertableVersion: async (appId, revertableVersion) => { - return await API.post({ + return await API.post< + SetRevertableAppVersionRequest, + SetRevertableAppVersionResponse + >({ url: `/api/applications/${appId}/setRevertableVersion`, body: { revertableVersion, diff --git a/packages/frontend-core/src/api/attachments.js b/packages/frontend-core/src/api/attachments.js deleted file mode 100644 index 72f280d99d..0000000000 --- a/packages/frontend-core/src/api/attachments.js +++ /dev/null @@ -1,78 +0,0 @@ -export const buildAttachmentEndpoints = API => { - /** - * Generates a signed URL to upload a file to an external datasource. - * @param datasourceId the ID of the datasource to upload to - * @param bucket the name of the bucket to upload to - * @param key the name of the file to upload to - */ - const getSignedDatasourceURL = async ({ datasourceId, bucket, key }) => { - return await API.post({ - url: `/api/attachments/${datasourceId}/url`, - body: { bucket, key }, - }) - } - - return { - getSignedDatasourceURL, - - /** - * Uploads an attachment to the server. - * @param data the attachment to upload - * @param tableId the table ID to upload to - */ - uploadAttachment: async ({ data, tableId }) => { - return await API.post({ - url: `/api/attachments/${tableId}/upload`, - body: data, - 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, - }) - }, - - /** - * Uploads a file to an external datasource. - * @param datasourceId the ID of the datasource to upload to - * @param bucket the name of the bucket to upload to - * @param key the name of the file to upload to - * @param data the file to upload - */ - externalUpload: async ({ datasourceId, bucket, key, data }) => { - const { signedUrl, publicUrl } = await getSignedDatasourceURL({ - datasourceId, - bucket, - key, - }) - await API.put({ - url: signedUrl, - body: data, - json: false, - external: true, - }) - return { publicUrl } - }, - /** - * Download an attachment from a row given its column name. - * @param datasourceId the ID of the datasource to download from - * @param rowId the ID of the row to download from - * @param columnName the column name to download - */ - downloadAttachment: async (datasourceId, rowId, columnName, options) => { - return await API.get({ - url: `/api/${datasourceId}/rows/${rowId}/attachment/${columnName}`, - parseResponse: response => response, - suppressErrors: options?.suppressErrors, - }) - }, - } -} diff --git a/packages/frontend-core/src/api/attachments.ts b/packages/frontend-core/src/api/attachments.ts new file mode 100644 index 0000000000..0cedfb2cf1 --- /dev/null +++ b/packages/frontend-core/src/api/attachments.ts @@ -0,0 +1,121 @@ +import { + DownloadAttachmentResponse, + GetSignedUploadUrlRequest, + GetSignedUploadUrlResponse, + ProcessAttachmentResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface AttachmentEndpoints { + downloadAttachment: ( + datasourceId: string, + rowId: string, + columnName: string + ) => Promise + getSignedDatasourceURL: ( + datasourceId: string, + bucket: string, + key: string + ) => Promise + uploadAttachment: ( + tableId: string, + data: any + ) => Promise + uploadBuilderAttachment: (data: any) => Promise + externalUpload: ( + datasourceId: string, + bucket: string, + key: string, + data: any + ) => Promise<{ publicUrl: string | undefined }> +} + +export const buildAttachmentEndpoints = ( + API: BaseAPIClient +): AttachmentEndpoints => { + const endpoints: Pick = { + /** + * Generates a signed URL to upload a file to an external datasource. + * @param datasourceId the ID of the datasource to upload to + * @param bucket the name of the bucket to upload to + * @param key the name of the file to upload to + */ + getSignedDatasourceURL: async (datasourceId, bucket, key) => { + return await API.post< + GetSignedUploadUrlRequest, + GetSignedUploadUrlResponse + >({ + url: `/api/attachments/${datasourceId}/url`, + body: { bucket, key }, + }) + }, + } + + return { + ...endpoints, + + /** + * Uploads an attachment to the server. + * @param data the attachment to upload + * @param tableId the table ID to upload to + */ + uploadAttachment: async (tableId, data) => { + return await API.post({ + url: `/api/attachments/${tableId}/upload`, + body: data, + 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, + }) + }, + + /** + * Uploads a file to an external datasource. + * @param datasourceId the ID of the datasource to upload to + * @param bucket the name of the bucket to upload to + * @param key the name of the file to upload to + * @param data the file to upload + */ + externalUpload: async (datasourceId, bucket, key, data) => { + const { signedUrl, publicUrl } = await endpoints.getSignedDatasourceURL( + datasourceId, + bucket, + key + ) + if (!signedUrl) { + return { publicUrl: undefined } + } + await API.put({ + url: signedUrl, + body: data, + json: false, + external: true, + }) + return { publicUrl } + }, + + /** + * Download an attachment from a row given its column name. + * @param datasourceId the ID of the datasource to download from + * @param rowId the ID of the row to download from + * @param columnName the column name to download + */ + downloadAttachment: async (datasourceId, rowId, columnName) => { + return await API.get({ + url: `/api/${datasourceId}/rows/${rowId}/attachment/${columnName}`, + parseResponse: response => response as any, + suppressErrors: true, + }) + }, + } +} diff --git a/packages/frontend-core/src/api/auditLogs.js b/packages/frontend-core/src/api/auditLogs.js deleted file mode 100644 index c4230df6d9..0000000000 --- a/packages/frontend-core/src/api/auditLogs.js +++ /dev/null @@ -1,63 +0,0 @@ -const buildOpts = ({ - bookmark, - userIds, - appIds, - startDate, - endDate, - fullSearch, - events, -}) => { - const opts = {} - - if (bookmark) { - opts.bookmark = bookmark - } - - if (startDate && endDate) { - opts.startDate = startDate - opts.endDate = endDate - } else if (startDate && !endDate) { - opts.startDate = startDate - } - - if (fullSearch) { - opts.fullSearch = fullSearch - } - - if (events.length) { - opts.events = events - } - - if (userIds.length) { - opts.userIds = userIds - } - - if (appIds.length) { - opts.appIds = appIds - } - - return opts -} - -export const buildAuditLogsEndpoints = API => ({ - /** - * Gets a list of users in the current tenant. - */ - searchAuditLogs: async opts => { - return await API.post({ - url: `/api/global/auditlogs/search`, - body: buildOpts(opts), - }) - }, - - getEventDefinitions: async () => { - return await API.get({ - url: `/api/global/auditlogs/definitions`, - }) - }, - - getDownloadUrl: opts => { - const query = encodeURIComponent(JSON.stringify(opts)) - return `/api/global/auditlogs/download?query=${query}` - }, -}) diff --git a/packages/frontend-core/src/api/auditLogs.ts b/packages/frontend-core/src/api/auditLogs.ts new file mode 100644 index 0000000000..e0407f0412 --- /dev/null +++ b/packages/frontend-core/src/api/auditLogs.ts @@ -0,0 +1,35 @@ +import { + SearchAuditLogsRequest, + SearchAuditLogsResponse, + DefinitionsAuditLogsResponse, + DownloadAuditLogsRequest, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface AuditLogEndpoints { + searchAuditLogs: ( + opts: SearchAuditLogsRequest + ) => Promise + getEventDefinitions: () => Promise + getDownloadUrl: (opts: DownloadAuditLogsRequest) => string +} + +export const buildAuditLogEndpoints = ( + API: BaseAPIClient +): AuditLogEndpoints => ({ + searchAuditLogs: async opts => { + return await API.post({ + url: `/api/global/auditlogs/search`, + body: opts, + }) + }, + getEventDefinitions: async () => { + return await API.get({ + url: `/api/global/auditlogs/definitions`, + }) + }, + getDownloadUrl: opts => { + const query = encodeURIComponent(JSON.stringify(opts)) + return `/api/global/auditlogs/download?query=${query}` + }, +}) diff --git a/packages/frontend-core/src/api/auth.js b/packages/frontend-core/src/api/auth.ts similarity index 51% rename from packages/frontend-core/src/api/auth.js rename to packages/frontend-core/src/api/auth.ts index 9289d71239..7d429c0c82 100644 --- a/packages/frontend-core/src/api/auth.js +++ b/packages/frontend-core/src/api/auth.ts @@ -1,12 +1,46 @@ -export const buildAuthEndpoints = API => ({ +import { + GetInitInfoResponse, + LoginRequest, + LoginResponse, + LogoutResponse, + PasswordResetRequest, + PasswordResetResponse, + PasswordResetUpdateRequest, + PasswordResetUpdateResponse, + SetInitInfoRequest, + SetInitInfoResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface AuthEndpoints { + logIn: ( + tenantId: string, + username: string, + password: string + ) => Promise + logOut: () => Promise + requestForgotPassword: ( + tenantId: string, + email: string + ) => Promise + resetPassword: ( + tenantId: string, + password: string, + resetCode: string + ) => Promise + setInitInfo: (info: SetInitInfoRequest) => Promise + getInitInfo: () => Promise +} + +export const buildAuthEndpoints = (API: BaseAPIClient): AuthEndpoints => ({ /** * Performs a login request. * @param tenantId the ID of the tenant to log in to * @param username the username (email) * @param password the password */ - logIn: async ({ tenantId, username, password }) => { - return await API.post({ + logIn: async (tenantId, username, password) => { + return await API.post({ url: `/api/global/auth/${tenantId}/login`, body: { username, @@ -49,8 +83,8 @@ export const buildAuthEndpoints = API => ({ * @param tenantId the ID of the tenant the user is in * @param email the email address of the user */ - requestForgotPassword: async ({ tenantId, email }) => { - return await API.post({ + requestForgotPassword: async (tenantId, email) => { + return await API.post({ url: `/api/global/auth/${tenantId}/reset`, body: { email, @@ -64,8 +98,11 @@ export const buildAuthEndpoints = API => ({ * @param password the new password to set * @param resetCode the reset code to authenticate the request */ - resetPassword: async ({ tenantId, password, resetCode }) => { - return await API.post({ + resetPassword: async (tenantId, password, resetCode) => { + return await API.post< + PasswordResetUpdateRequest, + PasswordResetUpdateResponse + >({ url: `/api/global/auth/${tenantId}/reset/update`, body: { password, diff --git a/packages/frontend-core/src/api/automations.js b/packages/frontend-core/src/api/automations.js deleted file mode 100644 index 37a834cf04..0000000000 --- a/packages/frontend-core/src/api/automations.js +++ /dev/null @@ -1,111 +0,0 @@ -export const buildAutomationEndpoints = API => ({ - /** - * Executes an automation. Must have "App Action" trigger. - * @param automationId the ID of the automation to trigger - * @param fields the fields to trigger the automation with - */ - triggerAutomation: async ({ automationId, fields, timeout }) => { - return await API.post({ - url: `/api/automations/${automationId}/trigger`, - body: { fields, timeout }, - }) - }, - - /** - * 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}`, - }) - }, - - /** - * Get the logs for the app, or by automation ID. - * @param automationId The ID of the automation to get logs for. - * @param startDate An ISO date string to state the start of the date range. - * @param status The status, error or success. - * @param page The page to retrieve. - */ - getAutomationLogs: async ({ automationId, startDate, status, page }) => { - return await API.post({ - url: "/api/automations/logs/search", - body: { - automationId, - startDate, - status, - page, - }, - }) - }, - - /** - * Clears automation log errors (which are creating notification) for - * automation or the app. - * @param automationId optional - the ID of the automation to clear errors for. - * @param appId The app ID to clear errors for. - */ - clearAutomationLogErrors: async ({ automationId, appId }) => { - return await API.delete({ - url: "/api/automations/logs", - body: { - appId, - automationId, - }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/automations.ts b/packages/frontend-core/src/api/automations.ts new file mode 100644 index 0000000000..e87999e55b --- /dev/null +++ b/packages/frontend-core/src/api/automations.ts @@ -0,0 +1,158 @@ +import { + ClearAutomationLogRequest, + ClearAutomationLogResponse, + CreateAutomationRequest, + CreateAutomationResponse, + DeleteAutomationResponse, + FetchAutomationResponse, + GetAutomationStepDefinitionsResponse, + SearchAutomationLogsRequest, + SearchAutomationLogsResponse, + TestAutomationRequest, + TestAutomationResponse, + TriggerAutomationRequest, + TriggerAutomationResponse, + UpdateAutomationRequest, + UpdateAutomationResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface AutomationEndpoints { + getAutomations: () => Promise + createAutomation: ( + automation: CreateAutomationRequest + ) => Promise + updateAutomation: ( + automation: UpdateAutomationRequest + ) => Promise + deleteAutomation: ( + automationId: string, + automationRev: string + ) => Promise + clearAutomationLogErrors: ( + automationId: string, + appId: string + ) => Promise + triggerAutomation: ( + automationId: string, + fields: Record, + timeout: number + ) => Promise + testAutomation: ( + automationdId: string, + data: TestAutomationRequest + ) => Promise + getAutomationDefinitions: () => Promise + getAutomationLogs: ( + options: SearchAutomationLogsRequest + ) => Promise +} + +export const buildAutomationEndpoints = ( + API: BaseAPIClient +): AutomationEndpoints => ({ + /** + * Executes an automation. Must have "App Action" trigger. + * @param automationId the ID of the automation to trigger + * @param fields the fields to trigger the automation with + * @param timeout a timeout override + */ + triggerAutomation: async (automationId, fields, timeout) => { + return await API.post({ + url: `/api/automations/${automationId}/trigger`, + body: { fields, timeout }, + }) + }, + + /** + * Tests an automation with data. + * @param automationId the ID of the automation to test + * @param data the test data to run against the automation + */ + testAutomation: async (automationId, data) => { + return await API.post({ + url: `/api/automations/${automationId}/test`, + body: data, + }) + }, + + /** + * 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}`, + }) + }, + + /** + * Get the logs for the app, or by automation ID. + */ + getAutomationLogs: async data => { + return await API.post({ + url: "/api/automations/logs/search", + body: data, + }) + }, + + /** + * Clears automation log errors (which are creating notification) for + * automation or the app. + * @param automationId optional - the ID of the automation to clear errors for. + * @param appId The app ID to clear errors for. + */ + clearAutomationLogErrors: async (automationId, appId) => { + return await API.delete< + ClearAutomationLogRequest, + ClearAutomationLogResponse + >({ + url: "/api/automations/logs", + body: { + appId, + automationId, + }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/backups.js b/packages/frontend-core/src/api/backups.js deleted file mode 100644 index 40546b6f66..0000000000 --- a/packages/frontend-core/src/api/backups.js +++ /dev/null @@ -1,46 +0,0 @@ -export const buildBackupsEndpoints = API => ({ - searchBackups: async ({ appId, trigger, type, page, startDate, endDate }) => { - const opts = {} - if (page) { - opts.page = page - } - if (trigger && type) { - opts.trigger = trigger.toLowerCase() - opts.type = type.toLowerCase() - } - if (startDate && endDate) { - opts.startDate = startDate - opts.endDate = endDate - } - return await API.post({ - url: `/api/apps/${appId}/backups/search`, - body: opts, - }) - }, - - createManualBackup: async ({ appId }) => { - return await API.post({ - url: `/api/apps/${appId}/backups`, - }) - }, - - deleteBackup: async ({ appId, backupId }) => { - return await API.delete({ - url: `/api/apps/${appId}/backups/${backupId}`, - }) - }, - - updateBackup: async ({ appId, backupId, name }) => { - return await API.patch({ - url: `/api/apps/${appId}/backups/${backupId}`, - body: { name }, - }) - }, - - restoreBackup: async ({ appId, backupId, name }) => { - return await API.post({ - url: `/api/apps/${appId}/backups/${backupId}/import`, - body: { name }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/backups.ts b/packages/frontend-core/src/api/backups.ts new file mode 100644 index 0000000000..090ff97fc6 --- /dev/null +++ b/packages/frontend-core/src/api/backups.ts @@ -0,0 +1,50 @@ +import { + CreateAppBackupResponse, + ImportAppBackupResponse, + SearchAppBackupsRequest, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface BackupEndpoints { + createManualBackup: (appId: string) => Promise + restoreBackup: ( + appId: string, + backupId: string, + name?: string + ) => Promise + + // Missing request or response types + searchBackups: (appId: string, opts: SearchAppBackupsRequest) => Promise + deleteBackup: ( + appId: string, + backupId: string + ) => Promise<{ message: string }> +} + +export const buildBackupEndpoints = (API: BaseAPIClient): BackupEndpoints => ({ + createManualBackup: async appId => { + return await API.post({ + url: `/api/apps/${appId}/backups`, + }) + }, + searchBackups: async (appId, opts) => { + return await API.post({ + url: `/api/apps/${appId}/backups/search`, + body: opts, + }) + }, + deleteBackup: async (appId, backupId) => { + return await API.delete({ + url: `/api/apps/${appId}/backups/${backupId}`, + }) + }, + restoreBackup: async (appId, backupId, name) => { + return await API.post({ + url: `/api/apps/${appId}/backups/${backupId}/import`, + // Name is a legacy thing, but unsure if it is needed for restoring. + // Leaving this in just in case, but not type casting the body here + // as we won't normally have it, but it's required in the type. + body: { name }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/configs.js b/packages/frontend-core/src/api/configs.ts similarity index 66% rename from packages/frontend-core/src/api/configs.js rename to packages/frontend-core/src/api/configs.ts index 8d0d176a55..82f08e58a7 100644 --- a/packages/frontend-core/src/api/configs.js +++ b/packages/frontend-core/src/api/configs.ts @@ -1,4 +1,32 @@ -export const buildConfigEndpoints = API => ({ +import { + Config, + ConfigChecklistResponse, + ConfigType, + DeleteConfigResponse, + FindConfigResponse, + GetPublicOIDCConfigResponse, + GetPublicSettingsResponse, + OIDCLogosConfig, + SaveConfigRequest, + SaveConfigResponse, + UploadConfigFileResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface ConfigEndpoints { + getConfig: (type: ConfigType) => Promise + getTenantConfig: (tentantId: string) => Promise + getOIDCConfig: (tenantId: string) => Promise + getOIDCLogos: () => Promise> + saveConfig: (config: SaveConfigRequest) => Promise + deleteConfig: (id: string, rev: string) => Promise + getChecklist: (tenantId: string) => Promise + uploadLogo: (data: any) => Promise + uploadFavicon: (data: any) => Promise + uploadOIDCLogo: (name: string, data: any) => Promise +} + +export const buildConfigEndpoints = (API: BaseAPIClient): ConfigEndpoints => ({ /** * Saves a global config. * @param config the config to save @@ -25,7 +53,7 @@ export const buildConfigEndpoints = API => ({ * @param id the id of the config to delete * @param rev the revision of the config to delete */ - deleteConfig: async ({ id, rev }) => { + deleteConfig: async (id, rev) => { return await API.delete({ url: `/api/global/configs/${id}/${rev}`, }) @@ -90,7 +118,7 @@ export const buildConfigEndpoints = API => ({ * @param name the name of the OIDC provider * @param data the logo form data to upload */ - uploadOIDCLogo: async ({ name, data }) => { + uploadOIDCLogo: async (name, data) => { return await API.post({ url: `/api/global/configs/upload/logos_oidc/${name}`, body: data, diff --git a/packages/frontend-core/src/api/datasources.js b/packages/frontend-core/src/api/datasources.js deleted file mode 100644 index 7cc05960d7..0000000000 --- a/packages/frontend-core/src/api/datasources.js +++ /dev/null @@ -1,92 +0,0 @@ -export const buildDatasourceEndpoints = API => ({ - /** - * Gets a list of datasources. - */ - getDatasources: async () => { - return await API.get({ - url: "/api/datasources", - }) - }, - - /** - * Prompts the server to build the schema for a datasource. - * @param datasourceId the datasource ID to build the schema for - * @param tablesFilter list of specific table names to be build the schema - */ - buildDatasourceSchema: async ({ datasourceId, tablesFilter }) => { - return await API.post({ - url: `/api/datasources/${datasourceId}/schema`, - body: { - tablesFilter, - }, - }) - }, - - /** - * Creates a datasource - * @param datasource the datasource to create - * @param fetchSchema whether to fetch the schema or not - * @param tablesFilter a list of tables to actually fetch rather than simply - * all that are accessible. - */ - createDatasource: async ({ datasource, fetchSchema, tablesFilter }) => { - return await API.post({ - url: "/api/datasources", - body: { - datasource, - fetchSchema, - tablesFilter, - }, - }) - }, - - /** - * Updates a datasource - * @param datasource the datasource to update - */ - updateDatasource: async datasource => { - return await API.put({ - url: `/api/datasources/${datasource._id}`, - body: datasource, - }) - }, - - /** - * Deletes a datasource. - * @param datasourceId the ID of the ddtasource to delete - * @param datasourceRev the rev of the datasource to delete - */ - deleteDatasource: async ({ datasourceId, datasourceRev }) => { - return await API.delete({ - url: `/api/datasources/${datasourceId}/${datasourceRev}`, - }) - }, - - /** - * Validate a datasource configuration - * @param datasource the datasource configuration to validate - */ - validateDatasource: async datasource => { - return await API.post({ - url: `/api/datasources/verify`, - body: { datasource }, - }) - }, - - /** - * Fetch table names available within the datasource, for filtering out undesired tables - * @param datasource the datasource configuration to use for fetching tables - */ - fetchInfoForDatasource: async datasource => { - return await API.post({ - url: `/api/datasources/info`, - body: { datasource }, - }) - }, - - fetchExternalSchema: async datasourceId => { - return await API.get({ - url: `/api/datasources/${datasourceId}/schema/external`, - }) - }, -}) diff --git a/packages/frontend-core/src/api/datasources.ts b/packages/frontend-core/src/api/datasources.ts new file mode 100644 index 0000000000..1d35a6c347 --- /dev/null +++ b/packages/frontend-core/src/api/datasources.ts @@ -0,0 +1,132 @@ +import { + BuildSchemaFromSourceRequest, + BuildSchemaFromSourceResponse, + CreateDatasourceRequest, + CreateDatasourceResponse, + Datasource, + DeleteDatasourceResponse, + FetchDatasourceInfoRequest, + FetchDatasourceInfoResponse, + FetchExternalSchemaResponse, + UpdateDatasourceRequest, + UpdateDatasourceResponse, + VerifyDatasourceRequest, + VerifyDatasourceResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface DatasourceEndpoints { + getDatasources: () => Promise + buildDatasourceSchema: ( + datasourceId: string, + tablesFilter?: string[] + ) => Promise + createDatasource: ( + data: CreateDatasourceRequest + ) => Promise + updateDatasource: ( + datasource: Datasource + ) => Promise + deleteDatasource: ( + id: string, + rev: string + ) => Promise + validateDatasource: ( + datasource: Datasource + ) => Promise + fetchInfoForDatasource: ( + datasource: Datasource + ) => Promise + fetchExternalSchema: ( + datasourceId: string + ) => Promise +} + +export const buildDatasourceEndpoints = ( + API: BaseAPIClient +): DatasourceEndpoints => ({ + /** + * Gets a list of datasources. + */ + getDatasources: async () => { + return await API.get({ + url: "/api/datasources", + }) + }, + + /** + * Prompts the server to build the schema for a datasource. + */ + buildDatasourceSchema: async (datasourceId, tablesFilter?) => { + return await API.post< + BuildSchemaFromSourceRequest, + BuildSchemaFromSourceResponse + >({ + url: `/api/datasources/${datasourceId}/schema`, + body: { + tablesFilter, + }, + }) + }, + + /** + * Creates a datasource + */ + createDatasource: async data => { + return await API.post({ + url: "/api/datasources", + body: data, + }) + }, + + /** + * Updates a datasource + */ + updateDatasource: async datasource => { + return await API.put({ + url: `/api/datasources/${datasource._id}`, + body: datasource, + }) + }, + + /** + * Deletes a datasource. + */ + deleteDatasource: async (id: string, rev: string) => { + return await API.delete({ + url: `/api/datasources/${id}/${rev}`, + }) + }, + + /** + * Validate a datasource configuration + */ + validateDatasource: async (datasource: Datasource) => { + return await API.post({ + url: `/api/datasources/verify`, + body: { datasource }, + }) + }, + + /** + * Fetch table names available within the datasource, for filtering out undesired tables + */ + fetchInfoForDatasource: async (datasource: Datasource) => { + return await API.post< + FetchDatasourceInfoRequest, + FetchDatasourceInfoResponse + >({ + url: `/api/datasources/info`, + body: { datasource }, + }) + }, + + /** + * Fetches the external schema of a datasource + */ + fetchExternalSchema: async (datasourceId: string) => { + return await API.get({ + url: `/api/datasources/${datasourceId}/schema/external`, + }) + }, +}) diff --git a/packages/frontend-core/src/api/environmentVariables.js b/packages/frontend-core/src/api/environmentVariables.js deleted file mode 100644 index badd93ad69..0000000000 --- a/packages/frontend-core/src/api/environmentVariables.js +++ /dev/null @@ -1,36 +0,0 @@ -export const buildEnvironmentVariableEndpoints = API => ({ - checkEnvironmentVariableStatus: async () => { - return await API.get({ - url: `/api/env/variables/status`, - }) - }, - - /** - * Fetches a list of environment variables - */ - fetchEnvironmentVariables: async () => { - return await API.get({ - url: `/api/env/variables`, - json: false, - }) - }, - - createEnvironmentVariable: async data => { - return await API.post({ - url: `/api/env/variables`, - body: data, - }) - }, - deleteEnvironmentVariable: async varName => { - return await API.delete({ - url: `/api/env/variables/${varName}`, - }) - }, - - updateEnvironmentVariable: async data => { - return await API.patch({ - url: `/api/env/variables/${data.name}`, - body: data, - }) - }, -}) diff --git a/packages/frontend-core/src/api/environmentVariables.ts b/packages/frontend-core/src/api/environmentVariables.ts new file mode 100644 index 0000000000..8dd2988c5f --- /dev/null +++ b/packages/frontend-core/src/api/environmentVariables.ts @@ -0,0 +1,58 @@ +import { + CreateEnvironmentVariableRequest, + CreateEnvironmentVariableResponse, + DeleteEnvironmentVariablesResponse, + GetEnvironmentVariablesResponse, + StatusEnvironmentVariableResponse, + UpdateEnvironmentVariableRequest, + UpdateEnvironmentVariableResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface EnvironmentVariableEndpoints { + checkEnvironmentVariableStatus: () => Promise + fetchEnvironmentVariables: () => Promise + createEnvironmentVariable: ( + data: CreateEnvironmentVariableRequest + ) => Promise + deleteEnvironmentVariable: ( + name: string + ) => Promise + updateEnvironmentVariable: ( + name: string, + data: UpdateEnvironmentVariableRequest + ) => Promise +} + +export const buildEnvironmentVariableEndpoints = ( + API: BaseAPIClient +): EnvironmentVariableEndpoints => ({ + checkEnvironmentVariableStatus: async () => { + return await API.get({ + url: `/api/env/variables/status`, + }) + }, + fetchEnvironmentVariables: async () => { + return await API.get({ + url: `/api/env/variables`, + json: false, + }) + }, + createEnvironmentVariable: async data => { + return await API.post({ + url: `/api/env/variables`, + body: data, + }) + }, + deleteEnvironmentVariable: async name => { + return await API.delete({ + url: `/api/env/variables/${name}`, + }) + }, + updateEnvironmentVariable: async (name, data) => { + return await API.patch({ + url: `/api/env/variables/${name}`, + body: data, + }) + }, +}) diff --git a/packages/frontend-core/src/api/events.js b/packages/frontend-core/src/api/events.js deleted file mode 100644 index 3f17722d3e..0000000000 --- a/packages/frontend-core/src/api/events.js +++ /dev/null @@ -1,13 +0,0 @@ -export const buildEventEndpoints = API => ({ - /** - * Publish a specific event to the backend. - */ - publishEvent: async eventType => { - return await API.post({ - url: `/api/global/event/publish`, - body: { - type: eventType, - }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/events.ts b/packages/frontend-core/src/api/events.ts new file mode 100644 index 0000000000..2bec0ea680 --- /dev/null +++ b/packages/frontend-core/src/api/events.ts @@ -0,0 +1,21 @@ +import { + EventPublishType, + PostEventPublishRequest, + PostEventPublishResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface EventEndpoints { + publishEvent: (type: EventPublishType) => Promise +} + +export const buildEventEndpoints = (API: BaseAPIClient): EventEndpoints => ({ + publishEvent: async type => { + return await API.post({ + url: `/api/global/event/publish`, + body: { + type, + }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/flags.js b/packages/frontend-core/src/api/flags.js deleted file mode 100644 index 16adeb7b5d..0000000000 --- a/packages/frontend-core/src/api/flags.js +++ /dev/null @@ -1,34 +0,0 @@ -export const buildFlagEndpoints = API => ({ - /** - * Gets the current user flags object. - */ - getFlags: async () => { - return await API.get({ - url: "/api/users/flags", - }) - }, - - /** - * Updates a flag for the current user. - * @param flag the flag to update - * @param value the value to set the flag to - */ - updateFlag: async ({ flag, value }) => { - return await API.post({ - url: "/api/users/flags", - body: { - flag, - value, - }, - }) - }, - /** - * Allows us to experimentally toggle a beta UI feature through a cookie. - * @param value the feature to toggle - */ - toggleUiFeature: async ({ value }) => { - return await API.post({ - url: `/api/beta/${value}`, - }) - }, -}) diff --git a/packages/frontend-core/src/api/flags.ts b/packages/frontend-core/src/api/flags.ts new file mode 100644 index 0000000000..baaa2309bc --- /dev/null +++ b/packages/frontend-core/src/api/flags.ts @@ -0,0 +1,48 @@ +import { + GetUserFlagsResponse, + SetUserFlagRequest, + SetUserFlagResponse, + ToggleBetaFeatureResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface FlagEndpoints { + getFlags: () => Promise + updateFlag: (flag: string, value: any) => Promise + toggleUiFeature: (value: string) => Promise +} + +export const buildFlagEndpoints = (API: BaseAPIClient): FlagEndpoints => ({ + /** + * Gets the current user flags object. + */ + getFlags: async () => { + return await API.get({ + url: "/api/users/flags", + }) + }, + + /** + * Updates a flag for the current user. + * @param flag the flag to update + * @param value the value to set the flag to + */ + updateFlag: async (flag, value) => { + return await API.post({ + url: "/api/users/flags", + body: { + flag, + value, + }, + }) + }, + /** + * Allows us to experimentally toggle a beta UI feature through a cookie. + * @param value the feature to toggle + */ + toggleUiFeature: async value => { + return await API.post({ + url: `/api/beta/${value}`, + }) + }, +}) diff --git a/packages/frontend-core/src/api/groups.js b/packages/frontend-core/src/api/groups.ts similarity index 53% rename from packages/frontend-core/src/api/groups.js rename to packages/frontend-core/src/api/groups.ts index b1be230ac6..c09c5284ec 100644 --- a/packages/frontend-core/src/api/groups.js +++ b/packages/frontend-core/src/api/groups.ts @@ -1,13 +1,50 @@ -export const buildGroupsEndpoints = API => { - // underlying functionality of adding/removing users/apps to groups - async function updateGroupResource(groupId, resource, operation, ids) { - if (!Array.isArray(ids)) { - ids = [ids] - } - return await API.post({ +import { SearchUserGroupResponse, UserGroup } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface GroupEndpoints { + saveGroup: (group: UserGroup) => Promise<{ _id: string; _rev: string }> + getGroups: () => Promise + getGroup: (id: string) => Promise + deleteGroup: (id: string, rev: string) => Promise<{ message: string }> + getGroupUsers: ( + data: GetGroupUsersRequest + ) => Promise + addUsersToGroup: (groupId: string, userIds: string[]) => Promise + removeUsersFromGroup: (groupId: string, userIds: string[]) => Promise + addAppsToGroup: (groupId: string, appArray: object[]) => Promise + removeAppsFromGroup: (groupId: string, appArray: object[]) => Promise + addGroupAppBuilder: (groupId: string, appId: string) => Promise + removeGroupAppBuilder: (groupId: string, appId: string) => Promise +} + +enum GroupResource { + USERS = "users", + APPS = "apps", +} + +enum GroupOperation { + ADD = "add", + REMOVE = "remove", +} + +type GetGroupUsersRequest = { + id: string + bookmark?: string + emailSearch?: string +} + +export const buildGroupsEndpoints = (API: BaseAPIClient): GroupEndpoints => { + // Underlying functionality of adding/removing users/apps to groups + async function updateGroupResource( + groupId: string, + resource: GroupResource, + operation: GroupOperation, + resources: string[] | object[] + ) { + return await API.post<{ [key in GroupOperation]?: string[] | object[] }>({ url: `/api/global/groups/${groupId}/${resource}`, body: { - [operation]: ids, + [operation]: resources, }, }) } @@ -46,7 +83,7 @@ export const buildGroupsEndpoints = API => { * @param id the id of the config to delete * @param rev the revision of the config to delete */ - deleteGroup: async ({ id, rev }) => { + deleteGroup: async (id, rev) => { return await API.delete({ url: `/api/global/groups/${id}/${rev}`, }) @@ -61,9 +98,8 @@ export const buildGroupsEndpoints = API => { url += `bookmark=${bookmark}&` } if (emailSearch) { - url += `emailSearch=${emailSearch}&` + url += `emailSearch=${emailSearch}` } - return await API.get({ url, }) @@ -75,7 +111,12 @@ export const buildGroupsEndpoints = API => { * @param userIds The user IDs to be added */ addUsersToGroup: async (groupId, userIds) => { - return updateGroupResource(groupId, "users", "add", userIds) + return updateGroupResource( + groupId, + GroupResource.USERS, + GroupOperation.ADD, + userIds + ) }, /** @@ -84,7 +125,12 @@ export const buildGroupsEndpoints = API => { * @param userIds The user IDs to be removed */ removeUsersFromGroup: async (groupId, userIds) => { - return updateGroupResource(groupId, "users", "remove", userIds) + return updateGroupResource( + groupId, + GroupResource.USERS, + GroupOperation.REMOVE, + userIds + ) }, /** @@ -93,7 +139,12 @@ export const buildGroupsEndpoints = API => { * @param appArray Array of objects, containing the appId and roleId to be added */ addAppsToGroup: async (groupId, appArray) => { - return updateGroupResource(groupId, "apps", "add", appArray) + return updateGroupResource( + groupId, + GroupResource.APPS, + GroupOperation.ADD, + appArray + ) }, /** @@ -102,7 +153,12 @@ export const buildGroupsEndpoints = API => { * @param appArray Array of objects, containing the appId to be removed */ removeAppsFromGroup: async (groupId, appArray) => { - return updateGroupResource(groupId, "apps", "remove", appArray) + return updateGroupResource( + groupId, + GroupResource.APPS, + GroupOperation.REMOVE, + appArray + ) }, /** @@ -110,7 +166,7 @@ export const buildGroupsEndpoints = API => { * @param groupId The group to update * @param appId The app id where the builder will be added */ - addGroupAppBuilder: async ({ groupId, appId }) => { + addGroupAppBuilder: async (groupId, appId) => { return await API.post({ url: `/api/global/groups/${groupId}/app/${appId}/builder`, }) @@ -121,7 +177,7 @@ export const buildGroupsEndpoints = API => { * @param groupId The group to update * @param appId The app id where the builder will be removed */ - removeGroupAppBuilder: async ({ groupId, appId }) => { + removeGroupAppBuilder: async (groupId, appId) => { return await API.delete({ url: `/api/global/groups/${groupId}/app/${appId}/builder`, }) diff --git a/packages/frontend-core/src/api/hosting.js b/packages/frontend-core/src/api/hosting.js deleted file mode 100644 index 8c398f9ae7..0000000000 --- a/packages/frontend-core/src/api/hosting.js +++ /dev/null @@ -1,19 +0,0 @@ -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.ts similarity index 65% rename from packages/frontend-core/src/api/index.js rename to packages/frontend-core/src/api/index.ts index 1ca8b7c3f4..f7b05c338a 100644 --- a/packages/frontend-core/src/api/index.js +++ b/packages/frontend-core/src/api/index.ts @@ -1,3 +1,13 @@ +import { + HTTPMethod, + APICallParams, + APIClientConfig, + APIClient, + APICallConfig, + BaseAPIClient, + Headers, + APIError, +} from "./types" import { Helpers } from "@budibase/bbui" import { Header } from "@budibase/shared-core" import { ApiVersion } from "../constants" @@ -10,7 +20,6 @@ import { buildAutomationEndpoints } from "./automations" import { buildConfigEndpoints } from "./configs" import { buildDatasourceEndpoints } from "./datasources" import { buildFlagEndpoints } from "./flags" -import { buildHostingEndpoints } from "./hosting" import { buildLayoutEndpoints } from "./layouts" import { buildOtherEndpoints } from "./other" import { buildPermissionsEndpoints } from "./permissions" @@ -29,10 +38,10 @@ import { buildViewV2Endpoints } from "./viewsV2" import { buildLicensingEndpoints } from "./licensing" import { buildGroupsEndpoints } from "./groups" import { buildPluginEndpoints } from "./plugins" -import { buildBackupsEndpoints } from "./backups" +import { buildBackupEndpoints } from "./backups" import { buildEnvironmentVariableEndpoints } from "./environmentVariables" import { buildEventEndpoints } from "./events" -import { buildAuditLogsEndpoints } from "./auditLogs" +import { buildAuditLogEndpoints } from "./auditLogs" import { buildLogsEndpoints } from "./logs" import { buildMigrationEndpoints } from "./migrations" import { buildRowActionEndpoints } from "./rowActions" @@ -45,55 +54,21 @@ import { buildRowActionEndpoints } from "./rowActions" */ export const APISessionID = Helpers.uuid() -const defaultAPIClientConfig = { - /** - * Certain definitions can't change at runtime for client apps, such as the - * schema of tables. The endpoints that are cacheable can be cached by passing - * in this flag. It's disabled by default to avoid bugs with stale data. - */ - enableCaching: false, - - /** - * A function can be passed in to attach headers to all outgoing requests. - * This function is passed in the headers object, which should be directly - * mutated. No return value is required. - */ - attachHeaders: null, - - /** - * A function can be passed in which will be invoked any time an API error - * occurs. An error is defined as a status code >= 400. This function is - * invoked before the actual JS error is thrown up the stack. - */ - onError: null, - - /** - * A function can be passed to be called when an API call returns info about a migration running for a specific app - */ - onMigrationDetected: null, -} - /** * Constructs an API client with the provided configuration. - * @param config the API client configuration - * @return {object} the API client */ -export const createAPIClient = config => { - config = { - ...defaultAPIClientConfig, - ...config, - } - let cache = {} +export const createAPIClient = (config: APIClientConfig = {}): APIClient => { + let cache: Record = {} // Generates an error object from an API response const makeErrorFromResponse = async ( - response, - method, + response: Response, + method: HTTPMethod, suppressErrors = false - ) => { + ): Promise => { // Try to read a message from the error let message = response.statusText - let json = null + let json: any = null try { json = await response.json() if (json?.message) { @@ -116,32 +91,34 @@ export const createAPIClient = config => { } // Generates an error object from a string - const makeError = (message, request) => { + const makeError = ( + message: string, + url?: string, + method?: HTTPMethod + ): APIError => { return { message, json: null, status: 400, - url: request?.url, - method: request?.method, + url: url, + method: method, handled: true, + suppressErrors: false, } } // Performs an API call to the server. - const makeApiCall = async ({ - method, - url, - body, - json = true, - external = false, - parseResponse, - suppressErrors = false, - }) => { + const makeApiCall = async ( + callConfig: APICallConfig + ): Promise => { + let { json, method, external, body, url, parseResponse, suppressErrors } = + callConfig + // Ensure we don't do JSON processing if sending a GET request - json = json && method !== "GET" + json = json && method !== HTTPMethod.GET // Build headers - let headers = { Accept: "application/json" } + let headers: Headers = { Accept: "application/json" } headers[Header.SESSION_ID] = APISessionID if (!external) { headers[Header.API_VER] = ApiVersion @@ -154,17 +131,17 @@ export const createAPIClient = config => { } // Build request body - let requestBody = body + let requestBody: any = body if (json) { try { requestBody = JSON.stringify(body) } catch (error) { - throw makeError("Invalid JSON body", { url, method }) + throw makeError("Invalid JSON body", url, method) } } // Make request - let response + let response: Response try { response = await fetch(url, { method, @@ -174,21 +151,23 @@ export const createAPIClient = config => { }) } catch (error) { delete cache[url] - throw makeError("Failed to send request", { url, method }) + throw makeError("Failed to send request", url, method) } // Handle response if (response.status >= 200 && response.status < 400) { handleMigrations(response) try { - if (parseResponse) { + if (response.status === 204) { + return undefined as ResponseT + } else if (parseResponse) { return await parseResponse(response) } else { - return await response.json() + return (await response.json()) as ResponseT } } catch (error) { delete cache[url] - return null + throw `Failed to parse response: ${error}` } } else { delete cache[url] @@ -196,7 +175,7 @@ export const createAPIClient = config => { } } - const handleMigrations = response => { + const handleMigrations = (response: Response) => { if (!config.onMigrationDetected) { return } @@ -210,48 +189,57 @@ export const createAPIClient = config => { // Performs an API call to the server and caches the response. // Future invocation for this URL will return the cached result instead of // hitting the server again. - const makeCachedApiCall = async params => { - const identifier = params.url - if (!identifier) { - return null - } + const makeCachedApiCall = async ( + callConfig: APICallConfig + ): Promise => { + const identifier = callConfig.url if (!cache[identifier]) { - cache[identifier] = makeApiCall(params) + cache[identifier] = makeApiCall(callConfig) cache[identifier] = await cache[identifier] } - return await cache[identifier] + return (await cache[identifier]) as ResponseT } // Constructs an API call function for a particular HTTP method - const requestApiCall = method => async params => { - try { - let { url, cache = false, external = false } = params - if (!external) { - url = `/${url}`.replace("//", "/") - } + const requestApiCall = + (method: HTTPMethod) => + async ( + params: APICallParams + ): Promise => { + try { + let callConfig: APICallConfig = { + json: true, + external: false, + suppressErrors: false, + cache: false, + method, + ...params, + } + let { url, cache, external } = callConfig + if (!external) { + callConfig.url = `/${url}`.replace("//", "/") + } - // Cache the request if possible and desired - const cacheRequest = cache && config?.enableCaching - const handler = cacheRequest ? makeCachedApiCall : makeApiCall - - const enrichedParams = { ...params, method, url } - return await handler(enrichedParams) - } catch (error) { - if (config?.onError) { - config.onError(error) + // Cache the request if possible and desired + const cacheRequest = cache && config?.enableCaching + const handler = cacheRequest ? makeCachedApiCall : makeApiCall + return await handler(callConfig) + } catch (error) { + if (config?.onError) { + config.onError(error) + } + throw error } - throw error } - } // Build the underlying core API methods - let API = { - post: requestApiCall("POST"), - get: requestApiCall("GET"), - patch: requestApiCall("PATCH"), - delete: requestApiCall("DELETE"), - put: requestApiCall("PUT"), - error: message => { + let API: BaseAPIClient = { + post: requestApiCall(HTTPMethod.POST), + get: requestApiCall(HTTPMethod.GET), + patch: requestApiCall(HTTPMethod.PATCH), + delete: requestApiCall(HTTPMethod.DELETE), + put: requestApiCall(HTTPMethod.PUT), + error: (message: string) => { throw makeError(message) }, invalidateCache: () => { @@ -260,9 +248,9 @@ export const createAPIClient = config => { // Generic utility to extract the current app ID. Assumes that any client // that exists in an app context will be attaching our app ID header. - getAppID: () => { - let headers = {} - config?.attachHeaders(headers) + getAppID: (): string => { + let headers: Headers = {} + config?.attachHeaders?.(headers) return headers?.[Header.APP_ID] }, } @@ -279,7 +267,6 @@ export const createAPIClient = config => { ...buildConfigEndpoints(API), ...buildDatasourceEndpoints(API), ...buildFlagEndpoints(API), - ...buildHostingEndpoints(API), ...buildLayoutEndpoints(API), ...buildOtherEndpoints(API), ...buildPermissionsEndpoints(API), @@ -297,10 +284,10 @@ export const createAPIClient = config => { ...buildLicensingEndpoints(API), ...buildGroupsEndpoints(API), ...buildPluginEndpoints(API), - ...buildBackupsEndpoints(API), + ...buildBackupEndpoints(API), ...buildEnvironmentVariableEndpoints(API), ...buildEventEndpoints(API), - ...buildAuditLogsEndpoints(API), + ...buildAuditLogEndpoints(API), ...buildLogsEndpoints(API), ...buildMigrationEndpoints(API), viewV2: buildViewV2Endpoints(API), diff --git a/packages/frontend-core/src/api/layouts.js b/packages/frontend-core/src/api/layouts.js deleted file mode 100644 index 51bce1f533..0000000000 --- a/packages/frontend-core/src/api/layouts.js +++ /dev/null @@ -1,23 +0,0 @@ -export const buildLayoutEndpoints = API => ({ - /** - * Saves a layout. - * @param layout the layout to save - */ - saveLayout: async layout => { - return await API.post({ - url: "/api/layouts", - body: layout, - }) - }, - - /** - * Deletes a layout. - * @param layoutId the ID of the layout to delete - * @param layoutRev the rev of the layout to delete - */ - deleteLayout: async ({ layoutId, layoutRev }) => { - return await API.delete({ - url: `/api/layouts/${layoutId}/${layoutRev}`, - }) - }, -}) diff --git a/packages/frontend-core/src/api/layouts.ts b/packages/frontend-core/src/api/layouts.ts new file mode 100644 index 0000000000..233f60e6c2 --- /dev/null +++ b/packages/frontend-core/src/api/layouts.ts @@ -0,0 +1,35 @@ +import { + DeleteLayoutResponse, + SaveLayoutRequest, + SaveLayoutResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface LayoutEndpoints { + saveLayout: (layout: SaveLayoutRequest) => Promise + deleteLayout: (id: string, rev: string) => Promise +} + +export const buildLayoutEndpoints = (API: BaseAPIClient): LayoutEndpoints => ({ + /** + * Saves a layout. + * @param layout the layout to save + */ + saveLayout: async layout => { + return await API.post({ + url: "/api/layouts", + body: layout, + }) + }, + + /** + * Deletes a layout. + * @param layoutId the ID of the layout to delete + * @param layoutRev the rev of the layout to delete + */ + deleteLayout: async (id: string, rev: string) => { + return await API.delete({ + url: `/api/layouts/${id}/${rev}`, + }) + }, +}) diff --git a/packages/frontend-core/src/api/licensing.js b/packages/frontend-core/src/api/licensing.js deleted file mode 100644 index 987fc34cf5..0000000000 --- a/packages/frontend-core/src/api/licensing.js +++ /dev/null @@ -1,75 +0,0 @@ -export const buildLicensingEndpoints = API => ({ - // LICENSE KEY - - activateLicenseKey: async data => { - return API.post({ - url: `/api/global/license/key`, - body: data, - }) - }, - deleteLicenseKey: async () => { - return API.delete({ - url: `/api/global/license/key`, - }) - }, - getLicenseKey: async () => { - try { - return await API.get({ - url: "/api/global/license/key", - }) - } catch (e) { - if (e.status !== 404) { - throw e - } - } - }, - - // OFFLINE LICENSE - - activateOfflineLicense: async ({ offlineLicenseToken }) => { - return API.post({ - url: "/api/global/license/offline", - body: { - offlineLicenseToken, - }, - }) - }, - deleteOfflineLicense: async () => { - return API.delete({ - url: "/api/global/license/offline", - }) - }, - getOfflineLicense: async () => { - try { - return await API.get({ - url: "/api/global/license/offline", - }) - } catch (e) { - if (e.status !== 404) { - throw e - } - } - }, - getOfflineLicenseIdentifier: async () => { - return await API.get({ - url: "/api/global/license/offline/identifier", - }) - }, - - /** - * Refreshes the license cache - */ - refreshLicense: async () => { - return API.post({ - url: "/api/global/license/refresh", - }) - }, - /** - * Retrieve the usage information for the tenant - */ - getQuotaUsage: async () => { - return API.get({ - url: "/api/global/license/usage", - }) - }, -}) diff --git a/packages/frontend-core/src/api/licensing.ts b/packages/frontend-core/src/api/licensing.ts new file mode 100644 index 0000000000..bc499b92d4 --- /dev/null +++ b/packages/frontend-core/src/api/licensing.ts @@ -0,0 +1,107 @@ +import { + ActivateLicenseKeyRequest, + ActivateLicenseKeyResponse, + ActivateOfflineLicenseTokenRequest, + ActivateOfflineLicenseTokenResponse, + GetLicenseKeyResponse, + GetOfflineIdentifierResponse, + GetOfflineLicenseTokenResponse, + QuotaUsage, + RefreshOfflineLicenseResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface LicensingEndpoints { + activateLicenseKey: ( + licenseKey: string + ) => Promise + deleteLicenseKey: () => Promise + getLicenseKey: () => Promise + activateOfflineLicense: ( + offlineLicenseToken: string + ) => Promise + deleteOfflineLicense: () => Promise + getOfflineLicense: () => Promise + getOfflineLicenseIdentifier: () => Promise + refreshLicense: () => Promise + getQuotaUsage: () => Promise +} + +export const buildLicensingEndpoints = ( + API: BaseAPIClient +): LicensingEndpoints => ({ + // LICENSE KEY + activateLicenseKey: async licenseKey => { + return API.post({ + url: `/api/global/license/key`, + body: { licenseKey }, + }) + }, + deleteLicenseKey: async () => { + return API.delete({ + url: `/api/global/license/key`, + }) + }, + getLicenseKey: async () => { + try { + return await API.get({ + url: "/api/global/license/key", + }) + } catch (e: any) { + if (e.status !== 404) { + throw e + } + } + }, + + // OFFLINE LICENSE + activateOfflineLicense: async offlineLicenseToken => { + return API.post< + ActivateOfflineLicenseTokenRequest, + ActivateOfflineLicenseTokenResponse + >({ + url: "/api/global/license/offline", + body: { + offlineLicenseToken, + }, + }) + }, + deleteOfflineLicense: async () => { + return API.delete({ + url: "/api/global/license/offline", + }) + }, + getOfflineLicense: async () => { + try { + return await API.get({ + url: "/api/global/license/offline", + }) + } catch (e: any) { + if (e.status !== 404) { + throw e + } + } + }, + getOfflineLicenseIdentifier: async () => { + return await API.get({ + url: "/api/global/license/offline/identifier", + }) + }, + + /** + * Refreshes the license cache + */ + refreshLicense: async () => { + return API.post({ + url: "/api/global/license/refresh", + }) + }, + /** + * Retrieve the usage information for the tenant + */ + getQuotaUsage: async () => { + return API.get({ + url: "/api/global/license/usage", + }) + }, +}) diff --git a/packages/frontend-core/src/api/logs.js b/packages/frontend-core/src/api/logs.ts similarity index 57% rename from packages/frontend-core/src/api/logs.js rename to packages/frontend-core/src/api/logs.ts index b6cb98627c..4c883233f6 100644 --- a/packages/frontend-core/src/api/logs.js +++ b/packages/frontend-core/src/api/logs.ts @@ -1,4 +1,10 @@ -export const buildLogsEndpoints = API => ({ +import { BaseAPIClient } from "./types" + +export interface LogEndpoints { + getSystemLogs: () => Promise +} + +export const buildLogsEndpoints = (API: BaseAPIClient): LogEndpoints => ({ /** * Gets a stream for the system logs. */ diff --git a/packages/frontend-core/src/api/migrations.js b/packages/frontend-core/src/api/migrations.js deleted file mode 100644 index 2da70d6fcb..0000000000 --- a/packages/frontend-core/src/api/migrations.js +++ /dev/null @@ -1,10 +0,0 @@ -export const buildMigrationEndpoints = API => ({ - /** - * Gets the info about the current app migration - */ - getMigrationStatus: async () => { - return await API.get({ - url: "/api/migrations/status", - }) - }, -}) diff --git a/packages/frontend-core/src/api/migrations.ts b/packages/frontend-core/src/api/migrations.ts new file mode 100644 index 0000000000..8213691205 --- /dev/null +++ b/packages/frontend-core/src/api/migrations.ts @@ -0,0 +1,19 @@ +import { GetOldMigrationStatus } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface MigrationEndpoints { + getMigrationStatus: () => Promise +} + +export const buildMigrationEndpoints = ( + API: BaseAPIClient +): MigrationEndpoints => ({ + /** + * Gets the info about the current app migration + */ + getMigrationStatus: async () => { + return await API.get({ + url: "/api/migrations/status", + }) + }, +}) diff --git a/packages/frontend-core/src/api/other.js b/packages/frontend-core/src/api/other.ts similarity index 56% rename from packages/frontend-core/src/api/other.js rename to packages/frontend-core/src/api/other.ts index 3d171eaab4..c39538fd7b 100644 --- a/packages/frontend-core/src/api/other.js +++ b/packages/frontend-core/src/api/other.ts @@ -1,4 +1,21 @@ -export const buildOtherEndpoints = API => ({ +import { + FetchBuiltinPermissionsResponse, + FetchIntegrationsResponse, + GetEnvironmentResponse, + GetVersionResponse, + SystemStatusResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface OtherEndpoints { + getSystemStatus: () => Promise + getBudibaseVersion: () => Promise + getIntegrations: () => Promise + getBasePermissions: () => Promise + getEnvironment: () => Promise +} + +export const buildOtherEndpoints = (API: BaseAPIClient): OtherEndpoints => ({ /** * Gets the current environment details. */ @@ -31,7 +48,7 @@ export const buildOtherEndpoints = API => ({ */ getBudibaseVersion: async () => { return ( - await API.get({ + await API.get({ url: "/api/dev/version", }) ).version @@ -45,14 +62,4 @@ export const buildOtherEndpoints = API => ({ url: "/api/permission/builtin", }) }, - - /** - * Check if they are part of the budibase beta program. - */ - checkBetaAccess: async email => { - return await API.get({ - url: `/api/beta/access?email=${email}`, - external: true, - }) - }, }) diff --git a/packages/frontend-core/src/api/permissions.js b/packages/frontend-core/src/api/permissions.ts similarity index 56% rename from packages/frontend-core/src/api/permissions.js rename to packages/frontend-core/src/api/permissions.ts index dd0005aaf2..8a4a99b642 100644 --- a/packages/frontend-core/src/api/permissions.js +++ b/packages/frontend-core/src/api/permissions.ts @@ -1,4 +1,32 @@ -export const buildPermissionsEndpoints = API => ({ +import { + AddPermissionResponse, + GetDependantResourcesResponse, + GetResourcePermsResponse, + PermissionLevel, + RemovePermissionResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface PermissionEndpoints { + getPermissionForResource: ( + resourceId: string + ) => Promise + updatePermissionForResource: ( + resourceId: string, + roleId: string, + level: PermissionLevel + ) => Promise + removePermissionFromResource: ( + resourceId: string, + roleId: string, + level: PermissionLevel + ) => Promise + getDependants: (resourceId: string) => Promise +} + +export const buildPermissionsEndpoints = ( + API: BaseAPIClient +): PermissionEndpoints => ({ /** * Gets the permission required to access a specific resource * @param resourceId the resource ID to check @@ -14,9 +42,8 @@ export const buildPermissionsEndpoints = API => ({ * @param resourceId the ID of the resource to update * @param roleId the ID of the role to update the permissions of * @param level the level to assign the role for this resource - * @return {Promise<*>} */ - updatePermissionForResource: async ({ resourceId, roleId, level }) => { + updatePermissionForResource: async (resourceId, roleId, level) => { return await API.post({ url: `/api/permission/${roleId}/${resourceId}/${level}`, }) @@ -27,9 +54,8 @@ export const buildPermissionsEndpoints = API => ({ * @param resourceId the ID of the resource to update * @param roleId the ID of the role to update the permissions of * @param level the level to remove the role for this resource - * @return {Promise<*>} */ - removePermissionFromResource: async ({ resourceId, roleId, level }) => { + removePermissionFromResource: async (resourceId, roleId, level) => { return await API.delete({ url: `/api/permission/${roleId}/${resourceId}/${level}`, }) diff --git a/packages/frontend-core/src/api/plugins.js b/packages/frontend-core/src/api/plugins.ts similarity index 57% rename from packages/frontend-core/src/api/plugins.js rename to packages/frontend-core/src/api/plugins.ts index 8735b04caa..5a3a3a3e6a 100644 --- a/packages/frontend-core/src/api/plugins.js +++ b/packages/frontend-core/src/api/plugins.ts @@ -1,4 +1,21 @@ -export const buildPluginEndpoints = API => ({ +import { + CreatePluginRequest, + CreatePluginResponse, + DeletePluginResponse, + FetchPluginResponse, + UploadPluginRequest, + UploadPluginResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface PluginEndpoins { + uploadPlugin: (data: UploadPluginRequest) => Promise + createPlugin: (data: CreatePluginRequest) => Promise + getPlugins: () => Promise + deletePlugin: (pluginId: string) => Promise +} + +export const buildPluginEndpoints = (API: BaseAPIClient): PluginEndpoins => ({ /** * Uploads a plugin tarball bundle * @param data the plugin tarball bundle to upload diff --git a/packages/frontend-core/src/api/queries.js b/packages/frontend-core/src/api/queries.ts similarity index 50% rename from packages/frontend-core/src/api/queries.js rename to packages/frontend-core/src/api/queries.ts index f18ec7c4ec..b088e4487c 100644 --- a/packages/frontend-core/src/api/queries.js +++ b/packages/frontend-core/src/api/queries.ts @@ -1,12 +1,42 @@ -export const buildQueryEndpoints = API => ({ +import { + DeleteQueryResponse, + ExecuteQueryRequest, + ExecuteV2QueryResponse, + FetchQueriesResponse, + FindQueryResponse, + ImportRestQueryRequest, + ImportRestQueryResponse, + PreviewQueryRequest, + PreviewQueryResponse, + SaveQueryRequest, + SaveQueryResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface QueryEndpoints { + executeQuery: ( + queryId: string, + opts?: ExecuteQueryRequest + ) => Promise + fetchQueryDefinition: (queryId: string) => Promise + getQueries: () => Promise + saveQuery: (query: SaveQueryRequest) => Promise + deleteQuery: (id: string, rev: string) => Promise + previewQuery: (query: PreviewQueryRequest) => Promise + importQueries: ( + data: ImportRestQueryRequest + ) => Promise +} + +export const buildQueryEndpoints = (API: BaseAPIClient): QueryEndpoints => ({ /** * Executes a query against an external data connector. * @param queryId the ID of the query to execute * @param pagination pagination info for the query * @param parameters parameters for the query */ - executeQuery: async ({ queryId, pagination, parameters }) => { - return await API.post({ + executeQuery: async (queryId, { pagination, parameters } = {}) => { + return await API.post({ url: `/api/v2/queries/${queryId}`, body: { parameters, @@ -48,27 +78,22 @@ export const buildQueryEndpoints = API => ({ /** * Deletes a query - * @param queryId the ID of the query to delete - * @param queryRev the rev of the query to delete + * @param id the ID of the query to delete + * @param rev the rev of the query to delete */ - deleteQuery: async ({ queryId, queryRev }) => { + deleteQuery: async (id, rev) => { return await API.delete({ - url: `/api/queries/${queryId}/${queryRev}`, + url: `/api/queries/${id}/${rev}`, }) }, /** * Imports a set of queries into a certain datasource - * @param datasourceId the datasource ID to import queries into - * @param data the data string of the content to import */ - importQueries: async ({ datasourceId, data }) => { + importQueries: async data => { return await API.post({ url: "/api/queries/import", - body: { - datasourceId, - data, - }, + body: data, }) }, diff --git a/packages/frontend-core/src/api/relationships.js b/packages/frontend-core/src/api/relationships.js deleted file mode 100644 index 45595750a8..0000000000 --- a/packages/frontend-core/src/api/relationships.js +++ /dev/null @@ -1,21 +0,0 @@ -export const buildRelationshipEndpoints = API => ({ - /** - * Fetches related rows for a certain field of a certain row. - * @param tableId the ID of the table to fetch from - * @param rowId the ID of the row to fetch related rows for - * @param fieldName the name of the relationship field - */ - fetchRelationshipData: async ({ tableId, rowId, fieldName }) => { - if (!tableId || !rowId) { - return [] - } - const response = await API.get({ - url: `/api/${tableId}/${rowId}/enrich?field=${fieldName}`, - }) - if (!fieldName) { - return response || [] - } else { - return response[fieldName] || [] - } - }, -}) diff --git a/packages/frontend-core/src/api/relationships.ts b/packages/frontend-core/src/api/relationships.ts new file mode 100644 index 0000000000..3d5f1a006d --- /dev/null +++ b/packages/frontend-core/src/api/relationships.ts @@ -0,0 +1,31 @@ +import { FetchEnrichedRowResponse, Row } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface RelationshipEndpoints { + fetchRelationshipData: ( + sourceId: string, + rowId: string, + fieldName?: string + ) => Promise +} + +export const buildRelationshipEndpoints = ( + API: BaseAPIClient +): RelationshipEndpoints => ({ + /** + * Fetches related rows for a certain field of a certain row. + * @param sourceId the ID of the table to fetch from + * @param rowId the ID of the row to fetch related rows for + * @param fieldName the name of the relationship field + */ + fetchRelationshipData: async (sourceId, rowId, fieldName) => { + const response = await API.get({ + url: `/api/${sourceId}/${rowId}/enrich?field=${fieldName}`, + }) + if (!fieldName) { + return [response] + } else { + return response[fieldName] || [] + } + }, +}) diff --git a/packages/frontend-core/src/api/roles.js b/packages/frontend-core/src/api/roles.ts similarity index 50% rename from packages/frontend-core/src/api/roles.js rename to packages/frontend-core/src/api/roles.ts index 80b8028c12..780bc511ed 100644 --- a/packages/frontend-core/src/api/roles.js +++ b/packages/frontend-core/src/api/roles.ts @@ -1,12 +1,29 @@ -export const buildRoleEndpoints = API => ({ +import { + AccessibleRolesResponse, + DeleteRoleResponse, + FetchRolesResponse, + SaveRoleRequest, + SaveRoleResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface RoleEndpoints { + deleteRole: (id: string, rev: string) => Promise + saveRole: (role: SaveRoleRequest) => Promise + getRoles: () => Promise + getRolesForApp: (appId: string) => Promise + getAccessibleRoles: () => Promise +} + +export const buildRoleEndpoints = (API: BaseAPIClient): RoleEndpoints => ({ /** * Deletes a role. - * @param roleId the ID of the role to delete - * @param roleRev the rev of the role to delete + * @param id the ID of the role to delete + * @param rev the rev of the role to delete */ - deleteRole: async ({ roleId, roleRev }) => { + deleteRole: async (id, rev) => { return await API.delete({ - url: `/api/roles/${roleId}/${roleRev}`, + url: `/api/roles/${id}/${rev}`, }) }, diff --git a/packages/frontend-core/src/api/routes.js b/packages/frontend-core/src/api/routes.js deleted file mode 100644 index 28e206debc..0000000000 --- a/packages/frontend-core/src/api/routes.js +++ /dev/null @@ -1,19 +0,0 @@ -export const buildRouteEndpoints = API => ({ - /** - * Fetches available routes for the client app. - */ - fetchClientAppRoutes: async () => { - return await API.get({ - url: `/api/routing/client`, - }) - }, - - /** - * Fetches all routes for the current app. - */ - fetchAppRoutes: async () => { - return await API.get({ - url: "/api/routing", - }) - }, -}) diff --git a/packages/frontend-core/src/api/routes.ts b/packages/frontend-core/src/api/routes.ts new file mode 100644 index 0000000000..0adedf15e2 --- /dev/null +++ b/packages/frontend-core/src/api/routes.ts @@ -0,0 +1,30 @@ +import { + FetchClientScreenRoutingResponse, + FetchScreenRoutingResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface RouteEndpoints { + fetchClientAppRoutes: () => Promise + fetchAppRoutes: () => Promise +} + +export const buildRouteEndpoints = (API: BaseAPIClient): RouteEndpoints => ({ + /** + * Fetches available routes for the client app. + */ + fetchClientAppRoutes: async () => { + return await API.get({ + url: `/api/routing/client`, + }) + }, + + /** + * Fetches all routes for the current app. + */ + fetchAppRoutes: async () => { + return await API.get({ + url: "/api/routing", + }) + }, +}) diff --git a/packages/frontend-core/src/api/rowActions.js b/packages/frontend-core/src/api/rowActions.ts similarity index 51% rename from packages/frontend-core/src/api/rowActions.js rename to packages/frontend-core/src/api/rowActions.ts index 071af953ef..eef50ed940 100644 --- a/packages/frontend-core/src/api/rowActions.js +++ b/packages/frontend-core/src/api/rowActions.ts @@ -1,13 +1,46 @@ -export const buildRowActionEndpoints = API => ({ +import { + RowActionsResponse, + RowActionResponse, + CreateRowActionRequest, + RowActionPermissionsResponse, + RowActionTriggerRequest, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface RowActionEndpoints { + fetch: (tableId: string) => Promise> + create: (tableId: string, name: string) => Promise + delete: (tableId: string, rowActionId: string) => Promise + enableView: ( + tableId: string, + rowActionId: string, + viewId: string + ) => Promise + disableView: ( + tableId: string, + rowActionId: string, + viewId: string + ) => Promise + trigger: ( + sourceId: string, + rowActionId: string, + rowId: string + ) => Promise +} + +export const buildRowActionEndpoints = ( + API: BaseAPIClient +): RowActionEndpoints => ({ /** * Gets the available row actions for a table. * @param tableId the ID of the table */ fetch: async tableId => { - const res = await API.get({ - url: `/api/tables/${tableId}/actions`, - }) - return res?.actions || {} + return ( + await API.get({ + url: `/api/tables/${tableId}/actions`, + }) + ).actions }, /** @@ -15,8 +48,8 @@ export const buildRowActionEndpoints = API => ({ * @param name the name of the row action * @param tableId the ID of the table */ - create: async ({ name, tableId }) => { - return await API.post({ + create: async (tableId, name) => { + return await API.post({ url: `/api/tables/${tableId}/actions`, body: { name, @@ -24,27 +57,12 @@ export const buildRowActionEndpoints = API => ({ }) }, - /** - * Updates a row action. - * @param name the new name of the row action - * @param tableId the ID of the table - * @param rowActionId the ID of the row action to update - */ - update: async ({ tableId, rowActionId, name }) => { - return await API.put({ - url: `/api/tables/${tableId}/actions/${rowActionId}`, - body: { - name, - }, - }) - }, - /** * Deletes a row action. * @param tableId the ID of the table * @param rowActionId the ID of the row action to delete */ - delete: async ({ tableId, rowActionId }) => { + delete: async (tableId, rowActionId) => { return await API.delete({ url: `/api/tables/${tableId}/actions/${rowActionId}`, }) @@ -56,7 +74,7 @@ export const buildRowActionEndpoints = API => ({ * @param rowActionId the ID of the row action * @param viewId the ID of the view */ - enableView: async ({ tableId, rowActionId, viewId }) => { + enableView: async (tableId, rowActionId, viewId) => { return await API.post({ url: `/api/tables/${tableId}/actions/${rowActionId}/permissions/${viewId}`, }) @@ -68,7 +86,7 @@ export const buildRowActionEndpoints = API => ({ * @param rowActionId the ID of the row action * @param viewId the ID of the view */ - disableView: async ({ tableId, rowActionId, viewId }) => { + disableView: async (tableId, rowActionId, viewId) => { return await API.delete({ url: `/api/tables/${tableId}/actions/${rowActionId}/permissions/${viewId}`, }) @@ -79,8 +97,8 @@ export const buildRowActionEndpoints = API => ({ * @param tableId the ID of the table * @param rowActionId the ID of the row action to trigger */ - trigger: async ({ sourceId, rowActionId, rowId }) => { - return await API.post({ + trigger: async (sourceId, rowActionId, rowId) => { + return await API.post({ url: `/api/tables/${sourceId}/actions/${rowActionId}/trigger`, body: { rowId, diff --git a/packages/frontend-core/src/api/rows.js b/packages/frontend-core/src/api/rows.js deleted file mode 100644 index 0a0d48da43..0000000000 --- a/packages/frontend-core/src/api/rows.js +++ /dev/null @@ -1,117 +0,0 @@ -export const buildRowEndpoints = API => ({ - /** - * Fetches data about a certain row in a table. - * @param tableId the ID of the table to fetch from - * @param rowId the ID of the row to fetch - */ - fetchRow: async ({ tableId, rowId }) => { - if (!tableId || !rowId) { - return null - } - return await API.get({ - url: `/api/${tableId}/rows/${rowId}`, - }) - }, - - /** - * Creates or updates a row in a table. - * @param row the row to save - * @param suppressErrors whether or not to suppress error notifications - */ - saveRow: async (row, suppressErrors = false) => { - const resourceId = row?._viewId || row?.tableId - if (!resourceId) { - return - } - return await API.post({ - url: `/api/${resourceId}/rows`, - body: row, - suppressErrors, - }) - }, - - /** - * Patches a row in a table. - * @param row the row to patch - * @param suppressErrors whether or not to suppress error notifications - */ - patchRow: async (row, suppressErrors = false) => { - const resourceId = row?._viewId || row?.tableId - if (!resourceId) { - return - } - return await API.patch({ - url: `/api/${resourceId}/rows`, - body: row, - suppressErrors, - }) - }, - - /** - * Deletes a row from a table. - * @param tableId the ID of the table or view to delete from - * @param rowId the ID of the row to delete - * @param revId the rev of the row to delete - */ - deleteRow: async ({ tableId, rowId, revId }) => { - if (!tableId || !rowId) { - return - } - return await API.delete({ - url: `/api/${tableId}/rows`, - body: { - _id: rowId, - _rev: revId, - }, - }) - }, - - /** - * Deletes multiple rows from a table. - * @param tableId the table or view ID to delete the rows from - * @param rows the array of rows to delete - */ - deleteRows: async ({ tableId, rows }) => { - rows?.forEach(row => { - delete row?._viewId - }) - return await API.delete({ - url: `/api/${tableId}/rows`, - body: { - rows, - }, - }) - }, - - /** - * Exports rows. - * @param tableId the table ID to export the rows from - * @param rows the array of rows to export - * @param format the format to export (csv or json) - * @param columns which columns to export (all if undefined) - * @param delimiter how values should be separated in a CSV (default is comma) - */ - exportRows: async ({ - tableId, - rows, - format, - columns, - search, - delimiter, - customHeaders, - }) => { - return await API.post({ - url: `/api/${tableId}/rows/exportRows?format=${format}`, - body: { - rows, - columns, - delimiter, - customHeaders, - ...search, - }, - parseResponse: async response => { - return await response.text() - }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/rows.ts b/packages/frontend-core/src/api/rows.ts new file mode 100644 index 0000000000..cee434037e --- /dev/null +++ b/packages/frontend-core/src/api/rows.ts @@ -0,0 +1,120 @@ +import { + DeleteRowRequest, + ExportRowsRequest, + FindRowResponse, + PatchRowRequest, + PatchRowResponse, + Row, + SaveRowRequest, + SaveRowResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface RowEndpoints { + fetchRow: (tableId: string, rowId: string) => Promise + saveRow: ( + row: SaveRowRequest, + suppressErrors?: boolean + ) => Promise + patchRow: ( + row: PatchRowRequest, + suppressErrors?: boolean + ) => Promise + deleteRow: (sourceId: string, id: string) => Promise + deleteRows: (sourceId: string, rows: (Row | string)[]) => Promise + exportRows: ( + tableId: string, + format: string, + data: ExportRowsRequest + ) => Promise +} + +export const buildRowEndpoints = (API: BaseAPIClient): RowEndpoints => ({ + /** + * Fetches data about a certain row in a data source. + * @param sourceId the ID of the table or view to fetch from + * @param rowId the ID of the row to fetch + */ + fetchRow: async (sourceId, rowId) => { + return await API.get({ + url: `/api/${sourceId}/rows/${rowId}`, + }) + }, + + /** + * Creates or updates a row in a table. + * @param row the row to save + * @param suppressErrors whether or not to suppress error notifications + */ + saveRow: async (row, suppressErrors = false) => { + const sourceId = row._viewId || row.tableId + return await API.post({ + url: `/api/${sourceId}/rows`, + body: row, + suppressErrors, + }) + }, + + /** + * Patches a row in a table. + * @param row the row to patch + * @param suppressErrors whether or not to suppress error notifications + */ + patchRow: async (row, suppressErrors = false) => { + const sourceId = row._viewId || row.tableId + return await API.patch({ + url: `/api/${sourceId}/rows`, + body: row, + suppressErrors, + }) + }, + + /** + * Deletes a row from a table. + * @param sourceId the ID of the table or view to delete from + * @param rowId the ID of the row to delete + */ + deleteRow: async (sourceId, rowId) => { + return await API.delete({ + url: `/api/${sourceId}/rows`, + body: { + _id: rowId, + }, + }) + }, + + /** + * Deletes multiple rows from a table. + * @param sourceId the table or view ID to delete the rows from + * @param rows the array of rows to delete + */ + deleteRows: async (sourceId, rows) => { + rows.forEach(row => { + if (typeof row === "object") { + delete row?._viewId + } + }) + return await API.delete({ + url: `/api/${sourceId}/rows`, + body: { + rows, + }, + }) + }, + + /** + * Exports rows. + * @param tableId the table ID to export the rows from + * @param format the format to export (csv or json) + * @param data the export options + */ + exportRows: async (tableId, format, data) => { + return await API.post({ + url: `/api/${tableId}/rows/exportRows?format=${format}`, + body: data, + parseResponse: async response => { + return await response.text() + }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/screens.js b/packages/frontend-core/src/api/screens.js deleted file mode 100644 index 1daa79153b..0000000000 --- a/packages/frontend-core/src/api/screens.js +++ /dev/null @@ -1,23 +0,0 @@ -export const buildScreenEndpoints = API => ({ - /** - * Saves a screen definition - * @param screen the screen to save - */ - saveScreen: async screen => { - return await API.post({ - url: "/api/screens", - body: screen, - }) - }, - - /** - * Deletes a screen. - * @param screenId the ID of the screen to delete - * @param screenRev the rev of the screen to delete - */ - deleteScreen: async ({ screenId, screenRev }) => { - return await API.delete({ - url: `/api/screens/${screenId}/${screenRev}`, - }) - }, -}) diff --git a/packages/frontend-core/src/api/screens.ts b/packages/frontend-core/src/api/screens.ts new file mode 100644 index 0000000000..bdb01e1427 --- /dev/null +++ b/packages/frontend-core/src/api/screens.ts @@ -0,0 +1,35 @@ +import { + DeleteScreenResponse, + SaveScreenRequest, + SaveScreenResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface ScreenEndpoints { + saveScreen: (screen: SaveScreenRequest) => Promise + deleteScreen: (id: string, rev: string) => Promise +} + +export const buildScreenEndpoints = (API: BaseAPIClient): ScreenEndpoints => ({ + /** + * Saves a screen definition + * @param screen the screen to save + */ + saveScreen: async screen => { + return await API.post({ + url: "/api/screens", + body: screen, + }) + }, + + /** + * Deletes a screen. + * @param id the ID of the screen to delete + * @param rev the rev of the screen to delete + */ + deleteScreen: async (id, rev) => { + return await API.delete({ + url: `/api/screens/${id}/${rev}`, + }) + }, +}) diff --git a/packages/frontend-core/src/api/self.js b/packages/frontend-core/src/api/self.ts similarity index 59% rename from packages/frontend-core/src/api/self.js rename to packages/frontend-core/src/api/self.ts index cdd5eaba53..db5b1226eb 100644 --- a/packages/frontend-core/src/api/self.js +++ b/packages/frontend-core/src/api/self.ts @@ -1,11 +1,28 @@ -export const buildSelfEndpoints = API => ({ +import { + AppSelfResponse, + FetchAPIKeyResponse, + GenerateAPIKeyResponse, + GetGlobalSelfResponse, + UpdateSelfRequest, + UpdateSelfResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface SelfEndpoints { + updateSelf: (user: UpdateSelfRequest) => Promise + generateAPIKey: () => Promise + fetchDeveloperInfo: () => Promise + fetchBuilderSelf: () => Promise + fetchSelf: () => Promise +} + +export const buildSelfEndpoints = (API: BaseAPIClient): SelfEndpoints => ({ /** * Using the logged in user, this will generate a new API key, * assuming the user is a builder. - * @return {Promise} returns the API response, including an API key. */ generateAPIKey: async () => { - const response = await API.post({ + const response = await API.post({ url: "/api/global/self/api_key", }) return response?.apiKey @@ -13,7 +30,6 @@ export const buildSelfEndpoints = API => ({ /** * retrieves the API key for the logged in user. - * @return {Promise} An object containing the user developer information. */ fetchDeveloperInfo: async () => { return API.get({ diff --git a/packages/frontend-core/src/api/tables.js b/packages/frontend-core/src/api/tables.js deleted file mode 100644 index dc9008e4eb..0000000000 --- a/packages/frontend-core/src/api/tables.js +++ /dev/null @@ -1,152 +0,0 @@ -export const buildTableEndpoints = API => ({ - /** - * Fetches a table definition. - * Since definitions cannot change at runtime, the result is cached. - * @param tableId the ID of the table to fetch - */ - fetchTableDefinition: async tableId => { - return await API.get({ - url: `/api/tables/${tableId}`, - cache: true, - }) - }, - - /** - * Fetches all rows from a table. - * @param tableId the ID of the table for fetch - */ - fetchTableData: async tableId => { - return await API.get({ url: `/api/${tableId}/rows` }) - }, - - /** - * Searches a table using Lucene. - * @param tableId the ID of the table to search - * @param query the lucene search query - * @param bookmark the current pagination bookmark - * @param limit the number of rows to retrieve - * @param sort the field to sort by - * @param sortOrder the order to sort by - * @param sortType the type to sort by, either numerically or alphabetically - * @param paginate whether to paginate the data - */ - searchTable: async ({ - tableId, - query, - bookmark, - limit, - sort, - sortOrder, - sortType, - paginate, - }) => { - if (!tableId) { - return { - rows: [], - } - } - return await API.post({ - url: `/api/${tableId}/search`, - body: { - ...(query ? { query } : {}), - bookmark, - limit, - sort, - sortOrder, - sortType, - paginate, - }, - }) - }, - - /** - * Imports data into an existing table - * @param tableId the table ID to import to - * @param rows the data import object - * @param identifierFields column names to be used as keys for overwriting existing rows - */ - importTableData: async ({ tableId, rows, identifierFields }) => { - return await API.post({ - url: `/api/tables/${tableId}/import`, - body: { - rows, - identifierFields, - }, - }) - }, - csvToJson: async csvString => { - return await API.post({ - url: "/api/convert/csvToJson", - body: { - csvString, - }, - }) - }, - - /** - * Gets a list of tables. - */ - getTables: async () => { - return await API.get({ - url: "/api/tables", - }) - }, - - /** - * Get a single table based on table ID. - */ - getTable: async tableId => { - return await API.get({ - url: `/api/tables/${tableId}`, - }) - }, - - /** - * Saves a table. - * @param table the table to save - */ - saveTable: async table => { - return await API.post({ - url: "/api/tables", - body: table, - }) - }, - - /** - * Deletes a table. - * @param tableId the ID of the table to delete - * @param tableRev the rev of the table to delete - */ - deleteTable: async ({ tableId, tableRev }) => { - return await API.delete({ - url: `/api/tables/${tableId}/${tableRev}`, - }) - }, - validateNewTableImport: async ({ rows, schema }) => { - return await API.post({ - url: "/api/tables/validateNewTableImport", - body: { - rows, - schema, - }, - }) - }, - validateExistingTableImport: async ({ rows, tableId }) => { - return await API.post({ - url: "/api/tables/validateExistingTableImport", - body: { - rows, - tableId, - }, - }) - }, - migrateColumn: async ({ tableId, oldColumn, newColumn }) => { - return await API.post({ - url: `/api/tables/${tableId}/migrate`, - body: { - oldColumn, - newColumn, - }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/tables.ts b/packages/frontend-core/src/api/tables.ts new file mode 100644 index 0000000000..ebbd7c09bf --- /dev/null +++ b/packages/frontend-core/src/api/tables.ts @@ -0,0 +1,192 @@ +import { + BulkImportRequest, + BulkImportResponse, + CsvToJsonRequest, + CsvToJsonResponse, + FetchTablesResponse, + Row, + SaveTableRequest, + SaveTableResponse, + SearchRowRequest, + PaginatedSearchRowResponse, + TableSchema, + ValidateNewTableImportRequest, + ValidateTableImportRequest, + ValidateTableImportResponse, + FindTableResponse, + FetchRowsResponse, + MigrateTableResponse, + MigrateTableRequest, + DeleteTableResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface TableEndpoints { + fetchTableDefinition: (tableId: string) => Promise + fetchTableData: (tableId: string) => Promise + searchTable: ( + sourceId: string, + opts: SearchRowRequest + ) => Promise + importTableData: ( + tableId: string, + rows: Row[], + identifierFields?: string[] + ) => Promise + csvToJson: (csvString: string) => Promise + getTables: () => Promise + getTable: (tableId: string) => Promise + saveTable: (table: SaveTableRequest) => Promise + deleteTable: (id: string, rev: string) => Promise + validateNewTableImport: ( + rows: Row[], + schema: TableSchema + ) => Promise + validateExistingTableImport: ( + rows: Row[], + tableId?: string + ) => Promise + migrateColumn: ( + tableId: string, + oldColumn: string, + newColumn: string + ) => Promise +} + +export const buildTableEndpoints = (API: BaseAPIClient): TableEndpoints => ({ + /** + * Fetches a table definition. + * Since definitions cannot change at runtime, the result is cached. + * @param tableId the ID of the table to fetch + */ + fetchTableDefinition: async tableId => { + return await API.get({ + url: `/api/tables/${tableId}`, + cache: true, + }) + }, + + /** + * Fetches all rows from a table. + * @param sourceId the ID of the table to fetch + */ + fetchTableData: async sourceId => { + return await API.get({ url: `/api/${sourceId}/rows` }) + }, + + /** + * Searches a table using Lucene. + * @param sourceId the ID of the table to search + * @param opts the search opts + */ + searchTable: async (sourceId, opts) => { + return await API.post({ + url: `/api/${sourceId}/search`, + body: opts, + }) + }, + + /** + * Imports data into an existing table + * @param tableId the table ID to import to + * @param rows the data import object + * @param identifierFields column names to be used as keys for overwriting existing rows + */ + importTableData: async (tableId, rows, identifierFields) => { + return await API.post({ + url: `/api/tables/${tableId}/import`, + body: { + rows, + identifierFields, + }, + }) + }, + + /** + * Converts a CSV string to JSON + * @param csvString the CSV string + */ + csvToJson: async csvString => { + return await API.post({ + url: "/api/convert/csvToJson", + body: { + csvString, + }, + }) + }, + + /** + * Gets a list of tables. + */ + getTables: async () => { + return await API.get({ + url: "/api/tables", + }) + }, + + /** + * Get a single table based on table ID. + * Dupe of fetchTableDefinition but not cached? + */ + getTable: async tableId => { + return await API.get({ + url: `/api/tables/${tableId}`, + }) + }, + + /** + * Saves a table. + * @param table the table to save + */ + saveTable: async table => { + return await API.post({ + url: "/api/tables", + body: table, + }) + }, + + /** + * Deletes a table. + * @param id the ID of the table to delete + * @param rev the rev of the table to delete + */ + deleteTable: async (id, rev) => { + return await API.delete({ + url: `/api/tables/${id}/${rev}`, + }) + }, + + validateNewTableImport: async (rows, schema) => { + return await API.post< + ValidateNewTableImportRequest, + ValidateTableImportResponse + >({ + url: "/api/tables/validateNewTableImport", + body: { + rows, + schema, + }, + }) + }, + validateExistingTableImport: async (rows, tableId) => { + return await API.post< + ValidateTableImportRequest, + ValidateTableImportResponse + >({ + url: "/api/tables/validateExistingTableImport", + body: { + rows, + tableId, + }, + }) + }, + migrateColumn: async (tableId, oldColumn, newColumn) => { + return await API.post({ + url: `/api/tables/${tableId}/migrate`, + body: { + oldColumn, + newColumn, + }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/templates.js b/packages/frontend-core/src/api/templates.js deleted file mode 100644 index 660a85d745..0000000000 --- a/packages/frontend-core/src/api/templates.js +++ /dev/null @@ -1,35 +0,0 @@ -export const buildTemplateEndpoints = API => ({ - /** - * Gets the list of email template definitions. - */ - getEmailTemplateDefinitions: async () => { - return await API.get({ url: "/api/global/template/definitions" }) - }, - - /** - * Gets the list of email templates. - */ - getEmailTemplates: async () => { - return await API.get({ url: "/api/global/template/email" }) - }, - - /** - * Saves an email template. - * @param template the template to save - */ - saveEmailTemplate: async template => { - return await API.post({ - url: "/api/global/template", - body: template, - }) - }, - - /** - * Gets a list of app templates. - */ - getAppTemplates: async () => { - return await API.get({ - url: "/api/templates", - }) - }, -}) diff --git a/packages/frontend-core/src/api/templates.ts b/packages/frontend-core/src/api/templates.ts new file mode 100644 index 0000000000..f712bb1d39 --- /dev/null +++ b/packages/frontend-core/src/api/templates.ts @@ -0,0 +1,57 @@ +import { + FetchGlobalTemplateByTypeResponse, + FetchGlobalTemplateDefinitionResponse, + FetchTemplateResponse, + SaveGlobalTemplateRequest, + SaveGlobalTemplateResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface TemplateEndpoints { + getEmailTemplates: () => Promise + getAppTemplates: () => Promise + getEmailTemplateDefinitions: () => Promise + saveEmailTemplate: ( + template: SaveGlobalTemplateRequest + ) => Promise +} + +export const buildTemplateEndpoints = ( + API: BaseAPIClient +): TemplateEndpoints => ({ + /** + * Gets the list of email template definitions. + */ + getEmailTemplateDefinitions: async () => { + return await API.get({ url: "/api/global/template/definitions" }) + }, + + /** + * Gets the list of email templates. + */ + getEmailTemplates: async () => { + return await API.get({ + url: "/api/global/template/email", + }) + }, + + /** + * Saves an email template. + * @param template the template to save + */ + saveEmailTemplate: async template => { + return await API.post({ + url: "/api/global/template", + body: template, + }) + }, + + /** + * Gets a list of app templates. + */ + getAppTemplates: async () => { + return await API.get({ + url: "/api/templates", + }) + }, +}) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts new file mode 100644 index 0000000000..0db1049591 --- /dev/null +++ b/packages/frontend-core/src/api/types.ts @@ -0,0 +1,136 @@ +import { AIEndpoints } from "./ai" +import { AnalyticsEndpoints } from "./analytics" +import { AppEndpoints } from "./app" +import { AttachmentEndpoints } from "./attachments" +import { AuditLogEndpoints } from "./auditLogs" +import { AuthEndpoints } from "./auth" +import { AutomationEndpoints } from "./automations" +import { BackupEndpoints } from "./backups" +import { ConfigEndpoints } from "./configs" +import { DatasourceEndpoints } from "./datasources" +import { EnvironmentVariableEndpoints } from "./environmentVariables" +import { EventEndpoints } from "./events" +import { FlagEndpoints } from "./flags" +import { GroupEndpoints } from "./groups" +import { LayoutEndpoints } from "./layouts" +import { LicensingEndpoints } from "./licensing" +import { LogEndpoints } from "./logs" +import { MigrationEndpoints } from "./migrations" +import { OtherEndpoints } from "./other" +import { PermissionEndpoints } from "./permissions" +import { PluginEndpoins } from "./plugins" +import { QueryEndpoints } from "./queries" +import { RelationshipEndpoints } from "./relationships" +import { RoleEndpoints } from "./roles" +import { RouteEndpoints } from "./routes" +import { RowActionEndpoints } from "./rowActions" +import { RowEndpoints } from "./rows" +import { ScreenEndpoints } from "./screens" +import { SelfEndpoints } from "./self" +import { TableEndpoints } from "./tables" +import { TemplateEndpoints } from "./templates" +import { UserEndpoints } from "./user" +import { ViewEndpoints } from "./views" +import { ViewV2Endpoints } from "./viewsV2" + +export enum HTTPMethod { + POST = "POST", + PATCH = "PATCH", + GET = "GET", + PUT = "PUT", + DELETE = "DELETE", +} + +export type Headers = Record + +export type APIClientConfig = { + enableCaching?: boolean + attachHeaders?: (headers: Headers) => void + onError?: (error: any) => void + onMigrationDetected?: (migration: string) => void +} + +export type APICallConfig = { + method: HTTPMethod + url: string + json: boolean + external: boolean + suppressErrors: boolean + cache: boolean + body?: RequestT + parseResponse?: (response: Response) => Promise | ResponseT +} + +export type APICallParams< + RequestT = null, + ResponseT = void +> = RequestT extends null + ? Pick, "url"> & + Partial> + : Pick, "url" | "body"> & + Partial> + +export type BaseAPIClient = { + post: ( + params: APICallParams + ) => Promise + get: ( + params: APICallParams + ) => Promise + put: ( + params: APICallParams + ) => Promise + delete: ( + params: APICallParams + ) => Promise + patch: ( + params: APICallParams + ) => Promise + error: (message: string) => void + invalidateCache: () => void + getAppID: () => string +} + +export type APIError = { + message?: string + url?: string + method?: HTTPMethod + json: any + status: number + handled: boolean + suppressErrors: boolean +} + +export type APIClient = BaseAPIClient & + AIEndpoints & + AnalyticsEndpoints & + AppEndpoints & + AttachmentEndpoints & + AuditLogEndpoints & + AuthEndpoints & + AutomationEndpoints & + BackupEndpoints & + ConfigEndpoints & + DatasourceEndpoints & + EnvironmentVariableEndpoints & + EventEndpoints & + FlagEndpoints & + GroupEndpoints & + LayoutEndpoints & + LicensingEndpoints & + LogEndpoints & + MigrationEndpoints & + OtherEndpoints & + PermissionEndpoints & + PluginEndpoins & + QueryEndpoints & + RelationshipEndpoints & + RoleEndpoints & + RouteEndpoints & + RowEndpoints & + ScreenEndpoints & + SelfEndpoints & + TableEndpoints & + TemplateEndpoints & + UserEndpoints & + ViewEndpoints & { rowActions: RowActionEndpoints; viewV2: ViewV2Endpoints } diff --git a/packages/frontend-core/src/api/user.js b/packages/frontend-core/src/api/user.ts similarity index 50% rename from packages/frontend-core/src/api/user.js rename to packages/frontend-core/src/api/user.ts index 45d481183a..84ec68644d 100644 --- a/packages/frontend-core/src/api/user.js +++ b/packages/frontend-core/src/api/user.ts @@ -1,4 +1,82 @@ -export const buildUserEndpoints = API => ({ +import { + AcceptUserInviteRequest, + AcceptUserInviteResponse, + BulkUserCreated, + BulkUserDeleted, + BulkUserRequest, + BulkUserResponse, + CheckInviteResponse, + CountUserResponse, + CreateAdminUserRequest, + CreateAdminUserResponse, + DeleteInviteUsersRequest, + DeleteInviteUsersResponse, + DeleteUserResponse, + FetchUsersResponse, + FindUserResponse, + GetUserInvitesResponse, + InviteUsersRequest, + InviteUsersResponse, + LookupAccountHolderResponse, + SaveUserResponse, + SearchUsersRequest, + SearchUsersResponse, + UpdateInviteRequest, + UpdateInviteResponse, + UpdateSelfMetadataRequest, + UpdateSelfMetadataResponse, + User, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface UserEndpoints { + getUsers: () => Promise + getUser: (userId: string) => Promise + updateOwnMetadata: ( + metadata: UpdateSelfMetadataRequest + ) => Promise + createAdminUser: ( + user: CreateAdminUserRequest + ) => Promise + saveUser: (user: User) => Promise + deleteUser: (userId: string) => Promise + deleteUsers: ( + users: Array<{ + userId: string + email: string + }> + ) => Promise + onboardUsers: (data: InviteUsersRequest) => Promise + getUserInvite: (code: string) => Promise + getUserInvites: () => Promise + inviteUsers: (users: InviteUsersRequest) => Promise + removeUserInvites: ( + data: DeleteInviteUsersRequest + ) => Promise + acceptInvite: ( + data: AcceptUserInviteRequest + ) => Promise + getUserCountByApp: (appId: string) => Promise + getAccountHolder: () => Promise + searchUsers: (data: SearchUsersRequest) => Promise + createUsers: ( + users: User[], + groups: any[] + ) => Promise + updateUserInvite: ( + code: string, + data: UpdateInviteRequest + ) => Promise + + // Missing request or response types + addAppBuilder: (userId: string, appId: string) => Promise<{ message: string }> + removeAppBuilder: ( + userId: string, + appId: string + ) => Promise<{ message: string }> +} + +export const buildUserEndpoints = (API: BaseAPIClient): UserEndpoints => ({ /** * Gets a list of users in the current tenant. */ @@ -9,33 +87,12 @@ export const buildUserEndpoints = API => ({ }, /** - * Gets a list of users in the current tenant. - * @param {string} bookmark The page to retrieve - * @param {object} query search filters for lookup by user (all operators not supported). - * @param {string} appId Facilitate app/role based user searching - * @param {boolean} paginate Allow the disabling of pagination - * @param {number} limit How many users to retrieve in a single search + * Searches a list of users in the current tenant. */ - searchUsers: async ({ paginate, bookmark, query, appId, limit } = {}) => { - const opts = {} - if (bookmark) { - opts.bookmark = bookmark - } - if (query) { - opts.query = query - } - if (appId) { - opts.appId = appId - } - if (typeof paginate === "boolean") { - opts.paginate = paginate - } - if (limit) { - opts.limit = limit - } + searchUsers: async data => { return await API.post({ url: `/api/global/users/search`, - body: opts, + body: data, }) }, @@ -48,17 +105,6 @@ export const buildUserEndpoints = 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 @@ -72,12 +118,12 @@ export const buildUserEndpoints = API => ({ /** * Creates an admin user. - * @param adminUser the admin user to create + * @param user the admin user to create */ - createAdminUser: async adminUser => { + createAdminUser: async user => { return await API.post({ url: "/api/global/users/init", - body: adminUser, + body: user, }) }, @@ -97,8 +143,8 @@ export const buildUserEndpoints = API => ({ * @param users the array of user objects to create * @param groups the array of group ids to add all users to */ - createUsers: async ({ users, groups }) => { - const res = await API.post({ + createUsers: async (users, groups) => { + const res = await API.post({ url: "/api/global/users/bulk", body: { create: { @@ -125,7 +171,7 @@ export const buildUserEndpoints = API => ({ * @param users the ID/email pair of the user to delete */ deleteUsers: async users => { - const res = await API.post({ + const res = await API.post({ url: `/api/global/users/bulk`, body: { delete: { @@ -137,54 +183,23 @@ export const buildUserEndpoints = API => ({ }, /** - * Invites a user to the current tenant. - * @param email the email address to send the invitation to - * @param builder whether the user should be a global builder - * @param admin whether the user should be a global admin + * Onboards multiple users */ - inviteUser: async ({ email, builder, admin, apps }) => { - return await API.post({ - url: "/api/global/users/invite", - body: { - email, - userInfo: { - admin: admin?.global ? { global: true } : undefined, - builder: builder?.global ? { global: true } : undefined, - apps: apps ? apps : undefined, - }, - }, - }) - }, - - onboardUsers: async payload => { + onboardUsers: async data => { return await API.post({ url: "/api/global/users/onboard", - body: payload.map(invite => { - const { email, admin, builder, apps } = invite - return { - email, - userInfo: { - admin, - builder, - apps: apps ? apps : undefined, - }, - } - }), + body: data, }) }, /** * Accepts a user invite as a body and will update the associated app roles. * for an existing invite - * @param invite the invite code sent in the email */ - updateUserInvite: async invite => { - await API.post({ - url: `/api/global/users/invite/update/${invite.code}`, - body: { - apps: invite.apps, - builder: invite.builder, - }, + updateUserInvite: async (code, data) => { + return await API.post({ + url: `/api/global/users/invite/update/${code}`, + body: data, }) }, @@ -214,72 +229,46 @@ export const buildUserEndpoints = API => ({ inviteUsers: async users => { return await API.post({ url: "/api/global/users/multi/invite", - body: users.map(user => { - let builder = undefined - if (user.admin || user.builder) { - builder = { global: true } - } else if (user.creator) { - builder = { creator: true } - } - return { - email: user.email, - userInfo: { - admin: user.admin ? { global: true } : undefined, - builder, - userGroups: user.groups, - roles: user.apps ? user.apps : undefined, - }, - } - }), + body: users, }) }, /** * Removes multiple user invites from Redis cache */ - removeUserInvites: async inviteCodes => { + removeUserInvites: async data => { return await API.post({ url: "/api/global/users/multi/invite/delete", - body: inviteCodes, + body: data, }) }, /** * Accepts an invite to join the platform and creates a user. - * @param inviteCode the invite code sent in the email - * @param password the password for the newly created user - * @param firstName the first name of the new user - * @param lastName the last name of the new user */ - acceptInvite: async ({ inviteCode, password, firstName, lastName }) => { + acceptInvite: async data => { return await API.post({ url: "/api/global/users/invite/accept", - body: { - inviteCode, - password, - firstName, - lastName, - }, + body: data, }) }, /** - * Accepts an invite to join the platform and creates a user. - * @param inviteCode the invite code sent in the email - * @param password the password for the newly created user + * Counts the number of users in an app */ - getUserCountByApp: async ({ appId }) => { - return await API.get({ + getUserCountByApp: async appId => { + const res = await API.get({ url: `/api/global/users/count/${appId}`, }) + return res.userCount }, /** * Adds a per app builder to the selected app - * @param appId the applications id * @param userId The id of the user to add as a builder + * @param appId the applications id */ - addAppBuilder: async ({ userId, appId }) => { + addAppBuilder: async (userId, appId) => { return await API.post({ url: `/api/global/users/${userId}/app/${appId}/builder`, }) @@ -287,15 +276,18 @@ export const buildUserEndpoints = API => ({ /** * Removes a per app builder to the selected app - * @param appId the applications id * @param userId The id of the user to remove as a builder + * @param appId the applications id */ - removeAppBuilder: async ({ userId, appId }) => { + removeAppBuilder: async (userId, appId) => { return await API.delete({ url: `/api/global/users/${userId}/app/${appId}/builder`, }) }, + /** + * Gets the account holder of the current tenant + */ getAccountHolder: async () => { return await API.get({ url: `/api/global/users/accountholder`, diff --git a/packages/frontend-core/src/api/views.js b/packages/frontend-core/src/api/views.ts similarity index 63% rename from packages/frontend-core/src/api/views.js rename to packages/frontend-core/src/api/views.ts index 1d710c5171..3f8ac8aa41 100644 --- a/packages/frontend-core/src/api/views.js +++ b/packages/frontend-core/src/api/views.ts @@ -1,4 +1,15 @@ -export const buildViewEndpoints = API => ({ +import { Row } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface ViewEndpoints { + // Missing request or response types + fetchViewData: (name: string, opts: any) => Promise + exportView: (name: string, format: string) => Promise + saveView: (view: any) => Promise + deleteView: (name: string) => Promise +} + +export const buildViewEndpoints = (API: BaseAPIClient): ViewEndpoints => ({ /** * Fetches all rows in a view * @param name the name of the view @@ -6,7 +17,7 @@ export const buildViewEndpoints = API => ({ * @param groupBy the field to group by * @param calculation the calculation to perform */ - fetchViewData: async ({ name, field, groupBy, calculation }) => { + fetchViewData: async (name, { field, groupBy, calculation }) => { const params = new URLSearchParams() if (calculation) { params.set("field", field) @@ -23,11 +34,11 @@ export const buildViewEndpoints = API => ({ /** * Exports a view for download - * @param viewName the view to export + * @param name the view to export * @param format the format to download */ - exportView: async ({ viewName, format }) => { - const safeViewName = encodeURIComponent(viewName) + exportView: async (name, format) => { + const safeViewName = encodeURIComponent(name) return await API.get({ url: `/api/views/export?view=${safeViewName}&format=${format}`, parseResponse: async response => { @@ -51,9 +62,9 @@ export const buildViewEndpoints = API => ({ * Deletes a view. * @param viewName the name of the view to delete */ - deleteView: async viewName => { + deleteView: async name => { return await API.delete({ - url: `/api/views/${encodeURIComponent(viewName)}`, + url: `/api/views/${encodeURIComponent(name)}`, }) }, }) diff --git a/packages/frontend-core/src/api/viewsV2.js b/packages/frontend-core/src/api/viewsV2.ts similarity index 56% rename from packages/frontend-core/src/api/viewsV2.js rename to packages/frontend-core/src/api/viewsV2.ts index d1cb07c6b0..5018448e8c 100644 --- a/packages/frontend-core/src/api/viewsV2.js +++ b/packages/frontend-core/src/api/viewsV2.ts @@ -1,4 +1,26 @@ -export const buildViewV2Endpoints = API => ({ +import { + CreateViewRequest, + CreateViewResponse, + SearchRowResponse, + SearchViewRowRequest, + UpdateViewRequest, + UpdateViewResponse, + ViewResponseEnriched, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface ViewV2Endpoints { + fetchDefinition: (viewId: string) => Promise + create: (view: CreateViewRequest) => Promise + update: (view: UpdateViewRequest) => Promise + fetch: ( + viewId: string, + opts: SearchViewRowRequest + ) => Promise + delete: (viewId: string) => Promise +} + +export const buildViewV2Endpoints = (API: BaseAPIClient): ViewV2Endpoints => ({ /** * Fetches the definition of a view * @param viewId the ID of the view to fetch @@ -9,6 +31,7 @@ export const buildViewV2Endpoints = API => ({ cache: true, }) }, + /** * Create a new view * @param view the view object @@ -19,6 +42,7 @@ export const buildViewV2Endpoints = API => ({ body: view, }) }, + /** * Updates a view * @param view the view object @@ -29,40 +53,19 @@ export const buildViewV2Endpoints = API => ({ body: view, }) }, + /** * Fetches all rows in a view * @param viewId the id of the view - * @param query the search query - * @param paginate whether to paginate or not - * @param limit page size - * @param bookmark pagination cursor - * @param sort sort column - * @param sortOrder sort order - * @param sortType sort type (text or numeric) + * @param opts the search options */ - fetch: async ({ - viewId, - query, - paginate, - limit, - bookmark, - sort, - sortOrder, - sortType, - }) => { + fetch: async (viewId, opts) => { return await API.post({ url: `/api/v2/views/${encodeURIComponent(viewId)}/search`, - body: { - query, - paginate, - limit, - bookmark, - sort, - sortOrder, - sortType, - }, + body: opts, }) }, + /** * Delete a view * @param viewId the id of the view diff --git a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte index 8ecec03e0e..96443311c2 100644 --- a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte +++ b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte @@ -29,11 +29,11 @@ const migrateUserColumn = async () => { try { - await API.migrateColumn({ - tableId: $definition._id, - oldColumn: column.schema.name, - newColumn: newColumnName, - }) + await API.migrateColumn( + $definition._id, + column.schema.name, + newColumnName + ) notifications.success("Column migrated") } catch (e) { notifications.error(`Failed to migrate: ${e.message}`) diff --git a/packages/frontend-core/src/components/grid/stores/datasources/table.js b/packages/frontend-core/src/components/grid/stores/datasources/table.js index e415f5914b..bb7bf0835e 100644 --- a/packages/frontend-core/src/components/grid/stores/datasources/table.js +++ b/packages/frontend-core/src/components/grid/stores/datasources/table.js @@ -19,10 +19,7 @@ export const createActions = context => { } const deleteRows = async rows => { - await API.deleteRows({ - tableId: get(datasource).tableId, - rows, - }) + await API.deleteRows(get(datasource).tableId, rows) } const isDatasourceValid = datasource => { @@ -30,8 +27,7 @@ export const createActions = context => { } const getRow = async id => { - const res = await API.searchTable({ - tableId: get(datasource).tableId, + const res = await API.searchTable(get(datasource).tableId, { limit: 1, query: { equal: { diff --git a/packages/frontend-core/src/components/grid/stores/datasources/viewV2.js b/packages/frontend-core/src/components/grid/stores/datasources/viewV2.js index 4a4a91658c..b9f4851a60 100644 --- a/packages/frontend-core/src/components/grid/stores/datasources/viewV2.js +++ b/packages/frontend-core/src/components/grid/stores/datasources/viewV2.js @@ -24,15 +24,11 @@ export const createActions = context => { } const deleteRows = async rows => { - await API.deleteRows({ - tableId: get(datasource).id, - rows, - }) + await API.deleteRows(get(datasource).id, rows) } const getRow = async id => { - const res = await API.viewV2.fetch({ - viewId: get(datasource).id, + const res = await API.viewV2.fetch(get(datasource).id, { limit: 1, query: { equal: { diff --git a/packages/frontend-core/src/constants.js b/packages/frontend-core/src/constants.ts similarity index 98% rename from packages/frontend-core/src/constants.js rename to packages/frontend-core/src/constants.ts index 662b086915..8a39e8c106 100644 --- a/packages/frontend-core/src/constants.js +++ b/packages/frontend-core/src/constants.ts @@ -76,9 +76,9 @@ export const ExtendedBudibaseRoleOptions = [ value: BudibaseRoles.Owner, sortOrder: 0, }, + ...BudibaseRoleOptions, + ...BudibaseRoleOptionsOld, ] - .concat(BudibaseRoleOptions) - .concat(BudibaseRoleOptionsOld) export const PlanType = { FREE: "free", diff --git a/packages/frontend-core/src/fetch/QueryFetch.js b/packages/frontend-core/src/fetch/QueryFetch.js index 6420893515..9fac9704d3 100644 --- a/packages/frontend-core/src/fetch/QueryFetch.js +++ b/packages/frontend-core/src/fetch/QueryFetch.js @@ -48,7 +48,7 @@ export default class QueryFetch extends DataFetch { } // Add pagination to query if supported - let queryPayload = { queryId: datasource?._id, parameters } + let queryPayload = { parameters } if (paginate && supportsPagination) { const requestCursor = type === "page" ? parseInt(cursor || 1) : cursor queryPayload.pagination = { page: requestCursor, limit } @@ -56,7 +56,7 @@ export default class QueryFetch extends DataFetch { // Execute query try { - const res = await this.API.executeQuery(queryPayload) + const res = await this.API.executeQuery(datasource?._id, queryPayload) const { data, pagination, ...rest } = res // Derive pagination info from response diff --git a/packages/frontend-core/src/fetch/RelationshipFetch.js b/packages/frontend-core/src/fetch/RelationshipFetch.js index 04797fcdf1..0dec535724 100644 --- a/packages/frontend-core/src/fetch/RelationshipFetch.js +++ b/packages/frontend-core/src/fetch/RelationshipFetch.js @@ -3,13 +3,16 @@ import DataFetch from "./DataFetch.js" export default class RelationshipFetch extends DataFetch { async getData() { const { datasource } = this.options + if (!datasource?.rowId || !datasource?.rowTableId) { + return { rows: [] } + } try { - const res = await this.API.fetchRelationshipData({ - rowId: datasource?.rowId, - tableId: datasource?.rowTableId, - fieldName: datasource?.fieldName, - }) - return { rows: res || [] } + const res = await this.API.fetchRelationshipData( + datasource.rowTableId, + datasource.rowId, + datasource.fieldName + ) + return { rows: res } } catch (error) { return { rows: [] } } diff --git a/packages/frontend-core/src/fetch/TableFetch.js b/packages/frontend-core/src/fetch/TableFetch.js index ed17c20c79..777d16aa45 100644 --- a/packages/frontend-core/src/fetch/TableFetch.js +++ b/packages/frontend-core/src/fetch/TableFetch.js @@ -19,8 +19,7 @@ export default class TableFetch extends DataFetch { // Search table try { - const res = await this.API.searchTable({ - tableId, + const res = await this.API.searchTable(tableId, { query, limit, sort: sortColumn, diff --git a/packages/frontend-core/src/fetch/ViewFetch.js b/packages/frontend-core/src/fetch/ViewFetch.js index 981969f46c..272c222dd4 100644 --- a/packages/frontend-core/src/fetch/ViewFetch.js +++ b/packages/frontend-core/src/fetch/ViewFetch.js @@ -8,7 +8,7 @@ export default class ViewFetch extends DataFetch { async getData() { const { datasource } = this.options try { - const res = await this.API.fetchViewData(datasource) + const res = await this.API.fetchViewData(datasource.name) return { rows: res || [] } } catch (error) { return { rows: [] } diff --git a/packages/frontend-core/src/fetch/ViewV2Fetch.js b/packages/frontend-core/src/fetch/ViewV2Fetch.js index 6bfef0927f..8436646077 100644 --- a/packages/frontend-core/src/fetch/ViewV2Fetch.js +++ b/packages/frontend-core/src/fetch/ViewV2Fetch.js @@ -62,8 +62,7 @@ export default class ViewV2Fetch extends DataFetch { } try { - const res = await this.API.viewV2.fetch({ - viewId: datasource.id, + const res = await this.API.viewV2.fetch(datasource.id, { ...(query ? { query } : {}), paginate, limit, diff --git a/packages/frontend-core/src/index.js b/packages/frontend-core/src/index.ts similarity index 100% rename from packages/frontend-core/src/index.js rename to packages/frontend-core/src/index.ts diff --git a/packages/frontend-core/tsconfig.json b/packages/frontend-core/tsconfig.json new file mode 100644 index 0000000000..3900034413 --- /dev/null +++ b/packages/frontend-core/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ESNext", + "moduleResolution": "bundler", + "skipLibCheck": true, + "paths": { + "@budibase/types": ["../types/src"], + "@budibase/shared-core": ["../shared-core/src"], + "@budibase/bbui": ["../bbui/src"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/server/nodemon.json b/packages/server/nodemon.json index b0e792d945..ac8c38ccb2 100644 --- a/packages/server/nodemon.json +++ b/packages/server/nodemon.json @@ -1,13 +1,19 @@ { "watch": [ "src", - "../backend-core", - "../pro", - "../types", - "../shared-core", - "../string-templates" + "../backend-core/src", + "../pro/src", + "../types/src", + "../shared-core/src", + "../string-templates/src" ], "ext": "js,ts,json,svelte", - "ignore": ["**/*.spec.ts", "**/*.spec.js", "../*/dist/**/*", "client/**/*", "builder/**/*"], + "ignore": [ + "**/*.spec.ts", + "**/*.spec.js", + "../*/dist/**/*", + "client/**/*", + "builder/**/*" + ], "exec": "yarn build && node --no-node-snapshot ./dist/index.js" }