Merge pull request #15096 from Budibase/frontend-core-ts-2

Convert frontend-core API client to Typescript
This commit is contained in:
Andrew Kingston 2024-12-17 10:22:24 +00:00 committed by GitHub
commit b7d7946e17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
112 changed files with 2307 additions and 1725 deletions

View File

@ -8,7 +8,7 @@ import { get } from "svelte/store"
import { auth, navigation } from "./stores/portal" import { auth, navigation } from "./stores/portal"
export const API = createAPIClient({ export const API = createAPIClient({
attachHeaders: (headers: Record<string, string>) => { attachHeaders: headers => {
// Attach app ID header from store // Attach app ID header from store
let appId = get(appStore).appId let appId = get(appStore).appId
if (appId) { if (appId) {
@ -22,7 +22,7 @@ export const API = createAPIClient({
} }
}, },
onError: (error: any) => { onError: error => {
const { url, message, status, method, handled } = error || {} const { url, message, status, method, handled } = error || {}
// Log any errors that we haven't manually handled // 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}` const updatingUrl = `/builder/app/updating/${appId}`
if (window.location.pathname === updatingUrl) { if (window.location.pathname === updatingUrl) {

View File

@ -98,9 +98,7 @@
async function generateAICronExpression() { async function generateAICronExpression() {
loadingAICronExpression = true loadingAICronExpression = true
try { try {
const response = await API.generateCronExpression({ const response = await API.generateCronExpression(aiCronPrompt)
prompt: aiCronPrompt,
})
cronExpression = response.message cronExpression = response.message
dispatch("change", response.message) dispatch("change", response.message)
} catch (err) { } catch (err) {

View File

@ -56,28 +56,19 @@
} }
const exportAllData = async () => { const exportAllData = async () => {
return await API.exportView({ return await API.exportView(view, exportFormat)
viewName: view,
format: exportFormat,
})
} }
const exportFilteredData = async () => { const exportFilteredData = async () => {
let payload = { let payload = {}
tableId: view,
format: exportFormat,
search: {
paginate: false,
},
}
if (selectedRows?.length) { if (selectedRows?.length) {
payload.rows = selectedRows.map(row => row._id) payload.rows = selectedRows.map(row => row._id)
} }
if (sorting) { if (sorting) {
payload.search.sort = sorting.sortColumn payload.sort = sorting.sortColumn
payload.search.sortOrder = sorting.sortOrder payload.sortOrder = sorting.sortOrder
} }
return await API.exportRows(payload) return await API.exportRows(view, exportFormat, payload)
} }
const exportData = async () => { const exportData = async () => {

View File

@ -30,11 +30,7 @@
const importData = async () => { const importData = async () => {
try { try {
loading = true loading = true
await API.importTableData({ await API.importTableData(tableId, rows, identifierFields)
tableId,
rows,
identifierFields,
})
notifications.success("Rows successfully imported") notifications.success("Rows successfully imported")
popover.hide() popover.hide()
} catch (error) { } catch (error) {

View File

@ -39,9 +39,9 @@
const toggleAction = async (action, enabled) => { const toggleAction = async (action, enabled) => {
if (enabled) { if (enabled) {
await rowActions.enableView(tableId, viewId, action.id) await rowActions.enableView(tableId, action.id, viewId)
} else { } else {
await rowActions.disableView(tableId, viewId, action.id) await rowActions.disableView(tableId, action.id, viewId)
} }
} }

View File

@ -128,11 +128,7 @@
allValid = false allValid = false
if (rows.length > 0) { if (rows.length > 0) {
const response = await API.validateExistingTableImport({ const response = await API.validateExistingTableImport(rows, tableId)
rows,
tableId,
})
validation = response.schemaValidation validation = response.schemaValidation
invalidColumns = response.invalidColumns invalidColumns = response.invalidColumns
allValid = response.allValid allValid = response.allValid

View File

@ -147,7 +147,7 @@
loading = true loading = true
try { try {
if (rows.length > 0) { if (rows.length > 0) {
const response = await API.validateNewTableImport({ rows, schema }) const response = await API.validateNewTableImport(rows, schema)
validation = response.schemaValidation validation = response.schemaValidation
allValid = response.allValid allValid = response.allValid
errors = response.errors errors = response.errors

View File

@ -68,7 +68,7 @@
} }
try { try {
const app = await API.duplicateApp(data, appId) const app = await API.duplicateApp(appId, data)
appsStore.load() appsStore.load()
if (!sdk.users.isBuilder($auth.user, app?.duplicateAppId)) { if (!sdk.users.isBuilder($auth.user, app?.duplicateAppId)) {
// Refresh for access to created applications // Refresh for access to created applications

View File

@ -1,8 +0,0 @@
declare module "api" {
const API: {
getPlugins: () => Promise<any>
createPlugin: (plugin: object) => Promise<any>
uploadPlugin: (plugin: FormData) => Promise<any>
deletePlugin: (id: string) => Promise<void>
}
}

View File

@ -105,9 +105,6 @@
if (!hasSynced && application) { if (!hasSynced && application) {
try { try {
await API.syncApp(application) await API.syncApp(application)
// check if user has beta access
// const betaResponse = await API.checkBetaAccess($auth?.user?.email)
// betaAccess = betaResponse.access
} catch (error) { } catch (error) {
notifications.error("Failed to sync with production database") notifications.error("Failed to sync with production database")
} }

View File

@ -43,8 +43,7 @@
return return
} }
try { try {
data = await API.fetchViewData({ data = await API.fetchViewData(name, {
name,
calculation, calculation,
field, field,
groupBy, groupBy,

View File

@ -99,21 +99,18 @@
} }
async function fetchBackups(filters, page, dateRange = []) { async function fetchBackups(filters, page, dateRange = []) {
const body = { const opts = {
appId: $appStore.appId,
...filters, ...filters,
page, page,
} }
const [startDate, endDate] = dateRange const [startDate, endDate] = dateRange
if (startDate) { if (startDate) {
body.startDate = startDate opts.startDate = startDate
} }
if (endDate) { if (endDate) {
body.endDate = endDate opts.endDate = endDate
} }
const response = await backups.searchBackups($appStore.appId, opts)
const response = await backups.searchBackups(body)
pageInfo.fetched(response.hasNextPage, response.nextPage) pageInfo.fetched(response.hasNextPage, response.nextPage)
// flatten so we have an easier structure to use for the table schema // flatten so we have an easier structure to use for the table schema
@ -123,9 +120,7 @@
async function createManualBackup() { async function createManualBackup() {
try { try {
loading = true loading = true
let response = await backups.createManualBackup({ let response = await backups.createManualBackup($appStore.appId)
appId: $appStore.appId,
})
await fetchBackups(filterOpt, page) await fetchBackups(filterOpt, page)
notifications.success(response.message) notifications.success(response.message)
} catch (err) { } catch (err) {
@ -149,24 +144,14 @@
async function handleButtonClick({ detail }) { async function handleButtonClick({ detail }) {
if (detail.type === "backupDelete") { if (detail.type === "backupDelete") {
await backups.deleteBackup({ await backups.deleteBackup($appStore.appId, detail.backupId)
appId: $appStore.appId,
backupId: detail.backupId,
})
await fetchBackups(filterOpt, page) await fetchBackups(filterOpt, page)
} else if (detail.type === "backupRestore") { } else if (detail.type === "backupRestore") {
await backups.restoreBackup({ await backups.restoreBackup(
appId: $appStore.appId, $appStore.appId,
backupId: detail.backupId, detail.backupId,
name: detail.restoreBackupName, detail.restoreBackupName
}) )
await fetchBackups(filterOpt, page)
} else if (detail.type === "backupUpdate") {
await backups.updateBackup({
appId: $appStore.appId,
backupId: detail.backupId,
name: detail.name,
})
await fetchBackups(filterOpt, page) await fetchBackups(filterOpt, page)
} }
} }

View File

@ -152,8 +152,8 @@
logsPageInfo.loading() logsPageInfo.loading()
await auditLogs.search({ await auditLogs.search({
bookmark: logsPage, bookmark: logsPage,
startDate: dateRange[0], startDate: dateRange[0] || undefined,
endDate: dateRange[1], endDate: dateRange[1] || undefined,
fullSearch: logSearchTerm, fullSearch: logSearchTerm,
userIds: selectedUsers, userIds: selectedUsers,
appIds: selectedApps, appIds: selectedApps,

View File

@ -64,7 +64,7 @@
const activateLicenseKey = async () => { const activateLicenseKey = async () => {
try { try {
await API.activateLicenseKey({ licenseKey }) await API.activateLicenseKey(licenseKey)
await auth.getSelf() await auth.getSelf()
await getLicenseKey() await getLicenseKey()
notifications.success("Successfully activated") notifications.success("Successfully activated")
@ -119,7 +119,7 @@
async function activateOfflineLicense(offlineLicenseToken) { async function activateOfflineLicense(offlineLicenseToken) {
try { try {
await API.activateOfflineLicense({ offlineLicenseToken }) await API.activateOfflineLicense(offlineLicenseToken)
await auth.getSelf() await auth.getSelf()
await getOfflineLicense() await getOfflineLicense()
notifications.success("Successfully activated") notifications.success("Successfully activated")

View File

@ -140,10 +140,7 @@
if (image) { if (image) {
let data = new FormData() let data = new FormData()
data.append("file", image) data.append("file", image)
await API.uploadOIDCLogo({ await API.uploadOIDCLogo(image.name, data)
name: image.name,
data,
})
} }
} }

View File

@ -69,10 +69,7 @@
async function deleteSmtp() { async function deleteSmtp() {
// Delete the SMTP config // Delete the SMTP config
try { try {
await API.deleteConfig({ await API.deleteConfig(smtpConfig._id, smtpConfig._rev)
id: smtpConfig._id,
rev: smtpConfig._rev,
})
smtpConfig = { smtpConfig = {
type: ConfigTypes.SMTP, type: ConfigTypes.SMTP,
config: { config: {

View File

@ -751,10 +751,7 @@ const automationActions = store => ({
automation.definition.trigger.inputs.rowActionId automation.definition.trigger.inputs.rowActionId
) )
} else { } else {
await API.deleteAutomation({ await API.deleteAutomation(automation?._id, automation?._rev)
automationId: automation?._id,
automationRev: automation?._rev,
})
} }
store.update(state => { store.update(state => {
@ -836,10 +833,7 @@ const automationActions = store => ({
test: async (automation, testData) => { test: async (automation, testData) => {
let result let result
try { try {
result = await API.testAutomation({ result = await API.testAutomation(automation?._id, testData)
automationId: automation?._id,
testData,
})
} catch (err) { } catch (err) {
const message = err.message || err.status || JSON.stringify(err) const message = err.message || err.status || JSON.stringify(err)
throw `Automation test failed - ${message}` throw `Automation test failed - ${message}`
@ -893,10 +887,7 @@ const automationActions = store => ({
}) })
}, },
clearLogErrors: async ({ automationId, appId } = {}) => { clearLogErrors: async ({ automationId, appId } = {}) => {
return await API.clearAutomationLogErrors({ return await API.clearAutomationLogErrors(automationId, appId)
automationId,
appId,
})
}, },
addTestDataToAutomation: data => { addTestDataToAutomation: data => {
let newAutomation = cloneDeep(get(selectedAutomation).data) let newAutomation = cloneDeep(get(selectedAutomation).data)

View File

@ -10,14 +10,11 @@ export function createFlagsStore() {
set(flags) set(flags)
}, },
updateFlag: async (flag, value) => { updateFlag: async (flag, value) => {
await API.updateFlag({ await API.updateFlag(flag, value)
flag,
value,
})
await actions.fetch() await actions.fetch()
}, },
toggleUiFeature: async feature => { toggleUiFeature: async feature => {
await API.toggleUiFeature({ value: feature }) await API.toggleUiFeature(feature)
}, },
} }

View File

@ -59,10 +59,7 @@ export class LayoutStore extends BudiStore {
if (!layout?._id) { if (!layout?._id) {
return return
} }
await API.deleteLayout({ await API.deleteLayout(layout._id, layout._rev)
layoutId: layout._id,
layoutRev: layout._rev,
})
this.update(state => { this.update(state => {
state.layouts = state.layouts.filter(x => x._id !== layout._id) state.layouts = state.layouts.filter(x => x._id !== layout._id)
return state return state

View File

@ -35,10 +35,7 @@ export class NavigationStore extends BudiStore {
async save(navigation) { async save(navigation) {
const appId = get(appStore).appId const appId = get(appStore).appId
const app = await API.saveAppMetadata({ const app = await API.saveAppMetadata(appId, { navigation })
appId,
metadata: { navigation },
})
this.syncAppNavigation(app.navigation) this.syncAppNavigation(app.navigation)
} }

View File

@ -7,18 +7,10 @@ export function createPermissionStore() {
return { return {
subscribe, subscribe,
save: async ({ level, role, resource }) => { save: async ({ level, role, resource }) => {
return await API.updatePermissionForResource({ return await API.updatePermissionForResource(resource, role, level)
resourceId: resource,
roleId: role,
level,
})
}, },
remove: async ({ level, role, resource }) => { remove: async ({ level, role, resource }) => {
return await API.removePermissionFromResource({ return await API.removePermissionFromResource(resource, role, level)
resourceId: resource,
roleId: role,
level,
})
}, },
forResource: async resourceId => { forResource: async resourceId => {
return (await API.getPermissionForResource(resourceId)).permissions return (await API.getPermissionForResource(resourceId)).permissions

View File

@ -62,10 +62,7 @@ export function createQueriesStore() {
} }
const importQueries = async ({ data, datasourceId }) => { const importQueries = async ({ data, datasourceId }) => {
return await API.importQueries({ return await API.importQueries(datasourceId, data)
datasourceId,
data,
})
} }
const select = id => { const select = id => {
@ -87,10 +84,7 @@ export function createQueriesStore() {
} }
const deleteQuery = async query => { const deleteQuery = async query => {
await API.deleteQuery({ await API.deleteQuery(query._id, query._rev)
queryId: query?._id,
queryRev: query?._rev,
})
store.update(state => { store.update(state => {
state.list = state.list.filter(existing => existing._id !== query._id) state.list = state.list.filter(existing => existing._id !== query._id)
return state return state

View File

@ -43,10 +43,7 @@ export function createRolesStore() {
setRoles(roles) setRoles(roles)
}, },
delete: async role => { delete: async role => {
await API.deleteRole({ await API.deleteRole(role._id, role._rev)
roleId: role?._id,
roleRev: role?._rev,
})
await actions.fetch() await actions.fetch()
}, },
save: async role => { save: async role => {

View File

@ -55,15 +55,12 @@ export class RowActionStore extends BudiStore {
} }
// Create the action // Create the action
const res = await API.rowActions.create({ const res = await API.rowActions.create(tableId, name)
name,
tableId,
})
// Enable action on this view if adding via a view // Enable action on this view if adding via a view
if (viewId) { if (viewId) {
await Promise.all([ await Promise.all([
this.enableView(tableId, viewId, res.id), this.enableView(tableId, res.id, viewId),
automationStore.actions.fetch(), automationStore.actions.fetch(),
]) ])
} else { } else {
@ -76,21 +73,13 @@ export class RowActionStore extends BudiStore {
return res return res
} }
enableView = async (tableId, viewId, rowActionId) => { enableView = async (tableId, rowActionId, viewId) => {
await API.rowActions.enableView({ await API.rowActions.enableView(tableId, rowActionId, viewId)
tableId,
viewId,
rowActionId,
})
await this.refreshRowActions(tableId) await this.refreshRowActions(tableId)
} }
disableView = async (tableId, viewId, rowActionId) => { disableView = async (tableId, rowActionId, viewId) => {
await API.rowActions.disableView({ await API.rowActions.disableView(tableId, rowActionId, viewId)
tableId,
viewId,
rowActionId,
})
await this.refreshRowActions(tableId) await this.refreshRowActions(tableId)
} }
@ -105,21 +94,14 @@ export class RowActionStore extends BudiStore {
} }
delete = async (tableId, rowActionId) => { delete = async (tableId, rowActionId) => {
await API.rowActions.delete({ await API.rowActions.delete(tableId, rowActionId)
tableId,
rowActionId,
})
await this.refreshRowActions(tableId) await this.refreshRowActions(tableId)
// We don't need to refresh automations as we can only delete row actions // 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 // from the automations store, so we already handle the state update there
} }
trigger = async (sourceId, rowActionId, rowId) => { trigger = async (sourceId, rowActionId, rowId) => {
await API.rowActions.trigger({ await API.rowActions.trigger(sourceId, rowActionId, rowId)
sourceId,
rowActionId,
rowId,
})
} }
} }

View File

@ -344,12 +344,7 @@ export class ScreenStore extends BudiStore {
let deleteUrls = [] let deleteUrls = []
screensToDelete.forEach(screen => { screensToDelete.forEach(screen => {
// Delete the screen // Delete the screen
promises.push( promises.push(API.deleteScreen(screen._id, screen._rev))
API.deleteScreen({
screenId: screen._id,
screenRev: screen._rev,
})
)
// Remove links to this screen // Remove links to this screen
deleteUrls.push(screen.routing.route) deleteUrls.push(screen.routing.route)
}) })

View File

@ -14,19 +14,13 @@ const createsnippets = () => {
...get(store).filter(snippet => snippet.name !== updatedSnippet.name), ...get(store).filter(snippet => snippet.name !== updatedSnippet.name),
updatedSnippet, updatedSnippet,
] ]
const app = await API.saveAppMetadata({ const app = await API.saveAppMetadata(get(appStore).appId, { snippets })
appId: get(appStore).appId,
metadata: { snippets },
})
syncMetadata(app) syncMetadata(app)
} }
const deleteSnippet = async snippetName => { const deleteSnippet = async snippetName => {
const snippets = get(store).filter(snippet => snippet.name !== snippetName) const snippets = get(store).filter(snippet => snippet.name !== snippetName)
const app = await API.saveAppMetadata({ const app = await API.saveAppMetadata(get(appStore).appId, { snippets })
appId: get(appStore).appId,
metadata: { snippets },
})
syncMetadata(app) syncMetadata(app)
} }

View File

@ -110,10 +110,7 @@ export function createTablesStore() {
if (!table?._id) { if (!table?._id) {
return return
} }
await API.deleteTable({ await API.deleteTable(table._id, table._rev || "rev")
tableId: table._id,
tableRev: table._rev || "rev",
})
replaceTable(table._id, null) replaceTable(table._id, null)
} }

View File

@ -264,10 +264,7 @@ describe("Navigation store", () => {
await ctx.test.navigationStore.save(update) await ctx.test.navigationStore.save(update)
expect(saveSpy).toHaveBeenCalledWith({ expect(saveSpy).toHaveBeenCalledWith("testing_123", { navigation: update })
appId: "testing_123",
metadata: { navigation: update },
})
expect(ctx.test.store.links.length).toBe(3) expect(ctx.test.store.links.length).toBe(3)

View File

@ -20,10 +20,7 @@ export const createThemeStore = () => {
} }
const save = async (theme, appId) => { const save = async (theme, appId) => {
const app = await API.saveAppMetadata({ const app = await API.saveAppMetadata(appId, { theme })
appId,
metadata: { theme },
})
store.update(state => { store.update(state => {
state.theme = app.theme state.theme = app.theme
return state return state
@ -32,10 +29,7 @@ export const createThemeStore = () => {
const saveCustom = async (theme, appId) => { const saveCustom = async (theme, appId) => {
const updated = { ...get(store).customTheme, ...theme } const updated = { ...get(store).customTheme, ...theme }
const app = await API.saveAppMetadata({ const app = await API.saveAppMetadata(appId, { customTheme: updated })
appId,
metadata: { customTheme: updated },
})
store.update(state => { store.update(state => {
state.customTheme = app.customTheme state.customTheme = app.customTheme
return state return state

View File

@ -128,10 +128,7 @@ export class AppsStore extends BudiStore {
} }
async save(appId, value) { async save(appId, value) {
await API.saveAppMetadata({ await API.saveAppMetadata(appId, value)
appId,
metadata: value,
})
this.update(state => { this.update(state => {
const updatedAppIndex = state.apps.findIndex( const updatedAppIndex = state.apps.findIndex(
app => app.instance._id === appId app => app.instance._id === appId

View File

@ -113,11 +113,7 @@ export function createAuthStore() {
}, },
login: async creds => { login: async creds => {
const tenantId = get(store).tenantId const tenantId = get(store).tenantId
await API.logIn({ await API.logIn(tenantId, creds.username, creds.password)
username: creds.username,
password: creds.password,
tenantId,
})
await actions.getSelf() await actions.getSelf()
}, },
logout: async () => { logout: async () => {
@ -138,18 +134,11 @@ export function createAuthStore() {
}, },
forgotPassword: async email => { forgotPassword: async email => {
const tenantId = get(store).tenantId const tenantId = get(store).tenantId
await API.requestForgotPassword({ await API.requestForgotPassword(tenantId, email)
tenantId,
email,
})
}, },
resetPassword: async (password, resetCode) => { resetPassword: async (password, resetCode) => {
const tenantId = get(store).tenantId const tenantId = get(store).tenantId
await API.resetPassword({ await API.resetPassword(tenantId, password, resetCode)
tenantId,
password,
resetCode,
})
}, },
generateAPIKey: async () => { generateAPIKey: async () => {
return API.generateAPIKey() return API.generateAPIKey()

View File

@ -11,40 +11,28 @@ export function createBackupsStore() {
}) })
} }
async function searchBackups({ async function searchBackups(appId, opts) {
appId, return API.searchBackups(appId, opts)
trigger,
type,
page,
startDate,
endDate,
}) {
return API.searchBackups({ appId, trigger, type, page, startDate, endDate })
} }
async function restoreBackup({ appId, backupId, name }) { async function restoreBackup(appId, backupId, name) {
return API.restoreBackup({ appId, backupId, name }) return API.restoreBackup(appId, backupId, name)
} }
async function deleteBackup({ appId, backupId }) { async function deleteBackup(appId, backupId) {
return API.deleteBackup({ appId, backupId }) return API.deleteBackup(appId, backupId)
} }
async function createManualBackup(appId) { async function createManualBackup(appId) {
return API.createManualBackup(appId) return API.createManualBackup(appId)
} }
async function updateBackup({ appId, backupId, name }) {
return API.updateBackup({ appId, backupId, name })
}
return { return {
createManualBackup, createManualBackup,
searchBackups, searchBackups,
selectBackup, selectBackup,
deleteBackup, deleteBackup,
restoreBackup, restoreBackup,
updateBackup,
subscribe: store.subscribe, subscribe: store.subscribe,
} }
} }

View File

@ -20,7 +20,6 @@ vi.mock("api", () => {
restoreBackup: vi.fn(() => "restoreBackupReturn"), restoreBackup: vi.fn(() => "restoreBackupReturn"),
deleteBackup: vi.fn(() => "deleteBackupReturn"), deleteBackup: vi.fn(() => "deleteBackupReturn"),
createManualBackup: vi.fn(() => "createManualBackupReturn"), createManualBackup: vi.fn(() => "createManualBackupReturn"),
updateBackup: vi.fn(() => "updateBackupReturn"),
}, },
} }
}) })
@ -61,8 +60,7 @@ describe("backups store", () => {
ctx.page = "page" ctx.page = "page"
ctx.startDate = "startDate" ctx.startDate = "startDate"
ctx.endDate = "endDate" ctx.endDate = "endDate"
ctx.value = await ctx.returnedStore.searchBackups({ ctx.value = await ctx.returnedStore.searchBackups(ctx.appId, {
appId: ctx.appId,
trigger: ctx.trigger, trigger: ctx.trigger,
type: ctx.type, type: ctx.type,
page: ctx.page, page: ctx.page,
@ -73,8 +71,7 @@ describe("backups store", () => {
it("calls and returns the API searchBackups method", ctx => { it("calls and returns the API searchBackups method", ctx => {
expect(API.searchBackups).toHaveBeenCalledTimes(1) expect(API.searchBackups).toHaveBeenCalledTimes(1)
expect(API.searchBackups).toHaveBeenCalledWith({ expect(API.searchBackups).toHaveBeenCalledWith(ctx.appId, {
appId: ctx.appId,
trigger: ctx.trigger, trigger: ctx.trigger,
type: ctx.type, type: ctx.type,
page: ctx.page, page: ctx.page,
@ -103,18 +100,12 @@ describe("backups store", () => {
beforeEach(async ctx => { beforeEach(async ctx => {
ctx.appId = "appId" ctx.appId = "appId"
ctx.backupId = "backupId" ctx.backupId = "backupId"
ctx.value = await ctx.returnedStore.deleteBackup({ ctx.value = await ctx.returnedStore.deleteBackup(ctx.appId, ctx.backupId)
appId: ctx.appId,
backupId: ctx.backupId,
})
}) })
it("calls and returns the API deleteBackup method", ctx => { it("calls and returns the API deleteBackup method", ctx => {
expect(API.deleteBackup).toHaveBeenCalledTimes(1) expect(API.deleteBackup).toHaveBeenCalledTimes(1)
expect(API.deleteBackup).toHaveBeenCalledWith({ expect(API.deleteBackup).toHaveBeenCalledWith(ctx.appId, ctx.backupId)
appId: ctx.appId,
backupId: ctx.backupId,
})
expect(ctx.value).toBe("deleteBackupReturn") expect(ctx.value).toBe("deleteBackupReturn")
}) })
}) })
@ -124,47 +115,24 @@ describe("backups store", () => {
ctx.appId = "appId" ctx.appId = "appId"
ctx.backupId = "backupId" ctx.backupId = "backupId"
ctx.$name = "name" // `name` is used by some sort of internal ctx thing and is readonly ctx.$name = "name" // `name` is used by some sort of internal ctx thing and is readonly
ctx.value = await ctx.returnedStore.restoreBackup({ ctx.value = await ctx.returnedStore.restoreBackup(
appId: ctx.appId, ctx.appId,
backupId: ctx.backupId, ctx.backupId,
name: ctx.$name, ctx.$name
}) )
}) })
it("calls and returns the API restoreBackup method", ctx => { it("calls and returns the API restoreBackup method", ctx => {
expect(API.restoreBackup).toHaveBeenCalledTimes(1) expect(API.restoreBackup).toHaveBeenCalledTimes(1)
expect(API.restoreBackup).toHaveBeenCalledWith({ expect(API.restoreBackup).toHaveBeenCalledWith(
appId: ctx.appId, ctx.appId,
backupId: ctx.backupId, ctx.backupId,
name: ctx.$name, ctx.$name
}) )
expect(ctx.value).toBe("restoreBackupReturn") 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", () => { describe("subscribe", () => {
it("calls and returns the API updateBackup method", ctx => { it("calls and returns the API updateBackup method", ctx => {
expect(ctx.returnedStore.subscribe).toBe(ctx.writableReturn.subscribe) expect(ctx.returnedStore.subscribe).toBe(ctx.writableReturn.subscribe)

View File

@ -46,10 +46,7 @@ export function createGroupsStore() {
}, },
delete: async group => { delete: async group => {
await API.deleteGroup({ await API.deleteGroup(group._id, group._rev)
id: group._id,
rev: group._rev,
})
store.update(state => { store.update(state => {
state = state.filter(state => state._id !== group._id) state = state.filter(state => state._id !== group._id)
return state return state
@ -89,11 +86,11 @@ export function createGroupsStore() {
}, },
addGroupAppBuilder: async (groupId, appId) => { addGroupAppBuilder: async (groupId, appId) => {
return await API.addGroupAppBuilder({ groupId, appId }) return await API.addGroupAppBuilder(groupId, appId)
}, },
removeGroupAppBuilder: async (groupId, appId) => { removeGroupAppBuilder: async (groupId, appId) => {
return await API.removeGroupAppBuilder({ groupId, appId }) return await API.removeGroupAppBuilder(groupId, appId)
}, },
} }

View File

@ -2,13 +2,13 @@ import { writable } from "svelte/store"
type GotoFuncType = (path: string) => void type GotoFuncType = (path: string) => void
interface Store { interface PortalNavigationStore {
initialisated: boolean initialisated: boolean
goto: GotoFuncType goto: GotoFuncType
} }
export function createNavigationStore() { export function createNavigationStore() {
const store = writable<Store>({ const store = writable<PortalNavigationStore>({
initialisated: false, initialisated: false,
goto: undefined as any, goto: undefined as any,
}) })

View File

@ -1,17 +1,13 @@
import { writable } from "svelte/store" import { writable } from "svelte/store"
import { PluginSource } from "constants/index" import { PluginSource } from "constants/index"
import { Plugin } from "@budibase/types"
import { API } from "api" import { API } from "api"
interface Plugin {
_id: string
}
export function createPluginsStore() { export function createPluginsStore() {
const { subscribe, set, update } = writable<Plugin[]>([]) const { subscribe, set, update } = writable<Plugin[]>([])
async function load() { async function load() {
const plugins = await API.getPlugins() const plugins: Plugin[] = await API.getPlugins()
set(plugins) set(plugins)
} }

View File

@ -35,7 +35,24 @@ export function createUsersStore() {
} }
async function invite(payload) { 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) { async function removeInvites(payload) {
@ -60,7 +77,7 @@ export function createUsersStore() {
} }
async function updateInvite(invite) { async function updateInvite(invite) {
return API.updateUserInvite(invite) return API.updateUserInvite(invite.code, invite)
} }
async function create(data) { async function create(data) {
@ -93,10 +110,7 @@ export function createUsersStore() {
return body return body
}) })
const response = await API.createUsers({ const response = await API.createUsers(mappedUsers, data.groups)
users: mappedUsers,
groups: data.groups,
})
// re-search from first page // re-search from first page
await search() await search()
@ -108,8 +122,8 @@ export function createUsersStore() {
update(users => users.filter(user => user._id !== id)) update(users => users.filter(user => user._id !== id))
} }
async function getUserCountByApp({ appId }) { async function getUserCountByApp(appId) {
return await API.getUserCountByApp({ appId }) return await API.getUserCountByApp(appId)
} }
async function bulkDelete(users) { async function bulkDelete(users) {
@ -121,11 +135,11 @@ export function createUsersStore() {
} }
async function addAppBuilder(userId, appId) { async function addAppBuilder(userId, appId) {
return await API.addAppBuilder({ userId, appId }) return await API.addAppBuilder(userId, appId)
} }
async function removeAppBuilder(userId, appId) { async function removeAppBuilder(userId, appId) {
return await API.removeAppBuilder({ userId, appId }) return await API.removeAppBuilder(userId, appId)
} }
async function getAccountHolder() { async function getAccountHolder() {

View File

@ -77,12 +77,11 @@ export const patchAPI = API => {
return await enrichRows(rows, tableId) return await enrichRows(rows, tableId)
} }
const searchTable = API.searchTable const searchTable = API.searchTable
API.searchTable = async params => { API.searchTable = async (sourceId, opts) => {
const tableId = params?.tableId const output = await searchTable(sourceId, opts)
const output = await searchTable(params)
return { return {
...output, ...output,
rows: await enrichRows(output?.rows, tableId), rows: await enrichRows(output.rows, sourceId),
} }
} }
const fetchViewData = API.fetchViewData const fetchViewData = API.fetchViewData

View File

@ -49,10 +49,7 @@
data.append("file", fileList[i]) data.append("file", fileList[i])
} }
try { try {
return await API.uploadAttachment({ return await API.uploadAttachment(formContext?.dataSource?.tableId, data)
data,
tableId: formContext?.dataSource?.tableId,
})
} catch (error) { } catch (error) {
return [] return []
} }

View File

@ -80,12 +80,7 @@
const upload = async () => { const upload = async () => {
loading = true loading = true
try { try {
const res = await API.externalUpload({ const res = await API.externalUpload(datasourceId, bucket, key, data)
datasourceId,
bucket,
key,
data,
})
notificationStore.actions.success("File uploaded successfully") notificationStore.actions.success("File uploaded successfully")
loading = false loading = false
return res return res

View File

@ -31,10 +31,10 @@
let attachRequest = new FormData() let attachRequest = new FormData()
attachRequest.append("file", signatureFile) attachRequest.append("file", signatureFile)
const resp = await API.uploadAttachment({ const resp = await API.uploadAttachment(
data: attachRequest, formContext?.dataSource?.tableId,
tableId: formContext?.dataSource?.tableId, attachRequest
}) )
const [signatureAttachment] = resp const [signatureAttachment] = resp
updateValue = signatureAttachment updateValue = signatureAttachment
} else { } else {

View File

@ -1,7 +1,7 @@
import { makePropSafe as safe } from "@budibase/string-templates" import { makePropSafe as safe } from "@budibase/string-templates"
import { API } from "../api/index.js" import { API } from "../api/index.js"
import { UILogicalOperator } from "@budibase/types" 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 // Map of data types to component types for search fields inside blocks
const schemaComponentMap = { const schemaComponentMap = {
@ -108,7 +108,7 @@ export const enrichFilter = (filter, columns, formId) => {
return { return {
logicalOperator: UILogicalOperator.ALL, logicalOperator: UILogicalOperator.ALL,
onEmptyFilter: OnEmptyFilter.RETURN_ALL, onEmptyFilter: Constants.OnEmptyFilter.RETURN_ALL,
groups: [ groups: [
...(filter?.groups || []), ...(filter?.groups || []),
{ {

View File

@ -147,7 +147,7 @@ const fetchRowHandler = async action => {
if (tableId && rowId) { if (tableId && rowId) {
try { try {
const row = await API.fetchRow({ tableId, rowId }) const row = await API.fetchRow(tableId, rowId)
return { row } return { row }
} catch (error) { } catch (error) {
@ -192,7 +192,7 @@ const deleteRowHandler = async action => {
return false return false
} }
const resp = await API.deleteRows({ tableId, rows: requestConfig }) const resp = await API.deleteRows(tableId, requestConfig)
if (!notificationOverride) { if (!notificationOverride) {
notificationStore.actions.success( notificationStore.actions.success(
@ -251,17 +251,14 @@ const navigationHandler = action => {
} }
const queryExecutionHandler = async action => { const queryExecutionHandler = async action => {
const { datasourceId, queryId, queryParams, notificationOverride } = const { queryId, queryParams, notificationOverride } = action.parameters
action.parameters
try { try {
const query = await API.fetchQueryDefinition(queryId) const query = await API.fetchQueryDefinition(queryId)
if (query?.datasourceId == null) { if (query?.datasourceId == null) {
notificationStore.actions.error("That query couldn't be found") notificationStore.actions.error("That query couldn't be found")
return false return false
} }
const result = await API.executeQuery({ const result = await API.executeQuery(queryId, {
datasourceId,
queryId,
parameters: queryParams, parameters: queryParams,
}) })
@ -381,10 +378,8 @@ const exportDataHandler = async action => {
if (typeof rows[0] !== "string") { if (typeof rows[0] !== "string") {
rows = rows.map(row => row._id) rows = rows.map(row => row._id)
} }
const data = await API.exportRows({ const data = await API.exportRows(tableId, type, {
tableId,
rows, rows,
format: type,
columns: columns?.map(column => column.name || column), columns: columns?.map(column => column.name || column),
delimiter, delimiter,
customHeaders, customHeaders,
@ -454,12 +449,7 @@ const downloadFileHandler = async action => {
const { type } = action.parameters const { type } = action.parameters
if (type === "attachment") { if (type === "attachment") {
const { tableId, rowId, attachmentColumn } = action.parameters const { tableId, rowId, attachmentColumn } = action.parameters
const res = await API.downloadAttachment( const res = await API.downloadAttachment(tableId, rowId, attachmentColumn)
tableId,
rowId,
attachmentColumn,
{ suppressErrors: true }
)
await downloadStream(res) await downloadStream(res)
return return
} }
@ -495,11 +485,7 @@ const downloadFileHandler = async action => {
const rowActionHandler = async action => { const rowActionHandler = async action => {
const { resourceId, rowId, rowActionId } = action.parameters const { resourceId, rowId, rowActionId } = action.parameters
await API.rowActions.trigger({ await API.rowActions.trigger(resourceId, rowActionId, rowId)
rowActionId,
sourceId: resourceId,
rowId,
})
// Refresh related datasources // Refresh related datasources
await dataSourceStore.actions.invalidateDataSource(resourceId, { await dataSourceStore.actions.invalidateDataSource(resourceId, {
invalidateRelationships: true, invalidateRelationships: true,

View File

@ -4,7 +4,7 @@
"description": "Budibase frontend core libraries used in builder and client", "description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "./src/index.ts",
"dependencies": { "dependencies": {
"@budibase/bbui": "*", "@budibase/bbui": "*",
"@budibase/shared-core": "*", "@budibase/shared-core": "*",

View File

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

View File

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

View File

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

View File

@ -0,0 +1,39 @@
import { BaseAPIClient } from "./types"
import {
AnalyticsEnabledResponse,
AnalyticsPingRequest,
AnalyticsPingResponse,
} from "@budibase/types"
export interface AnalyticsEndpoints {
getAnalyticsStatus: () => Promise<AnalyticsEnabledResponse>
analyticsPing: (
payload: Omit<AnalyticsPingRequest, "timezone">
) => Promise<AnalyticsPingResponse>
}
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<AnalyticsPingRequest, AnalyticsPingResponse>({
url: "/api/bbtel/ping",
body: {
...request,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
},
})
},
})

View File

@ -1,6 +1,72 @@
import { sdk } from "@budibase/shared-core" 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<FetchAppPackageResponse>
saveAppMetadata: (
appId: string,
metadata: UpdateAppRequest
) => Promise<UpdateAppResponse>
unpublishApp: (appId: string) => Promise<UnpublishAppResponse>
publishAppChanges: (appId: string) => Promise<PublishAppResponse>
revertAppChanges: (appId: string) => Promise<RevertAppResponse>
updateAppClientVersion: (appId: string) => Promise<UpdateAppClientResponse>
revertAppClientVersion: (appId: string) => Promise<RevertAppClientResponse>
releaseAppLock: (appId: string) => Promise<ClearDevLockResponse>
getAppDeployments: () => Promise<FetchDeploymentResponse>
createApp: (app: CreateAppRequest) => Promise<CreateAppResponse>
deleteApp: (appId: string) => Promise<DeleteAppResponse>
duplicateApp: (
appId: string,
app: DuplicateAppRequest
) => Promise<DuplicateAppResponse>
updateAppFromExport: (
appId: string,
body: ImportToUpdateAppRequest
) => Promise<ImportToUpdateAppResponse>
fetchSystemDebugInfo: () => Promise<GetDiagnosticsResponse>
syncApp: (appId: string) => Promise<SyncAppResponse>
getApps: () => Promise<FetchAppsResponse>
fetchComponentLibDefinitions: (
appId: string
) => Promise<FetchAppDefinitionResponse>
setRevertableVersion: (
appId: string,
revertableVersion: string
) => Promise<SetRevertableAppVersionResponse>
addSampleData: (appId: string) => Promise<AddAppSampleDataResponse>
// Missing request or response types
importApps: (apps: any) => Promise<any>
}
export const buildAppEndpoints = (API: BaseAPIClient): AppEndpoints => ({
/** /**
* Fetches screen definition for an app. * Fetches screen definition for an app.
* @param appId the ID of the app to fetch from * @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 appId the ID of the app to update
* @param metadata the app metadata to save * @param metadata the app metadata to save
*/ */
saveAppMetadata: async ({ appId, metadata }) => { saveAppMetadata: async (appId, metadata) => {
return await API.put({ return await API.put({
url: `/api/applications/${appId}`, url: `/api/applications/${appId}`,
body: metadata, body: metadata,
@ -87,7 +153,7 @@ export const buildAppEndpoints = API => ({
* Duplicate an existing app * Duplicate an existing app
* @param app the app to dupe * @param app the app to dupe
*/ */
duplicateApp: async (app, appId) => { duplicateApp: async (appId, app) => {
return await API.post({ return await API.post({
url: `/api/applications/${appId}/duplicate`, url: `/api/applications/${appId}/duplicate`,
body: app, body: app,
@ -184,7 +250,7 @@ export const buildAppEndpoints = API => ({
/** /**
* Fetches the definitions for component library components. This includes * Fetches the definitions for component library components. This includes
* their props and other metadata from components.json. * 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 => { fetchComponentLibDefinitions: async appId => {
return await API.get({ 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 => { addSampleData: async appId => {
return await API.post({ return await API.post({
url: `/api/applications/${appId}/sample`, 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) => { setRevertableVersion: async (appId, revertableVersion) => {
return await API.post({ return await API.post<
SetRevertableAppVersionRequest,
SetRevertableAppVersionResponse
>({
url: `/api/applications/${appId}/setRevertableVersion`, url: `/api/applications/${appId}/setRevertableVersion`,
body: { body: {
revertableVersion, revertableVersion,

View File

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

View File

@ -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<DownloadAttachmentResponse>
getSignedDatasourceURL: (
datasourceId: string,
bucket: string,
key: string
) => Promise<GetSignedUploadUrlResponse>
uploadAttachment: (
tableId: string,
data: any
) => Promise<ProcessAttachmentResponse>
uploadBuilderAttachment: (data: any) => Promise<ProcessAttachmentResponse>
externalUpload: (
datasourceId: string,
bucket: string,
key: string,
data: any
) => Promise<{ publicUrl: string | undefined }>
}
export const buildAttachmentEndpoints = (
API: BaseAPIClient
): AttachmentEndpoints => {
const endpoints: Pick<AttachmentEndpoints, "getSignedDatasourceURL"> = {
/**
* 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,
})
},
}
}

View File

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

View File

@ -0,0 +1,35 @@
import {
SearchAuditLogsRequest,
SearchAuditLogsResponse,
DefinitionsAuditLogsResponse,
DownloadAuditLogsRequest,
} from "@budibase/types"
import { BaseAPIClient } from "./types"
export interface AuditLogEndpoints {
searchAuditLogs: (
opts: SearchAuditLogsRequest
) => Promise<SearchAuditLogsResponse>
getEventDefinitions: () => Promise<DefinitionsAuditLogsResponse>
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}`
},
})

View File

@ -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<LoginResponse>
logOut: () => Promise<LogoutResponse>
requestForgotPassword: (
tenantId: string,
email: string
) => Promise<PasswordResetResponse>
resetPassword: (
tenantId: string,
password: string,
resetCode: string
) => Promise<PasswordResetUpdateResponse>
setInitInfo: (info: SetInitInfoRequest) => Promise<SetInitInfoResponse>
getInitInfo: () => Promise<GetInitInfoResponse>
}
export const buildAuthEndpoints = (API: BaseAPIClient): AuthEndpoints => ({
/** /**
* Performs a login request. * Performs a login request.
* @param tenantId the ID of the tenant to log in to * @param tenantId the ID of the tenant to log in to
* @param username the username (email) * @param username the username (email)
* @param password the password * @param password the password
*/ */
logIn: async ({ tenantId, username, password }) => { logIn: async (tenantId, username, password) => {
return await API.post({ return await API.post<LoginRequest, LoginResponse>({
url: `/api/global/auth/${tenantId}/login`, url: `/api/global/auth/${tenantId}/login`,
body: { body: {
username, username,
@ -49,8 +83,8 @@ export const buildAuthEndpoints = API => ({
* @param tenantId the ID of the tenant the user is in * @param tenantId the ID of the tenant the user is in
* @param email the email address of the user * @param email the email address of the user
*/ */
requestForgotPassword: async ({ tenantId, email }) => { requestForgotPassword: async (tenantId, email) => {
return await API.post({ return await API.post<PasswordResetRequest, PasswordResetResponse>({
url: `/api/global/auth/${tenantId}/reset`, url: `/api/global/auth/${tenantId}/reset`,
body: { body: {
email, email,
@ -64,8 +98,11 @@ export const buildAuthEndpoints = API => ({
* @param password the new password to set * @param password the new password to set
* @param resetCode the reset code to authenticate the request * @param resetCode the reset code to authenticate the request
*/ */
resetPassword: async ({ tenantId, password, resetCode }) => { resetPassword: async (tenantId, password, resetCode) => {
return await API.post({ return await API.post<
PasswordResetUpdateRequest,
PasswordResetUpdateResponse
>({
url: `/api/global/auth/${tenantId}/reset/update`, url: `/api/global/auth/${tenantId}/reset/update`,
body: { body: {
password, password,

View File

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

View File

@ -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<FetchAutomationResponse>
createAutomation: (
automation: CreateAutomationRequest
) => Promise<CreateAutomationResponse>
updateAutomation: (
automation: UpdateAutomationRequest
) => Promise<UpdateAutomationResponse>
deleteAutomation: (
automationId: string,
automationRev: string
) => Promise<DeleteAutomationResponse>
clearAutomationLogErrors: (
automationId: string,
appId: string
) => Promise<ClearAutomationLogResponse>
triggerAutomation: (
automationId: string,
fields: Record<string, any>,
timeout: number
) => Promise<TriggerAutomationResponse>
testAutomation: (
automationdId: string,
data: TestAutomationRequest
) => Promise<TestAutomationResponse>
getAutomationDefinitions: () => Promise<GetAutomationStepDefinitionsResponse>
getAutomationLogs: (
options: SearchAutomationLogsRequest
) => Promise<SearchAutomationLogsResponse>
}
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<TriggerAutomationRequest, TriggerAutomationResponse>({
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,
},
})
},
})

View File

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

View File

@ -0,0 +1,50 @@
import {
CreateAppBackupResponse,
ImportAppBackupResponse,
SearchAppBackupsRequest,
} from "@budibase/types"
import { BaseAPIClient } from "./types"
export interface BackupEndpoints {
createManualBackup: (appId: string) => Promise<CreateAppBackupResponse>
restoreBackup: (
appId: string,
backupId: string,
name?: string
) => Promise<ImportAppBackupResponse>
// Missing request or response types
searchBackups: (appId: string, opts: SearchAppBackupsRequest) => Promise<any>
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 },
})
},
})

View File

@ -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<FindConfigResponse>
getTenantConfig: (tentantId: string) => Promise<GetPublicSettingsResponse>
getOIDCConfig: (tenantId: string) => Promise<GetPublicOIDCConfigResponse>
getOIDCLogos: () => Promise<Config<OIDCLogosConfig>>
saveConfig: (config: SaveConfigRequest) => Promise<SaveConfigResponse>
deleteConfig: (id: string, rev: string) => Promise<DeleteConfigResponse>
getChecklist: (tenantId: string) => Promise<ConfigChecklistResponse>
uploadLogo: (data: any) => Promise<UploadConfigFileResponse>
uploadFavicon: (data: any) => Promise<UploadConfigFileResponse>
uploadOIDCLogo: (name: string, data: any) => Promise<UploadConfigFileResponse>
}
export const buildConfigEndpoints = (API: BaseAPIClient): ConfigEndpoints => ({
/** /**
* Saves a global config. * Saves a global config.
* @param config the config to save * @param config the config to save
@ -25,7 +53,7 @@ export const buildConfigEndpoints = API => ({
* @param id the id of the config to delete * @param id the id of the config to delete
* @param rev the revision 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({ return await API.delete({
url: `/api/global/configs/${id}/${rev}`, url: `/api/global/configs/${id}/${rev}`,
}) })
@ -90,7 +118,7 @@ export const buildConfigEndpoints = API => ({
* @param name the name of the OIDC provider * @param name the name of the OIDC provider
* @param data the logo form data to upload * @param data the logo form data to upload
*/ */
uploadOIDCLogo: async ({ name, data }) => { uploadOIDCLogo: async (name, data) => {
return await API.post({ return await API.post({
url: `/api/global/configs/upload/logos_oidc/${name}`, url: `/api/global/configs/upload/logos_oidc/${name}`,
body: data, body: data,

View File

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

View File

@ -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<Datasource[]>
buildDatasourceSchema: (
datasourceId: string,
tablesFilter?: string[]
) => Promise<BuildSchemaFromSourceResponse>
createDatasource: (
data: CreateDatasourceRequest
) => Promise<CreateDatasourceResponse>
updateDatasource: (
datasource: Datasource
) => Promise<UpdateDatasourceResponse>
deleteDatasource: (
id: string,
rev: string
) => Promise<DeleteDatasourceResponse>
validateDatasource: (
datasource: Datasource
) => Promise<VerifyDatasourceResponse>
fetchInfoForDatasource: (
datasource: Datasource
) => Promise<FetchDatasourceInfoResponse>
fetchExternalSchema: (
datasourceId: string
) => Promise<FetchExternalSchemaResponse>
}
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<UpdateDatasourceRequest, UpdateDatasourceResponse>({
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<VerifyDatasourceRequest, VerifyDatasourceResponse>({
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`,
})
},
})

View File

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

View File

@ -0,0 +1,58 @@
import {
CreateEnvironmentVariableRequest,
CreateEnvironmentVariableResponse,
DeleteEnvironmentVariablesResponse,
GetEnvironmentVariablesResponse,
StatusEnvironmentVariableResponse,
UpdateEnvironmentVariableRequest,
UpdateEnvironmentVariableResponse,
} from "@budibase/types"
import { BaseAPIClient } from "./types"
export interface EnvironmentVariableEndpoints {
checkEnvironmentVariableStatus: () => Promise<StatusEnvironmentVariableResponse>
fetchEnvironmentVariables: () => Promise<GetEnvironmentVariablesResponse>
createEnvironmentVariable: (
data: CreateEnvironmentVariableRequest
) => Promise<CreateEnvironmentVariableResponse>
deleteEnvironmentVariable: (
name: string
) => Promise<DeleteEnvironmentVariablesResponse>
updateEnvironmentVariable: (
name: string,
data: UpdateEnvironmentVariableRequest
) => Promise<UpdateEnvironmentVariableResponse>
}
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,
})
},
})

View File

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

View File

@ -0,0 +1,21 @@
import {
EventPublishType,
PostEventPublishRequest,
PostEventPublishResponse,
} from "@budibase/types"
import { BaseAPIClient } from "./types"
export interface EventEndpoints {
publishEvent: (type: EventPublishType) => Promise<PostEventPublishResponse>
}
export const buildEventEndpoints = (API: BaseAPIClient): EventEndpoints => ({
publishEvent: async type => {
return await API.post<PostEventPublishRequest, PostEventPublishResponse>({
url: `/api/global/event/publish`,
body: {
type,
},
})
},
})

View File

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

View File

@ -0,0 +1,48 @@
import {
GetUserFlagsResponse,
SetUserFlagRequest,
SetUserFlagResponse,
ToggleBetaFeatureResponse,
} from "@budibase/types"
import { BaseAPIClient } from "./types"
export interface FlagEndpoints {
getFlags: () => Promise<GetUserFlagsResponse>
updateFlag: (flag: string, value: any) => Promise<SetUserFlagResponse>
toggleUiFeature: (value: string) => Promise<ToggleBetaFeatureResponse>
}
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<SetUserFlagRequest, SetUserFlagResponse>({
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}`,
})
},
})

View File

@ -1,13 +1,50 @@
export const buildGroupsEndpoints = API => { import { SearchUserGroupResponse, UserGroup } from "@budibase/types"
// underlying functionality of adding/removing users/apps to groups import { BaseAPIClient } from "./types"
async function updateGroupResource(groupId, resource, operation, ids) {
if (!Array.isArray(ids)) { export interface GroupEndpoints {
ids = [ids] saveGroup: (group: UserGroup) => Promise<{ _id: string; _rev: string }>
} getGroups: () => Promise<UserGroup[]>
return await API.post({ getGroup: (id: string) => Promise<UserGroup>
deleteGroup: (id: string, rev: string) => Promise<{ message: string }>
getGroupUsers: (
data: GetGroupUsersRequest
) => Promise<SearchUserGroupResponse>
addUsersToGroup: (groupId: string, userIds: string[]) => Promise<void>
removeUsersFromGroup: (groupId: string, userIds: string[]) => Promise<void>
addAppsToGroup: (groupId: string, appArray: object[]) => Promise<void>
removeAppsFromGroup: (groupId: string, appArray: object[]) => Promise<void>
addGroupAppBuilder: (groupId: string, appId: string) => Promise<void>
removeGroupAppBuilder: (groupId: string, appId: string) => Promise<void>
}
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}`, url: `/api/global/groups/${groupId}/${resource}`,
body: { body: {
[operation]: ids, [operation]: resources,
}, },
}) })
} }
@ -46,7 +83,7 @@ export const buildGroupsEndpoints = API => {
* @param id the id of the config to delete * @param id the id of the config to delete
* @param rev the revision 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({ return await API.delete({
url: `/api/global/groups/${id}/${rev}`, url: `/api/global/groups/${id}/${rev}`,
}) })
@ -61,9 +98,8 @@ export const buildGroupsEndpoints = API => {
url += `bookmark=${bookmark}&` url += `bookmark=${bookmark}&`
} }
if (emailSearch) { if (emailSearch) {
url += `emailSearch=${emailSearch}&` url += `emailSearch=${emailSearch}`
} }
return await API.get({ return await API.get({
url, url,
}) })
@ -75,7 +111,12 @@ export const buildGroupsEndpoints = API => {
* @param userIds The user IDs to be added * @param userIds The user IDs to be added
*/ */
addUsersToGroup: async (groupId, userIds) => { 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 * @param userIds The user IDs to be removed
*/ */
removeUsersFromGroup: async (groupId, userIds) => { 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 * @param appArray Array of objects, containing the appId and roleId to be added
*/ */
addAppsToGroup: async (groupId, appArray) => { 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 * @param appArray Array of objects, containing the appId to be removed
*/ */
removeAppsFromGroup: async (groupId, appArray) => { 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 groupId The group to update
* @param appId The app id where the builder will be added * @param appId The app id where the builder will be added
*/ */
addGroupAppBuilder: async ({ groupId, appId }) => { addGroupAppBuilder: async (groupId, appId) => {
return await API.post({ return await API.post({
url: `/api/global/groups/${groupId}/app/${appId}/builder`, url: `/api/global/groups/${groupId}/app/${appId}/builder`,
}) })
@ -121,7 +177,7 @@ export const buildGroupsEndpoints = API => {
* @param groupId The group to update * @param groupId The group to update
* @param appId The app id where the builder will be removed * @param appId The app id where the builder will be removed
*/ */
removeGroupAppBuilder: async ({ groupId, appId }) => { removeGroupAppBuilder: async (groupId, appId) => {
return await API.delete({ return await API.delete({
url: `/api/global/groups/${groupId}/app/${appId}/builder`, url: `/api/global/groups/${groupId}/app/${appId}/builder`,
}) })

View File

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

View File

@ -1,3 +1,13 @@
import {
HTTPMethod,
APICallParams,
APIClientConfig,
APIClient,
APICallConfig,
BaseAPIClient,
Headers,
APIError,
} from "./types"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
import { Header } from "@budibase/shared-core" import { Header } from "@budibase/shared-core"
import { ApiVersion } from "../constants" import { ApiVersion } from "../constants"
@ -10,7 +20,6 @@ import { buildAutomationEndpoints } from "./automations"
import { buildConfigEndpoints } from "./configs" import { buildConfigEndpoints } from "./configs"
import { buildDatasourceEndpoints } from "./datasources" import { buildDatasourceEndpoints } from "./datasources"
import { buildFlagEndpoints } from "./flags" import { buildFlagEndpoints } from "./flags"
import { buildHostingEndpoints } from "./hosting"
import { buildLayoutEndpoints } from "./layouts" import { buildLayoutEndpoints } from "./layouts"
import { buildOtherEndpoints } from "./other" import { buildOtherEndpoints } from "./other"
import { buildPermissionsEndpoints } from "./permissions" import { buildPermissionsEndpoints } from "./permissions"
@ -29,10 +38,10 @@ import { buildViewV2Endpoints } from "./viewsV2"
import { buildLicensingEndpoints } from "./licensing" import { buildLicensingEndpoints } from "./licensing"
import { buildGroupsEndpoints } from "./groups" import { buildGroupsEndpoints } from "./groups"
import { buildPluginEndpoints } from "./plugins" import { buildPluginEndpoints } from "./plugins"
import { buildBackupsEndpoints } from "./backups" import { buildBackupEndpoints } from "./backups"
import { buildEnvironmentVariableEndpoints } from "./environmentVariables" import { buildEnvironmentVariableEndpoints } from "./environmentVariables"
import { buildEventEndpoints } from "./events" import { buildEventEndpoints } from "./events"
import { buildAuditLogsEndpoints } from "./auditLogs" import { buildAuditLogEndpoints } from "./auditLogs"
import { buildLogsEndpoints } from "./logs" import { buildLogsEndpoints } from "./logs"
import { buildMigrationEndpoints } from "./migrations" import { buildMigrationEndpoints } from "./migrations"
import { buildRowActionEndpoints } from "./rowActions" import { buildRowActionEndpoints } from "./rowActions"
@ -45,55 +54,21 @@ import { buildRowActionEndpoints } from "./rowActions"
*/ */
export const APISessionID = Helpers.uuid() 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. * Constructs an API client with the provided configuration.
* @param config the API client configuration
* @return {object} the API client
*/ */
export const createAPIClient = config => { export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
config = { let cache: Record<string, any> = {}
...defaultAPIClientConfig,
...config,
}
let cache = {}
// Generates an error object from an API response // Generates an error object from an API response
const makeErrorFromResponse = async ( const makeErrorFromResponse = async (
response, response: Response,
method, method: HTTPMethod,
suppressErrors = false suppressErrors = false
) => { ): Promise<APIError> => {
// Try to read a message from the error // Try to read a message from the error
let message = response.statusText let message = response.statusText
let json = null let json: any = null
try { try {
json = await response.json() json = await response.json()
if (json?.message) { if (json?.message) {
@ -116,32 +91,34 @@ export const createAPIClient = config => {
} }
// Generates an error object from a string // Generates an error object from a string
const makeError = (message, request) => { const makeError = (
message: string,
url?: string,
method?: HTTPMethod
): APIError => {
return { return {
message, message,
json: null, json: null,
status: 400, status: 400,
url: request?.url, url: url,
method: request?.method, method: method,
handled: true, handled: true,
suppressErrors: false,
} }
} }
// Performs an API call to the server. // Performs an API call to the server.
const makeApiCall = async ({ const makeApiCall = async <RequestT = null, ResponseT = void>(
method, callConfig: APICallConfig<RequestT, ResponseT>
url, ): Promise<ResponseT> => {
body, let { json, method, external, body, url, parseResponse, suppressErrors } =
json = true, callConfig
external = false,
parseResponse,
suppressErrors = false,
}) => {
// Ensure we don't do JSON processing if sending a GET request // Ensure we don't do JSON processing if sending a GET request
json = json && method !== "GET" json = json && method !== HTTPMethod.GET
// Build headers // Build headers
let headers = { Accept: "application/json" } let headers: Headers = { Accept: "application/json" }
headers[Header.SESSION_ID] = APISessionID headers[Header.SESSION_ID] = APISessionID
if (!external) { if (!external) {
headers[Header.API_VER] = ApiVersion headers[Header.API_VER] = ApiVersion
@ -154,17 +131,17 @@ export const createAPIClient = config => {
} }
// Build request body // Build request body
let requestBody = body let requestBody: any = body
if (json) { if (json) {
try { try {
requestBody = JSON.stringify(body) requestBody = JSON.stringify(body)
} catch (error) { } catch (error) {
throw makeError("Invalid JSON body", { url, method }) throw makeError("Invalid JSON body", url, method)
} }
} }
// Make request // Make request
let response let response: Response
try { try {
response = await fetch(url, { response = await fetch(url, {
method, method,
@ -174,21 +151,23 @@ export const createAPIClient = config => {
}) })
} catch (error) { } catch (error) {
delete cache[url] delete cache[url]
throw makeError("Failed to send request", { url, method }) throw makeError("Failed to send request", url, method)
} }
// Handle response // Handle response
if (response.status >= 200 && response.status < 400) { if (response.status >= 200 && response.status < 400) {
handleMigrations(response) handleMigrations(response)
try { try {
if (parseResponse) { if (response.status === 204) {
return undefined as ResponseT
} else if (parseResponse) {
return await parseResponse(response) return await parseResponse(response)
} else { } else {
return await response.json() return (await response.json()) as ResponseT
} }
} catch (error) { } catch (error) {
delete cache[url] delete cache[url]
return null throw `Failed to parse response: ${error}`
} }
} else { } else {
delete cache[url] delete cache[url]
@ -196,7 +175,7 @@ export const createAPIClient = config => {
} }
} }
const handleMigrations = response => { const handleMigrations = (response: Response) => {
if (!config.onMigrationDetected) { if (!config.onMigrationDetected) {
return return
} }
@ -210,48 +189,57 @@ export const createAPIClient = config => {
// Performs an API call to the server and caches the response. // Performs an API call to the server and caches the response.
// Future invocation for this URL will return the cached result instead of // Future invocation for this URL will return the cached result instead of
// hitting the server again. // hitting the server again.
const makeCachedApiCall = async params => { const makeCachedApiCall = async <RequestT = null, ResponseT = void>(
const identifier = params.url callConfig: APICallConfig<RequestT, ResponseT>
if (!identifier) { ): Promise<ResponseT> => {
return null const identifier = callConfig.url
}
if (!cache[identifier]) { if (!cache[identifier]) {
cache[identifier] = makeApiCall(params) cache[identifier] = makeApiCall(callConfig)
cache[identifier] = await cache[identifier] 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 // Constructs an API call function for a particular HTTP method
const requestApiCall = method => async params => { const requestApiCall =
try { (method: HTTPMethod) =>
let { url, cache = false, external = false } = params async <RequestT = null, ResponseT = void>(
if (!external) { params: APICallParams<RequestT, ResponseT>
url = `/${url}`.replace("//", "/") ): Promise<ResponseT> => {
} try {
let callConfig: APICallConfig<RequestT, ResponseT> = {
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 // Cache the request if possible and desired
const cacheRequest = cache && config?.enableCaching const cacheRequest = cache && config?.enableCaching
const handler = cacheRequest ? makeCachedApiCall : makeApiCall const handler = cacheRequest ? makeCachedApiCall : makeApiCall
return await handler(callConfig)
const enrichedParams = { ...params, method, url } } catch (error) {
return await handler(enrichedParams) if (config?.onError) {
} catch (error) { config.onError(error)
if (config?.onError) { }
config.onError(error) throw error
} }
throw error
} }
}
// Build the underlying core API methods // Build the underlying core API methods
let API = { let API: BaseAPIClient = {
post: requestApiCall("POST"), post: requestApiCall(HTTPMethod.POST),
get: requestApiCall("GET"), get: requestApiCall(HTTPMethod.GET),
patch: requestApiCall("PATCH"), patch: requestApiCall(HTTPMethod.PATCH),
delete: requestApiCall("DELETE"), delete: requestApiCall(HTTPMethod.DELETE),
put: requestApiCall("PUT"), put: requestApiCall(HTTPMethod.PUT),
error: message => { error: (message: string) => {
throw makeError(message) throw makeError(message)
}, },
invalidateCache: () => { invalidateCache: () => {
@ -260,9 +248,9 @@ export const createAPIClient = config => {
// Generic utility to extract the current app ID. Assumes that any client // 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. // that exists in an app context will be attaching our app ID header.
getAppID: () => { getAppID: (): string => {
let headers = {} let headers: Headers = {}
config?.attachHeaders(headers) config?.attachHeaders?.(headers)
return headers?.[Header.APP_ID] return headers?.[Header.APP_ID]
}, },
} }
@ -279,7 +267,6 @@ export const createAPIClient = config => {
...buildConfigEndpoints(API), ...buildConfigEndpoints(API),
...buildDatasourceEndpoints(API), ...buildDatasourceEndpoints(API),
...buildFlagEndpoints(API), ...buildFlagEndpoints(API),
...buildHostingEndpoints(API),
...buildLayoutEndpoints(API), ...buildLayoutEndpoints(API),
...buildOtherEndpoints(API), ...buildOtherEndpoints(API),
...buildPermissionsEndpoints(API), ...buildPermissionsEndpoints(API),
@ -297,10 +284,10 @@ export const createAPIClient = config => {
...buildLicensingEndpoints(API), ...buildLicensingEndpoints(API),
...buildGroupsEndpoints(API), ...buildGroupsEndpoints(API),
...buildPluginEndpoints(API), ...buildPluginEndpoints(API),
...buildBackupsEndpoints(API), ...buildBackupEndpoints(API),
...buildEnvironmentVariableEndpoints(API), ...buildEnvironmentVariableEndpoints(API),
...buildEventEndpoints(API), ...buildEventEndpoints(API),
...buildAuditLogsEndpoints(API), ...buildAuditLogEndpoints(API),
...buildLogsEndpoints(API), ...buildLogsEndpoints(API),
...buildMigrationEndpoints(API), ...buildMigrationEndpoints(API),
viewV2: buildViewV2Endpoints(API), viewV2: buildViewV2Endpoints(API),

View File

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

View File

@ -0,0 +1,35 @@
import {
DeleteLayoutResponse,
SaveLayoutRequest,
SaveLayoutResponse,
} from "@budibase/types"
import { BaseAPIClient } from "./types"
export interface LayoutEndpoints {
saveLayout: (layout: SaveLayoutRequest) => Promise<SaveLayoutResponse>
deleteLayout: (id: string, rev: string) => Promise<DeleteLayoutResponse>
}
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}`,
})
},
})

View File

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

View File

@ -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<ActivateLicenseKeyResponse>
deleteLicenseKey: () => Promise<void>
getLicenseKey: () => Promise<GetLicenseKeyResponse | void>
activateOfflineLicense: (
offlineLicenseToken: string
) => Promise<ActivateOfflineLicenseTokenResponse>
deleteOfflineLicense: () => Promise<void>
getOfflineLicense: () => Promise<GetOfflineLicenseTokenResponse | void>
getOfflineLicenseIdentifier: () => Promise<GetOfflineIdentifierResponse>
refreshLicense: () => Promise<RefreshOfflineLicenseResponse>
getQuotaUsage: () => Promise<QuotaUsage>
}
export const buildLicensingEndpoints = (
API: BaseAPIClient
): LicensingEndpoints => ({
// LICENSE KEY
activateLicenseKey: async licenseKey => {
return API.post<ActivateLicenseKeyRequest, ActivateLicenseKeyResponse>({
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",
})
},
})

View File

@ -1,4 +1,10 @@
export const buildLogsEndpoints = API => ({ import { BaseAPIClient } from "./types"
export interface LogEndpoints {
getSystemLogs: () => Promise<any>
}
export const buildLogsEndpoints = (API: BaseAPIClient): LogEndpoints => ({
/** /**
* Gets a stream for the system logs. * Gets a stream for the system logs.
*/ */

View File

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

View File

@ -0,0 +1,19 @@
import { GetOldMigrationStatus } from "@budibase/types"
import { BaseAPIClient } from "./types"
export interface MigrationEndpoints {
getMigrationStatus: () => Promise<GetOldMigrationStatus>
}
export const buildMigrationEndpoints = (
API: BaseAPIClient
): MigrationEndpoints => ({
/**
* Gets the info about the current app migration
*/
getMigrationStatus: async () => {
return await API.get({
url: "/api/migrations/status",
})
},
})

View File

@ -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<SystemStatusResponse>
getBudibaseVersion: () => Promise<string>
getIntegrations: () => Promise<FetchIntegrationsResponse>
getBasePermissions: () => Promise<FetchBuiltinPermissionsResponse>
getEnvironment: () => Promise<GetEnvironmentResponse>
}
export const buildOtherEndpoints = (API: BaseAPIClient): OtherEndpoints => ({
/** /**
* Gets the current environment details. * Gets the current environment details.
*/ */
@ -31,7 +48,7 @@ export const buildOtherEndpoints = API => ({
*/ */
getBudibaseVersion: async () => { getBudibaseVersion: async () => {
return ( return (
await API.get({ await API.get<GetVersionResponse>({
url: "/api/dev/version", url: "/api/dev/version",
}) })
).version ).version
@ -45,14 +62,4 @@ export const buildOtherEndpoints = API => ({
url: "/api/permission/builtin", 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,
})
},
}) })

View File

@ -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<GetResourcePermsResponse>
updatePermissionForResource: (
resourceId: string,
roleId: string,
level: PermissionLevel
) => Promise<AddPermissionResponse>
removePermissionFromResource: (
resourceId: string,
roleId: string,
level: PermissionLevel
) => Promise<RemovePermissionResponse>
getDependants: (resourceId: string) => Promise<GetDependantResourcesResponse>
}
export const buildPermissionsEndpoints = (
API: BaseAPIClient
): PermissionEndpoints => ({
/** /**
* Gets the permission required to access a specific resource * Gets the permission required to access a specific resource
* @param resourceId the resource ID to check * @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 resourceId the ID of the resource to update
* @param roleId the ID of the role to update the permissions of * @param roleId the ID of the role to update the permissions of
* @param level the level to assign the role for this resource * @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({ return await API.post({
url: `/api/permission/${roleId}/${resourceId}/${level}`, url: `/api/permission/${roleId}/${resourceId}/${level}`,
}) })
@ -27,9 +54,8 @@ export const buildPermissionsEndpoints = API => ({
* @param resourceId the ID of the resource to update * @param resourceId the ID of the resource to update
* @param roleId the ID of the role to update the permissions of * @param roleId the ID of the role to update the permissions of
* @param level the level to remove the role for this resource * @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({ return await API.delete({
url: `/api/permission/${roleId}/${resourceId}/${level}`, url: `/api/permission/${roleId}/${resourceId}/${level}`,
}) })

View File

@ -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<UploadPluginResponse>
createPlugin: (data: CreatePluginRequest) => Promise<CreatePluginResponse>
getPlugins: () => Promise<FetchPluginResponse>
deletePlugin: (pluginId: string) => Promise<DeletePluginResponse>
}
export const buildPluginEndpoints = (API: BaseAPIClient): PluginEndpoins => ({
/** /**
* Uploads a plugin tarball bundle * Uploads a plugin tarball bundle
* @param data the plugin tarball bundle to upload * @param data the plugin tarball bundle to upload

View File

@ -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<ExecuteV2QueryResponse>
fetchQueryDefinition: (queryId: string) => Promise<FindQueryResponse>
getQueries: () => Promise<FetchQueriesResponse>
saveQuery: (query: SaveQueryRequest) => Promise<SaveQueryResponse>
deleteQuery: (id: string, rev: string) => Promise<DeleteQueryResponse>
previewQuery: (query: PreviewQueryRequest) => Promise<PreviewQueryResponse>
importQueries: (
data: ImportRestQueryRequest
) => Promise<ImportRestQueryResponse>
}
export const buildQueryEndpoints = (API: BaseAPIClient): QueryEndpoints => ({
/** /**
* Executes a query against an external data connector. * Executes a query against an external data connector.
* @param queryId the ID of the query to execute * @param queryId the ID of the query to execute
* @param pagination pagination info for the query * @param pagination pagination info for the query
* @param parameters parameters for the query * @param parameters parameters for the query
*/ */
executeQuery: async ({ queryId, pagination, parameters }) => { executeQuery: async (queryId, { pagination, parameters } = {}) => {
return await API.post({ return await API.post<ExecuteQueryRequest, ExecuteV2QueryResponse>({
url: `/api/v2/queries/${queryId}`, url: `/api/v2/queries/${queryId}`,
body: { body: {
parameters, parameters,
@ -48,27 +78,22 @@ export const buildQueryEndpoints = API => ({
/** /**
* Deletes a query * Deletes a query
* @param queryId the ID of the query to delete * @param id the ID of the query to delete
* @param queryRev the rev 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({ return await API.delete({
url: `/api/queries/${queryId}/${queryRev}`, url: `/api/queries/${id}/${rev}`,
}) })
}, },
/** /**
* Imports a set of queries into a certain datasource * 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({ return await API.post({
url: "/api/queries/import", url: "/api/queries/import",
body: { body: data,
datasourceId,
data,
},
}) })
}, },

View File

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

View File

@ -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<Row[]>
}
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<FetchEnrichedRowResponse>({
url: `/api/${sourceId}/${rowId}/enrich?field=${fieldName}`,
})
if (!fieldName) {
return [response]
} else {
return response[fieldName] || []
}
},
})

View File

@ -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<DeleteRoleResponse>
saveRole: (role: SaveRoleRequest) => Promise<SaveRoleResponse>
getRoles: () => Promise<FetchRolesResponse>
getRolesForApp: (appId: string) => Promise<any>
getAccessibleRoles: () => Promise<AccessibleRolesResponse>
}
export const buildRoleEndpoints = (API: BaseAPIClient): RoleEndpoints => ({
/** /**
* Deletes a role. * Deletes a role.
* @param roleId the ID of the role to delete * @param id the ID of the role to delete
* @param roleRev the rev 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({ return await API.delete({
url: `/api/roles/${roleId}/${roleRev}`, url: `/api/roles/${id}/${rev}`,
}) })
}, },

View File

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

View File

@ -0,0 +1,30 @@
import {
FetchClientScreenRoutingResponse,
FetchScreenRoutingResponse,
} from "@budibase/types"
import { BaseAPIClient } from "./types"
export interface RouteEndpoints {
fetchClientAppRoutes: () => Promise<FetchClientScreenRoutingResponse>
fetchAppRoutes: () => Promise<FetchScreenRoutingResponse>
}
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",
})
},
})

View File

@ -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<Record<string, RowActionResponse>>
create: (tableId: string, name: string) => Promise<RowActionResponse>
delete: (tableId: string, rowActionId: string) => Promise<void>
enableView: (
tableId: string,
rowActionId: string,
viewId: string
) => Promise<RowActionPermissionsResponse>
disableView: (
tableId: string,
rowActionId: string,
viewId: string
) => Promise<RowActionPermissionsResponse>
trigger: (
sourceId: string,
rowActionId: string,
rowId: string
) => Promise<void>
}
export const buildRowActionEndpoints = (
API: BaseAPIClient
): RowActionEndpoints => ({
/** /**
* Gets the available row actions for a table. * Gets the available row actions for a table.
* @param tableId the ID of the table * @param tableId the ID of the table
*/ */
fetch: async tableId => { fetch: async tableId => {
const res = await API.get({ return (
url: `/api/tables/${tableId}/actions`, await API.get<RowActionsResponse>({
}) url: `/api/tables/${tableId}/actions`,
return res?.actions || {} })
).actions
}, },
/** /**
@ -15,8 +48,8 @@ export const buildRowActionEndpoints = API => ({
* @param name the name of the row action * @param name the name of the row action
* @param tableId the ID of the table * @param tableId the ID of the table
*/ */
create: async ({ name, tableId }) => { create: async (tableId, name) => {
return await API.post({ return await API.post<CreateRowActionRequest, RowActionResponse>({
url: `/api/tables/${tableId}/actions`, url: `/api/tables/${tableId}/actions`,
body: { body: {
name, 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. * Deletes a row action.
* @param tableId the ID of the table * @param tableId the ID of the table
* @param rowActionId the ID of the row action to delete * @param rowActionId the ID of the row action to delete
*/ */
delete: async ({ tableId, rowActionId }) => { delete: async (tableId, rowActionId) => {
return await API.delete({ return await API.delete({
url: `/api/tables/${tableId}/actions/${rowActionId}`, url: `/api/tables/${tableId}/actions/${rowActionId}`,
}) })
@ -56,7 +74,7 @@ export const buildRowActionEndpoints = API => ({
* @param rowActionId the ID of the row action * @param rowActionId the ID of the row action
* @param viewId the ID of the view * @param viewId the ID of the view
*/ */
enableView: async ({ tableId, rowActionId, viewId }) => { enableView: async (tableId, rowActionId, viewId) => {
return await API.post({ return await API.post({
url: `/api/tables/${tableId}/actions/${rowActionId}/permissions/${viewId}`, 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 rowActionId the ID of the row action
* @param viewId the ID of the view * @param viewId the ID of the view
*/ */
disableView: async ({ tableId, rowActionId, viewId }) => { disableView: async (tableId, rowActionId, viewId) => {
return await API.delete({ return await API.delete({
url: `/api/tables/${tableId}/actions/${rowActionId}/permissions/${viewId}`, url: `/api/tables/${tableId}/actions/${rowActionId}/permissions/${viewId}`,
}) })
@ -79,8 +97,8 @@ export const buildRowActionEndpoints = API => ({
* @param tableId the ID of the table * @param tableId the ID of the table
* @param rowActionId the ID of the row action to trigger * @param rowActionId the ID of the row action to trigger
*/ */
trigger: async ({ sourceId, rowActionId, rowId }) => { trigger: async (sourceId, rowActionId, rowId) => {
return await API.post({ return await API.post<RowActionTriggerRequest>({
url: `/api/tables/${sourceId}/actions/${rowActionId}/trigger`, url: `/api/tables/${sourceId}/actions/${rowActionId}/trigger`,
body: { body: {
rowId, rowId,

View File

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

View File

@ -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<FindRowResponse>
saveRow: (
row: SaveRowRequest,
suppressErrors?: boolean
) => Promise<SaveRowResponse>
patchRow: (
row: PatchRowRequest,
suppressErrors?: boolean
) => Promise<PatchRowResponse>
deleteRow: (sourceId: string, id: string) => Promise<void>
deleteRows: (sourceId: string, rows: (Row | string)[]) => Promise<void>
exportRows: (
tableId: string,
format: string,
data: ExportRowsRequest
) => Promise<string>
}
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<DeleteRowRequest>({
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<DeleteRowRequest>({
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()
},
})
},
})

View File

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

View File

@ -0,0 +1,35 @@
import {
DeleteScreenResponse,
SaveScreenRequest,
SaveScreenResponse,
} from "@budibase/types"
import { BaseAPIClient } from "./types"
export interface ScreenEndpoints {
saveScreen: (screen: SaveScreenRequest) => Promise<SaveScreenResponse>
deleteScreen: (id: string, rev: string) => Promise<DeleteScreenResponse>
}
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}`,
})
},
})

View File

@ -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<UpdateSelfResponse>
generateAPIKey: () => Promise<string | undefined>
fetchDeveloperInfo: () => Promise<FetchAPIKeyResponse>
fetchBuilderSelf: () => Promise<GetGlobalSelfResponse>
fetchSelf: () => Promise<AppSelfResponse>
}
export const buildSelfEndpoints = (API: BaseAPIClient): SelfEndpoints => ({
/** /**
* Using the logged in user, this will generate a new API key, * Using the logged in user, this will generate a new API key,
* assuming the user is a builder. * assuming the user is a builder.
* @return {Promise<object>} returns the API response, including an API key.
*/ */
generateAPIKey: async () => { generateAPIKey: async () => {
const response = await API.post({ const response = await API.post<null, GenerateAPIKeyResponse>({
url: "/api/global/self/api_key", url: "/api/global/self/api_key",
}) })
return response?.apiKey return response?.apiKey
@ -13,7 +30,6 @@ export const buildSelfEndpoints = API => ({
/** /**
* retrieves the API key for the logged in user. * retrieves the API key for the logged in user.
* @return {Promise<object>} An object containing the user developer information.
*/ */
fetchDeveloperInfo: async () => { fetchDeveloperInfo: async () => {
return API.get({ return API.get({

View File

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

View File

@ -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<FindTableResponse>
fetchTableData: (tableId: string) => Promise<FetchRowsResponse>
searchTable: (
sourceId: string,
opts: SearchRowRequest
) => Promise<PaginatedSearchRowResponse>
importTableData: (
tableId: string,
rows: Row[],
identifierFields?: string[]
) => Promise<BulkImportResponse>
csvToJson: (csvString: string) => Promise<CsvToJsonResponse>
getTables: () => Promise<FetchTablesResponse>
getTable: (tableId: string) => Promise<FindTableResponse>
saveTable: (table: SaveTableRequest) => Promise<SaveTableResponse>
deleteTable: (id: string, rev: string) => Promise<DeleteTableResponse>
validateNewTableImport: (
rows: Row[],
schema: TableSchema
) => Promise<ValidateTableImportResponse>
validateExistingTableImport: (
rows: Row[],
tableId?: string
) => Promise<ValidateTableImportResponse>
migrateColumn: (
tableId: string,
oldColumn: string,
newColumn: string
) => Promise<MigrateTableResponse>
}
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<BulkImportRequest, BulkImportResponse>({
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<CsvToJsonRequest, CsvToJsonResponse>({
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<MigrateTableRequest, MigrateTableResponse>({
url: `/api/tables/${tableId}/migrate`,
body: {
oldColumn,
newColumn,
},
})
},
})

View File

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

View File

@ -0,0 +1,57 @@
import {
FetchGlobalTemplateByTypeResponse,
FetchGlobalTemplateDefinitionResponse,
FetchTemplateResponse,
SaveGlobalTemplateRequest,
SaveGlobalTemplateResponse,
} from "@budibase/types"
import { BaseAPIClient } from "./types"
export interface TemplateEndpoints {
getEmailTemplates: () => Promise<FetchGlobalTemplateByTypeResponse>
getAppTemplates: () => Promise<FetchTemplateResponse>
getEmailTemplateDefinitions: () => Promise<FetchGlobalTemplateDefinitionResponse>
saveEmailTemplate: (
template: SaveGlobalTemplateRequest
) => Promise<SaveGlobalTemplateResponse>
}
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",
})
},
})

View File

@ -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<string, string>
export type APIClientConfig = {
enableCaching?: boolean
attachHeaders?: (headers: Headers) => void
onError?: (error: any) => void
onMigrationDetected?: (migration: string) => void
}
export type APICallConfig<RequestT, ResponseT> = {
method: HTTPMethod
url: string
json: boolean
external: boolean
suppressErrors: boolean
cache: boolean
body?: RequestT
parseResponse?: (response: Response) => Promise<ResponseT> | ResponseT
}
export type APICallParams<
RequestT = null,
ResponseT = void
> = RequestT extends null
? Pick<APICallConfig<RequestT, ResponseT>, "url"> &
Partial<APICallConfig<RequestT, ResponseT>>
: Pick<APICallConfig<RequestT, ResponseT>, "url" | "body"> &
Partial<APICallConfig<RequestT, ResponseT>>
export type BaseAPIClient = {
post: <RequestT = null, ResponseT = void>(
params: APICallParams<RequestT, ResponseT>
) => Promise<ResponseT>
get: <ResponseT = void>(
params: APICallParams<undefined | null, ResponseT>
) => Promise<ResponseT>
put: <RequestT = null, ResponseT = void>(
params: APICallParams<RequestT, ResponseT>
) => Promise<ResponseT>
delete: <RequestT = null, ResponseT = void>(
params: APICallParams<RequestT, ResponseT>
) => Promise<ResponseT>
patch: <RequestT = null, ResponseT = void>(
params: APICallParams<RequestT, ResponseT>
) => Promise<ResponseT>
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 }

View File

@ -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<FetchUsersResponse>
getUser: (userId: string) => Promise<FindUserResponse>
updateOwnMetadata: (
metadata: UpdateSelfMetadataRequest
) => Promise<UpdateSelfMetadataResponse>
createAdminUser: (
user: CreateAdminUserRequest
) => Promise<CreateAdminUserResponse>
saveUser: (user: User) => Promise<SaveUserResponse>
deleteUser: (userId: string) => Promise<DeleteUserResponse>
deleteUsers: (
users: Array<{
userId: string
email: string
}>
) => Promise<BulkUserDeleted | undefined>
onboardUsers: (data: InviteUsersRequest) => Promise<InviteUsersResponse>
getUserInvite: (code: string) => Promise<CheckInviteResponse>
getUserInvites: () => Promise<GetUserInvitesResponse>
inviteUsers: (users: InviteUsersRequest) => Promise<InviteUsersResponse>
removeUserInvites: (
data: DeleteInviteUsersRequest
) => Promise<DeleteInviteUsersResponse>
acceptInvite: (
data: AcceptUserInviteRequest
) => Promise<AcceptUserInviteResponse>
getUserCountByApp: (appId: string) => Promise<number>
getAccountHolder: () => Promise<LookupAccountHolderResponse>
searchUsers: (data: SearchUsersRequest) => Promise<SearchUsersResponse>
createUsers: (
users: User[],
groups: any[]
) => Promise<BulkUserCreated | undefined>
updateUserInvite: (
code: string,
data: UpdateInviteRequest
) => Promise<UpdateInviteResponse>
// 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. * 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. * Searches 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
*/ */
searchUsers: async ({ paginate, bookmark, query, appId, limit } = {}) => { searchUsers: async data => {
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
}
return await API.post({ return await API.post({
url: `/api/global/users/search`, 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. * Updates the current user metadata.
* @param metadata the metadata to save * @param metadata the metadata to save
@ -72,12 +118,12 @@ export const buildUserEndpoints = API => ({
/** /**
* Creates an admin user. * 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({ return await API.post({
url: "/api/global/users/init", 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 users the array of user objects to create
* @param groups the array of group ids to add all users to * @param groups the array of group ids to add all users to
*/ */
createUsers: async ({ users, groups }) => { createUsers: async (users, groups) => {
const res = await API.post({ const res = await API.post<BulkUserRequest, BulkUserResponse>({
url: "/api/global/users/bulk", url: "/api/global/users/bulk",
body: { body: {
create: { create: {
@ -125,7 +171,7 @@ export const buildUserEndpoints = API => ({
* @param users the ID/email pair of the user to delete * @param users the ID/email pair of the user to delete
*/ */
deleteUsers: async users => { deleteUsers: async users => {
const res = await API.post({ const res = await API.post<BulkUserRequest, BulkUserResponse>({
url: `/api/global/users/bulk`, url: `/api/global/users/bulk`,
body: { body: {
delete: { delete: {
@ -137,54 +183,23 @@ export const buildUserEndpoints = API => ({
}, },
/** /**
* Invites a user to the current tenant. * Onboards multiple users
* @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
*/ */
inviteUser: async ({ email, builder, admin, apps }) => { onboardUsers: async data => {
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 => {
return await API.post({ return await API.post({
url: "/api/global/users/onboard", url: "/api/global/users/onboard",
body: payload.map(invite => { body: data,
const { email, admin, builder, apps } = invite
return {
email,
userInfo: {
admin,
builder,
apps: apps ? apps : undefined,
},
}
}),
}) })
}, },
/** /**
* Accepts a user invite as a body and will update the associated app roles. * Accepts a user invite as a body and will update the associated app roles.
* for an existing invite * for an existing invite
* @param invite the invite code sent in the email
*/ */
updateUserInvite: async invite => { updateUserInvite: async (code, data) => {
await API.post({ return await API.post<UpdateInviteRequest, UpdateInviteResponse>({
url: `/api/global/users/invite/update/${invite.code}`, url: `/api/global/users/invite/update/${code}`,
body: { body: data,
apps: invite.apps,
builder: invite.builder,
},
}) })
}, },
@ -214,72 +229,46 @@ export const buildUserEndpoints = API => ({
inviteUsers: async users => { inviteUsers: async users => {
return await API.post({ return await API.post({
url: "/api/global/users/multi/invite", url: "/api/global/users/multi/invite",
body: users.map(user => { body: users,
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,
},
}
}),
}) })
}, },
/** /**
* Removes multiple user invites from Redis cache * Removes multiple user invites from Redis cache
*/ */
removeUserInvites: async inviteCodes => { removeUserInvites: async data => {
return await API.post({ return await API.post({
url: "/api/global/users/multi/invite/delete", url: "/api/global/users/multi/invite/delete",
body: inviteCodes, body: data,
}) })
}, },
/** /**
* Accepts an invite to join the platform and creates a user. * 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({ return await API.post({
url: "/api/global/users/invite/accept", url: "/api/global/users/invite/accept",
body: { body: data,
inviteCode,
password,
firstName,
lastName,
},
}) })
}, },
/** /**
* Accepts an invite to join the platform and creates a user. * Counts the number of users in an app
* @param inviteCode the invite code sent in the email
* @param password the password for the newly created user
*/ */
getUserCountByApp: async ({ appId }) => { getUserCountByApp: async appId => {
return await API.get({ const res = await API.get<CountUserResponse>({
url: `/api/global/users/count/${appId}`, url: `/api/global/users/count/${appId}`,
}) })
return res.userCount
}, },
/** /**
* Adds a per app builder to the selected app * 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 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({ return await API.post({
url: `/api/global/users/${userId}/app/${appId}/builder`, 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 * 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 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({ return await API.delete({
url: `/api/global/users/${userId}/app/${appId}/builder`, url: `/api/global/users/${userId}/app/${appId}/builder`,
}) })
}, },
/**
* Gets the account holder of the current tenant
*/
getAccountHolder: async () => { getAccountHolder: async () => {
return await API.get({ return await API.get({
url: `/api/global/users/accountholder`, url: `/api/global/users/accountholder`,

View File

@ -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<Row[]>
exportView: (name: string, format: string) => Promise<any>
saveView: (view: any) => Promise<any>
deleteView: (name: string) => Promise<any>
}
export const buildViewEndpoints = (API: BaseAPIClient): ViewEndpoints => ({
/** /**
* Fetches all rows in a view * Fetches all rows in a view
* @param name the name of the view * @param name the name of the view
@ -6,7 +17,7 @@ export const buildViewEndpoints = API => ({
* @param groupBy the field to group by * @param groupBy the field to group by
* @param calculation the calculation to perform * @param calculation the calculation to perform
*/ */
fetchViewData: async ({ name, field, groupBy, calculation }) => { fetchViewData: async (name, { field, groupBy, calculation }) => {
const params = new URLSearchParams() const params = new URLSearchParams()
if (calculation) { if (calculation) {
params.set("field", field) params.set("field", field)
@ -23,11 +34,11 @@ export const buildViewEndpoints = API => ({
/** /**
* Exports a view for download * Exports a view for download
* @param viewName the view to export * @param name the view to export
* @param format the format to download * @param format the format to download
*/ */
exportView: async ({ viewName, format }) => { exportView: async (name, format) => {
const safeViewName = encodeURIComponent(viewName) const safeViewName = encodeURIComponent(name)
return await API.get({ return await API.get({
url: `/api/views/export?view=${safeViewName}&format=${format}`, url: `/api/views/export?view=${safeViewName}&format=${format}`,
parseResponse: async response => { parseResponse: async response => {
@ -51,9 +62,9 @@ export const buildViewEndpoints = API => ({
* Deletes a view. * Deletes a view.
* @param viewName the name of the view to delete * @param viewName the name of the view to delete
*/ */
deleteView: async viewName => { deleteView: async name => {
return await API.delete({ return await API.delete({
url: `/api/views/${encodeURIComponent(viewName)}`, url: `/api/views/${encodeURIComponent(name)}`,
}) })
}, },
}) })

View File

@ -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<ViewResponseEnriched>
create: (view: CreateViewRequest) => Promise<CreateViewResponse>
update: (view: UpdateViewRequest) => Promise<UpdateViewResponse>
fetch: (
viewId: string,
opts: SearchViewRowRequest
) => Promise<SearchRowResponse>
delete: (viewId: string) => Promise<void>
}
export const buildViewV2Endpoints = (API: BaseAPIClient): ViewV2Endpoints => ({
/** /**
* Fetches the definition of a view * Fetches the definition of a view
* @param viewId the ID of the view to fetch * @param viewId the ID of the view to fetch
@ -9,6 +31,7 @@ export const buildViewV2Endpoints = API => ({
cache: true, cache: true,
}) })
}, },
/** /**
* Create a new view * Create a new view
* @param view the view object * @param view the view object
@ -19,6 +42,7 @@ export const buildViewV2Endpoints = API => ({
body: view, body: view,
}) })
}, },
/** /**
* Updates a view * Updates a view
* @param view the view object * @param view the view object
@ -29,40 +53,19 @@ export const buildViewV2Endpoints = API => ({
body: view, body: view,
}) })
}, },
/** /**
* Fetches all rows in a view * Fetches all rows in a view
* @param viewId the id of the view * @param viewId the id of the view
* @param query the search query * @param opts the search options
* @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)
*/ */
fetch: async ({ fetch: async (viewId, opts) => {
viewId,
query,
paginate,
limit,
bookmark,
sort,
sortOrder,
sortType,
}) => {
return await API.post({ return await API.post({
url: `/api/v2/views/${encodeURIComponent(viewId)}/search`, url: `/api/v2/views/${encodeURIComponent(viewId)}/search`,
body: { body: opts,
query,
paginate,
limit,
bookmark,
sort,
sortOrder,
sortType,
},
}) })
}, },
/** /**
* Delete a view * Delete a view
* @param viewId the id of the view * @param viewId the id of the view

Some files were not shown because too many files have changed in this diff Show More