Merge branch 'master' into fix/automation-webhook-improvements
This commit is contained in:
commit
57eaac88b1
|
@ -30,7 +30,7 @@ env:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
@ -47,7 +47,7 @@ jobs:
|
||||||
- run: yarn lint
|
- run: yarn lint
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
@ -76,7 +76,7 @@ jobs:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
helm-lint:
|
helm-lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
@ -88,7 +88,7 @@ jobs:
|
||||||
- run: cd charts/budibase && helm lint .
|
- run: cd charts/budibase && helm lint .
|
||||||
|
|
||||||
test-libraries:
|
test-libraries:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
@ -122,7 +122,7 @@ jobs:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
test-worker:
|
test-worker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
@ -151,7 +151,7 @@ jobs:
|
||||||
yarn test --verbose --reporters=default --reporters=github-actions
|
yarn test --verbose --reporters=default --reporters=github-actions
|
||||||
|
|
||||||
test-server:
|
test-server:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
datasource:
|
datasource:
|
||||||
|
@ -237,7 +237,7 @@ jobs:
|
||||||
yarn test --filter $FILTER --verbose --reporters=default --reporters=github-actions
|
yarn test --filter $FILTER --verbose --reporters=default --reporters=github-actions
|
||||||
|
|
||||||
check-pro-submodule:
|
check-pro-submodule:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
if: inputs.run_as_oss != true && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase')
|
if: inputs.run_as_oss != true && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase')
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo and submodules
|
- name: Checkout repo and submodules
|
||||||
|
@ -296,7 +296,7 @@ jobs:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
check-lockfile:
|
check-lockfile:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
if: inputs.run_as_oss != true && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase')
|
if: inputs.run_as_oss != true && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase')
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
const disabled = () => {
|
const disabled = () => {
|
||||||
return {
|
return {
|
||||||
SEND_EMAIL_SMTP: {
|
SEND_EMAIL_SMTP: {
|
||||||
disabled: !$admin.checklist.smtp.checked,
|
disabled: !$admin.checklist?.smtp?.checked,
|
||||||
message: "Please configure SMTP",
|
message: "Please configure SMTP",
|
||||||
},
|
},
|
||||||
COLLECT: {
|
COLLECT: {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 () => {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@
|
||||||
$: useAccountPortal = cloud && !$admin.disableAccountPortal
|
$: useAccountPortal = cloud && !$admin.disableAccountPortal
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if ($admin?.checklist?.adminUser.checked || useAccountPortal) {
|
if ($admin?.checklist?.adminUser?.checked || useAccountPortal) {
|
||||||
$redirect("../")
|
$redirect("../")
|
||||||
} else {
|
} else {
|
||||||
loaded = true
|
loaded = true
|
||||||
|
|
|
@ -36,10 +36,7 @@
|
||||||
await API.createAdminUser(adminUser)
|
await API.createAdminUser(adminUser)
|
||||||
notifications.success("Admin user created")
|
notifications.success("Admin user created")
|
||||||
await admin.init()
|
await admin.init()
|
||||||
await auth.login({
|
await auth.login(formData?.email.trim(), formData?.password)
|
||||||
username: formData?.email.trim(),
|
|
||||||
password: formData?.password,
|
|
||||||
})
|
|
||||||
$goto("../portal")
|
$goto("../portal")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
submitted = false
|
submitted = false
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,7 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
data = await API.fetchViewData({
|
data = await API.fetchViewData(name, {
|
||||||
name,
|
|
||||||
calculation,
|
calculation,
|
||||||
field,
|
field,
|
||||||
groupBy,
|
groupBy,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,7 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await auth.login({
|
await auth.login(formData?.username.trim(), formData?.password)
|
||||||
username: formData?.username.trim(),
|
|
||||||
password: formData?.password,
|
|
||||||
})
|
|
||||||
if ($auth?.user?.forceResetPassword) {
|
if ($auth?.user?.forceResetPassword) {
|
||||||
$goto("./reset")
|
$goto("./reset")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -66,10 +66,7 @@
|
||||||
|
|
||||||
async function login() {
|
async function login() {
|
||||||
try {
|
try {
|
||||||
await auth.login({
|
await auth.login(formData.email.trim(), formData.password.trim())
|
||||||
username: formData.email.trim(),
|
|
||||||
password: formData.password.trim(),
|
|
||||||
})
|
|
||||||
notifications.success("Logged in successfully")
|
notifications.success("Logged in successfully")
|
||||||
$goto("../portal")
|
$goto("../portal")
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -152,16 +152,16 @@
|
||||||
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,
|
||||||
events: selectedEvents,
|
events: selectedEvents,
|
||||||
})
|
})
|
||||||
logsPageInfo.fetched(
|
logsPageInfo.fetched(
|
||||||
$auditLogs.logs.hasNextPage,
|
$auditLogs.logs?.hasNextPage,
|
||||||
$auditLogs.logs.bookmark
|
$auditLogs.logs?.bookmark
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error(`Error getting audit logs - ${error}`)
|
notifications.error(`Error getting audit logs - ${error}`)
|
||||||
|
@ -200,6 +200,8 @@
|
||||||
return Object.entries(obj).map(([id, label]) => {
|
return Object.entries(obj).map(([id, label]) => {
|
||||||
return { id, label }
|
return { id, label }
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,7 +318,7 @@
|
||||||
<Table
|
<Table
|
||||||
on:click={({ detail }) => viewDetails(detail)}
|
on:click={({ detail }) => viewDetails(detail)}
|
||||||
{customRenderers}
|
{customRenderers}
|
||||||
data={$auditLogs.logs.data}
|
data={$auditLogs.logs?.data}
|
||||||
allowEditColumns={false}
|
allowEditColumns={false}
|
||||||
allowEditRows={false}
|
allowEditRows={false}
|
||||||
allowSelectRows={false}
|
allowSelectRows={false}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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: {
|
||||||
|
@ -180,7 +177,7 @@
|
||||||
<Button
|
<Button
|
||||||
secondary
|
secondary
|
||||||
on:click={deleteSmtp}
|
on:click={deleteSmtp}
|
||||||
disabled={!$admin.checklist.smtp.checked}
|
disabled={!$admin.checklist?.smtp?.checked}
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
import { writable } from "svelte/store"
|
import { writable, Writable } from "svelte/store"
|
||||||
|
|
||||||
export default class BudiStore {
|
interface BudiStoreOpts {
|
||||||
constructor(init, opts) {
|
debug?: boolean
|
||||||
const store = writable({ ...init })
|
}
|
||||||
|
|
||||||
|
export default class BudiStore<T> implements Writable<T> {
|
||||||
|
store: Writable<T>
|
||||||
|
subscribe: Writable<T>["subscribe"]
|
||||||
|
update: Writable<T>["update"]
|
||||||
|
set: Writable<T>["set"]
|
||||||
|
|
||||||
|
constructor(init: T, opts?: BudiStoreOpts) {
|
||||||
|
const store = writable<T>(init)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal Svelte store
|
* Internal Svelte store
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { it, expect, describe, beforeEach, vi } from "vitest"
|
import { it, expect, describe, beforeEach, vi } from "vitest"
|
||||||
import { DEFAULT_CONFIG, createAdminStore } from "./admin"
|
import { createAdminStore } from "./admin"
|
||||||
|
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
@ -45,11 +45,6 @@ describe("admin store", () => {
|
||||||
ctx.returnedStore = createAdminStore()
|
ctx.returnedStore = createAdminStore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("inits the writable store with the default config", () => {
|
|
||||||
expect(writable).toHaveBeenCalledTimes(1)
|
|
||||||
expect(writable).toHaveBeenCalledWith(DEFAULT_CONFIG)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("returns the created store", ctx => {
|
it("returns the created store", ctx => {
|
||||||
expect(ctx.returnedStore).toEqual({
|
expect(ctx.returnedStore).toEqual({
|
||||||
subscribe: expect.toBe(ctx.writableReturn.subscribe),
|
subscribe: expect.toBe(ctx.writableReturn.subscribe),
|
||||||
|
|
|
@ -2,27 +2,28 @@ import { writable, get } from "svelte/store"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { auth } from "stores/portal"
|
import { auth } from "stores/portal"
|
||||||
import { banner } from "@budibase/bbui"
|
import { banner } from "@budibase/bbui"
|
||||||
|
import {
|
||||||
|
ConfigChecklistResponse,
|
||||||
|
GetEnvironmentResponse,
|
||||||
|
SystemStatusResponse,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
export const DEFAULT_CONFIG = {
|
interface PortalAdminStore extends GetEnvironmentResponse {
|
||||||
|
loaded: boolean
|
||||||
|
checklist?: ConfigChecklistResponse
|
||||||
|
status?: SystemStatusResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAdminStore() {
|
||||||
|
const admin = writable<PortalAdminStore>({
|
||||||
loaded: false,
|
loaded: false,
|
||||||
multiTenancy: false,
|
multiTenancy: false,
|
||||||
cloud: false,
|
cloud: false,
|
||||||
isDev: false,
|
isDev: false,
|
||||||
disableAccountPortal: false,
|
disableAccountPortal: false,
|
||||||
accountPortalUrl: "",
|
|
||||||
importComplete: false,
|
|
||||||
checklist: {
|
|
||||||
apps: { checked: false },
|
|
||||||
smtp: { checked: false },
|
|
||||||
adminUser: { checked: false },
|
|
||||||
sso: { checked: false },
|
|
||||||
},
|
|
||||||
maintenance: [],
|
|
||||||
offlineMode: false,
|
offlineMode: false,
|
||||||
}
|
maintenance: [],
|
||||||
|
})
|
||||||
export function createAdminStore() {
|
|
||||||
const admin = writable(DEFAULT_CONFIG)
|
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
await getChecklist()
|
await getChecklist()
|
|
@ -1,19 +1,39 @@
|
||||||
import { derived } from "svelte/store"
|
import { derived } from "svelte/store"
|
||||||
|
// @ts-ignore
|
||||||
import { AppStatus } from "constants"
|
import { AppStatus } from "constants"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { auth } from "./auth"
|
import { auth } from "./auth"
|
||||||
import BudiStore from "../BudiStore" // move this
|
import BudiStore from "../BudiStore"
|
||||||
|
import { App, UpdateAppRequest } from "@budibase/types"
|
||||||
|
|
||||||
// properties that should always come from the dev app, not the deployed
|
interface AppIdentifierMetadata {
|
||||||
const DEV_PROPS = ["updatedBy", "updatedAt"]
|
devId?: string
|
||||||
|
devRev?: string
|
||||||
export const INITIAL_APPS_STATE = {
|
prodId?: string
|
||||||
apps: [],
|
prodRev?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AppsStore extends BudiStore {
|
interface AppUIMetadata {
|
||||||
|
deployed: boolean
|
||||||
|
lockedYou: boolean
|
||||||
|
lockedOther: boolean
|
||||||
|
favourite: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StoreApp extends App, AppIdentifierMetadata {}
|
||||||
|
|
||||||
|
interface EnrichedApp extends StoreApp, AppUIMetadata {}
|
||||||
|
|
||||||
|
interface PortalAppsStore {
|
||||||
|
apps: StoreApp[]
|
||||||
|
sortBy?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AppsStore extends BudiStore<PortalAppsStore> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({ ...INITIAL_APPS_STATE })
|
super({
|
||||||
|
apps: [],
|
||||||
|
})
|
||||||
|
|
||||||
this.extractAppId = this.extractAppId.bind(this)
|
this.extractAppId = this.extractAppId.bind(this)
|
||||||
this.getProdAppID = this.getProdAppID.bind(this)
|
this.getProdAppID = this.getProdAppID.bind(this)
|
||||||
|
@ -22,12 +42,12 @@ export class AppsStore extends BudiStore {
|
||||||
this.save = this.save.bind(this)
|
this.save = this.save.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
extractAppId(id) {
|
extractAppId(appId?: string) {
|
||||||
const split = id?.split("_") || []
|
const split = appId?.split("_") || []
|
||||||
return split.length ? split[split.length - 1] : null
|
return split.length ? split[split.length - 1] : null
|
||||||
}
|
}
|
||||||
|
|
||||||
getProdAppID(appId) {
|
getProdAppID(appId: string) {
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
return appId
|
return appId
|
||||||
}
|
}
|
||||||
|
@ -47,15 +67,15 @@ export class AppsStore extends BudiStore {
|
||||||
return `app${separator}${rest}`
|
return `app${separator}${rest}`
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSort(sortBy) {
|
async updateSort(sortBy: string) {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
sortBy,
|
sortBy,
|
||||||
}))
|
}))
|
||||||
this.updateUserSort(sortBy)
|
await this.updateUserSort(sortBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateUserSort(sortBy) {
|
async updateUserSort(sortBy: string) {
|
||||||
try {
|
try {
|
||||||
await auth.updateSelf({ appSort: sortBy })
|
await auth.updateSelf({ appSort: sortBy })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -64,16 +84,19 @@ export class AppsStore extends BudiStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const json = await API.getApps()
|
const json = (await API.getApps()) as App[]
|
||||||
if (Array.isArray(json)) {
|
if (Array.isArray(json)) {
|
||||||
// Merge apps into one sensible list
|
// Merge apps into one sensible list
|
||||||
let appMap = {}
|
let appMap: Record<string, StoreApp> = {}
|
||||||
let devApps = json.filter(app => app.status === AppStatus.DEV)
|
let devApps = json.filter(app => app.status === AppStatus.DEV)
|
||||||
let deployedApps = json.filter(app => app.status === AppStatus.DEPLOYED)
|
let deployedApps = json.filter(app => app.status === AppStatus.DEPLOYED)
|
||||||
|
|
||||||
// First append all dev app version
|
// First append all dev app version
|
||||||
devApps.forEach(app => {
|
devApps.forEach(app => {
|
||||||
const id = this.extractAppId(app.appId)
|
const id = this.extractAppId(app.appId)
|
||||||
|
if (!id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
appMap[id] = {
|
appMap[id] = {
|
||||||
...app,
|
...app,
|
||||||
devId: app.appId,
|
devId: app.appId,
|
||||||
|
@ -84,20 +107,22 @@ export class AppsStore extends BudiStore {
|
||||||
// Then merge with all prod app versions
|
// Then merge with all prod app versions
|
||||||
deployedApps.forEach(app => {
|
deployedApps.forEach(app => {
|
||||||
const id = this.extractAppId(app.appId)
|
const id = this.extractAppId(app.appId)
|
||||||
|
if (!id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Skip any deployed apps which don't have a dev counterpart
|
// Skip any deployed apps which don't have a dev counterpart
|
||||||
if (!appMap[id]) {
|
if (!appMap[id]) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let devProps = {}
|
// Extract certain properties from the dev app to override the prod app
|
||||||
|
let devProps: Pick<App, "updatedBy" | "updatedAt"> = {}
|
||||||
if (appMap[id]) {
|
if (appMap[id]) {
|
||||||
const entries = Object.entries(appMap[id]).filter(
|
devProps = {
|
||||||
([key]) => DEV_PROPS.indexOf(key) !== -1
|
updatedBy: appMap[id].updatedBy,
|
||||||
)
|
updatedAt: appMap[id].updatedAt,
|
||||||
entries.forEach(entry => {
|
}
|
||||||
devProps[entry[0]] = entry[1]
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
appMap[id] = {
|
appMap[id] = {
|
||||||
...appMap[id],
|
...appMap[id],
|
||||||
|
@ -111,7 +136,10 @@ export class AppsStore extends BudiStore {
|
||||||
// Transform into an array and clean up
|
// Transform into an array and clean up
|
||||||
const apps = Object.values(appMap)
|
const apps = Object.values(appMap)
|
||||||
apps.forEach(app => {
|
apps.forEach(app => {
|
||||||
app.appId = this.extractAppId(app.devId)
|
const appId = this.extractAppId(app.devId)
|
||||||
|
if (appId) {
|
||||||
|
app.appId = appId
|
||||||
|
}
|
||||||
delete app._id
|
delete app._id
|
||||||
delete app._rev
|
delete app._rev
|
||||||
})
|
})
|
||||||
|
@ -127,11 +155,8 @@ export class AppsStore extends BudiStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(appId, value) {
|
async save(appId: string, value: UpdateAppRequest) {
|
||||||
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
|
||||||
|
@ -156,15 +181,16 @@ export const sortBy = derived([appsStore, auth], ([$store, $auth]) => {
|
||||||
export const enrichedApps = derived(
|
export const enrichedApps = derived(
|
||||||
[appsStore, auth, sortBy],
|
[appsStore, auth, sortBy],
|
||||||
([$store, $auth, $sortBy]) => {
|
([$store, $auth, $sortBy]) => {
|
||||||
const enrichedApps = $store.apps
|
const enrichedApps: EnrichedApp[] = $store.apps.map(app => {
|
||||||
? $store.apps.map(app => ({
|
const user = $auth.user
|
||||||
|
return {
|
||||||
...app,
|
...app,
|
||||||
deployed: app.status === AppStatus.DEPLOYED,
|
deployed: app.status === AppStatus.DEPLOYED,
|
||||||
lockedYou: app.lockedBy && app.lockedBy.email === $auth.user?.email,
|
lockedYou: app.lockedBy != null && app.lockedBy.email === user?.email,
|
||||||
lockedOther: app.lockedBy && app.lockedBy.email !== $auth.user?.email,
|
lockedOther: app.lockedBy != null && app.lockedBy.email !== user?.email,
|
||||||
favourite: $auth.user?.appFavourites?.includes(app.appId),
|
favourite: !!user?.appFavourites?.includes(app.appId),
|
||||||
}))
|
}
|
||||||
: []
|
})
|
||||||
|
|
||||||
if ($sortBy === "status") {
|
if ($sortBy === "status") {
|
||||||
return enrichedApps.sort((a, b) => {
|
return enrichedApps.sort((a, b) => {
|
|
@ -1,43 +0,0 @@
|
||||||
import { writable, get } from "svelte/store"
|
|
||||||
import { API } from "api"
|
|
||||||
import { licensing } from "stores/portal"
|
|
||||||
|
|
||||||
export function createAuditLogsStore() {
|
|
||||||
const { subscribe, update } = writable({
|
|
||||||
events: {},
|
|
||||||
logs: {},
|
|
||||||
})
|
|
||||||
|
|
||||||
async function search(opts = {}) {
|
|
||||||
if (get(licensing).auditLogsEnabled) {
|
|
||||||
const paged = await API.searchAuditLogs(opts)
|
|
||||||
|
|
||||||
update(state => {
|
|
||||||
return { ...state, logs: { ...paged, opts } }
|
|
||||||
})
|
|
||||||
|
|
||||||
return paged
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getEventDefinitions() {
|
|
||||||
const events = await API.getEventDefinitions()
|
|
||||||
|
|
||||||
update(state => {
|
|
||||||
return { ...state, ...events }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDownloadUrl(opts = {}) {
|
|
||||||
return API.getDownloadUrl(opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
subscribe,
|
|
||||||
search,
|
|
||||||
getEventDefinitions,
|
|
||||||
getDownloadUrl,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const auditLogs = createAuditLogsStore()
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { get } from "svelte/store"
|
||||||
|
import { API } from "api"
|
||||||
|
import { licensing } from "./licensing"
|
||||||
|
import BudiStore from "../BudiStore"
|
||||||
|
import {
|
||||||
|
DownloadAuditLogsRequest,
|
||||||
|
SearchAuditLogsRequest,
|
||||||
|
SearchAuditLogsResponse,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
interface PortalAuditLogsStore {
|
||||||
|
events?: Record<string, string>
|
||||||
|
logs?: SearchAuditLogsResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AuditLogsStore extends BudiStore<PortalAuditLogsStore> {
|
||||||
|
constructor() {
|
||||||
|
super({})
|
||||||
|
}
|
||||||
|
|
||||||
|
async search(opts: SearchAuditLogsRequest = {}) {
|
||||||
|
if (get(licensing).auditLogsEnabled) {
|
||||||
|
const res = await API.searchAuditLogs(opts)
|
||||||
|
this.update(state => ({
|
||||||
|
...state,
|
||||||
|
logs: res,
|
||||||
|
}))
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEventDefinitions() {
|
||||||
|
const res = await API.getEventDefinitions()
|
||||||
|
this.update(state => ({
|
||||||
|
...state,
|
||||||
|
events: res.events,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
getDownloadUrl(opts: DownloadAuditLogsRequest = {}) {
|
||||||
|
return API.getDownloadUrl(opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const auditLogs = new AuditLogsStore()
|
|
@ -1,172 +0,0 @@
|
||||||
import { derived, writable, get } from "svelte/store"
|
|
||||||
import { API } from "api"
|
|
||||||
import { admin } from "stores/portal"
|
|
||||||
import analytics from "analytics"
|
|
||||||
|
|
||||||
export function createAuthStore() {
|
|
||||||
const auth = writable({
|
|
||||||
user: null,
|
|
||||||
accountPortalAccess: false,
|
|
||||||
tenantId: "default",
|
|
||||||
tenantSet: false,
|
|
||||||
loaded: false,
|
|
||||||
postLogout: false,
|
|
||||||
})
|
|
||||||
const store = derived(auth, $store => {
|
|
||||||
return {
|
|
||||||
user: $store.user,
|
|
||||||
accountPortalAccess: $store.accountPortalAccess,
|
|
||||||
tenantId: $store.tenantId,
|
|
||||||
tenantSet: $store.tenantSet,
|
|
||||||
loaded: $store.loaded,
|
|
||||||
postLogout: $store.postLogout,
|
|
||||||
isSSO: !!$store.user?.provider,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function setUser(user) {
|
|
||||||
auth.update(store => {
|
|
||||||
store.loaded = true
|
|
||||||
store.user = user
|
|
||||||
store.accountPortalAccess = user?.accountPortalAccess
|
|
||||||
if (user) {
|
|
||||||
store.tenantId = user.tenantId || "default"
|
|
||||||
store.tenantSet = true
|
|
||||||
}
|
|
||||||
return store
|
|
||||||
})
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
analytics
|
|
||||||
.activate()
|
|
||||||
.then(() => {
|
|
||||||
analytics.identify(user._id)
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// This request may fail due to browser extensions blocking requests
|
|
||||||
// containing the word analytics, so we don't want to spam users with
|
|
||||||
// an error here.
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setOrganisation(tenantId) {
|
|
||||||
const prevId = get(store).tenantId
|
|
||||||
auth.update(store => {
|
|
||||||
store.tenantId = tenantId
|
|
||||||
store.tenantSet = !!tenantId
|
|
||||||
return store
|
|
||||||
})
|
|
||||||
if (prevId !== tenantId) {
|
|
||||||
// re-init admin after setting org
|
|
||||||
await admin.init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setInitInfo(info) {
|
|
||||||
await API.setInitInfo(info)
|
|
||||||
auth.update(store => {
|
|
||||||
store.initInfo = info
|
|
||||||
return store
|
|
||||||
})
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
function setPostLogout() {
|
|
||||||
auth.update(store => {
|
|
||||||
store.postLogout = true
|
|
||||||
return store
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getInitInfo() {
|
|
||||||
const info = await API.getInitInfo()
|
|
||||||
auth.update(store => {
|
|
||||||
store.initInfo = info
|
|
||||||
return store
|
|
||||||
})
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
const actions = {
|
|
||||||
checkQueryString: async () => {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search)
|
|
||||||
if (urlParams.has("tenantId")) {
|
|
||||||
const tenantId = urlParams.get("tenantId")
|
|
||||||
await setOrganisation(tenantId)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setOrg: async tenantId => {
|
|
||||||
await setOrganisation(tenantId)
|
|
||||||
},
|
|
||||||
getSelf: async () => {
|
|
||||||
// We need to catch this locally as we never want this to fail, even
|
|
||||||
// though normally we never want to swallow API errors at the store level.
|
|
||||||
// We're either logged in or we aren't.
|
|
||||||
// We also need to always update the loaded flag.
|
|
||||||
try {
|
|
||||||
const user = await API.fetchBuilderSelf()
|
|
||||||
setUser(user)
|
|
||||||
} catch (error) {
|
|
||||||
setUser(null)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
login: async creds => {
|
|
||||||
const tenantId = get(store).tenantId
|
|
||||||
await API.logIn({
|
|
||||||
username: creds.username,
|
|
||||||
password: creds.password,
|
|
||||||
tenantId,
|
|
||||||
})
|
|
||||||
await actions.getSelf()
|
|
||||||
},
|
|
||||||
logout: async () => {
|
|
||||||
await API.logOut()
|
|
||||||
setPostLogout()
|
|
||||||
setUser(null)
|
|
||||||
await setInitInfo({})
|
|
||||||
},
|
|
||||||
updateSelf: async fields => {
|
|
||||||
await API.updateSelf({ ...fields })
|
|
||||||
// Refetch to enrich after update.
|
|
||||||
try {
|
|
||||||
const user = await API.fetchBuilderSelf()
|
|
||||||
setUser(user)
|
|
||||||
} catch (error) {
|
|
||||||
setUser(null)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
forgotPassword: async email => {
|
|
||||||
const tenantId = get(store).tenantId
|
|
||||||
await API.requestForgotPassword({
|
|
||||||
tenantId,
|
|
||||||
email,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
resetPassword: async (password, resetCode) => {
|
|
||||||
const tenantId = get(store).tenantId
|
|
||||||
await API.resetPassword({
|
|
||||||
tenantId,
|
|
||||||
password,
|
|
||||||
resetCode,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
generateAPIKey: async () => {
|
|
||||||
return API.generateAPIKey()
|
|
||||||
},
|
|
||||||
fetchAPIKey: async () => {
|
|
||||||
const info = await API.fetchDeveloperInfo()
|
|
||||||
return info?.apiKey
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
subscribe: store.subscribe,
|
|
||||||
setOrganisation,
|
|
||||||
getInitInfo,
|
|
||||||
setInitInfo,
|
|
||||||
...actions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const auth = createAuthStore()
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
import { get } from "svelte/store"
|
||||||
|
import { API } from "api"
|
||||||
|
import { admin } from "stores/portal"
|
||||||
|
import analytics from "analytics"
|
||||||
|
import BudiStore from "stores/BudiStore"
|
||||||
|
import {
|
||||||
|
isSSOUser,
|
||||||
|
SetInitInfoRequest,
|
||||||
|
UpdateSelfRequest,
|
||||||
|
User,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
interface PortalAuthStore {
|
||||||
|
user?: User
|
||||||
|
initInfo?: Record<string, any>
|
||||||
|
accountPortalAccess: boolean
|
||||||
|
loaded: boolean
|
||||||
|
isSSO: boolean
|
||||||
|
tenantId: string
|
||||||
|
tenantSet: boolean
|
||||||
|
postLogout: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthStore extends BudiStore<PortalAuthStore> {
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
accountPortalAccess: false,
|
||||||
|
tenantId: "default",
|
||||||
|
tenantSet: false,
|
||||||
|
loaded: false,
|
||||||
|
postLogout: false,
|
||||||
|
isSSO: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setUser(user?: User) {
|
||||||
|
this.set({
|
||||||
|
loaded: true,
|
||||||
|
user: user,
|
||||||
|
accountPortalAccess: !!user?.accountPortalAccess,
|
||||||
|
tenantId: user?.tenantId || "default",
|
||||||
|
tenantSet: !!user,
|
||||||
|
isSSO: user != null && isSSOUser(user),
|
||||||
|
postLogout: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
analytics
|
||||||
|
.activate()
|
||||||
|
.then(() => {
|
||||||
|
analytics.identify(user._id)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// This request may fail due to browser extensions blocking requests
|
||||||
|
// containing the word analytics, so we don't want to spam users with
|
||||||
|
// an error here.
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setOrganisation(tenantId: string) {
|
||||||
|
const prevId = get(this.store).tenantId
|
||||||
|
auth.update(store => {
|
||||||
|
store.tenantId = tenantId
|
||||||
|
store.tenantSet = !!tenantId
|
||||||
|
return store
|
||||||
|
})
|
||||||
|
if (prevId !== tenantId) {
|
||||||
|
// re-init admin after setting org
|
||||||
|
await admin.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setInitInfo(info: SetInitInfoRequest) {
|
||||||
|
await API.setInitInfo(info)
|
||||||
|
auth.update(store => {
|
||||||
|
store.initInfo = info
|
||||||
|
return store
|
||||||
|
})
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
setPostLogout() {
|
||||||
|
auth.update(store => {
|
||||||
|
store.postLogout = true
|
||||||
|
return store
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInitInfo() {
|
||||||
|
const info = await API.getInitInfo()
|
||||||
|
auth.update(store => {
|
||||||
|
store.initInfo = info
|
||||||
|
return store
|
||||||
|
})
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkQueryString() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
|
const tenantId = urlParams.get("tenantId")
|
||||||
|
if (tenantId) {
|
||||||
|
await this.setOrganisation(tenantId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setOrg(tenantId: string) {
|
||||||
|
await this.setOrganisation(tenantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSelf() {
|
||||||
|
// We need to catch this locally as we never want this to fail, even
|
||||||
|
// though normally we never want to swallow API errors at the store level.
|
||||||
|
// We're either logged in or we aren't.
|
||||||
|
// We also need to always update the loaded flag.
|
||||||
|
try {
|
||||||
|
const user = await API.fetchBuilderSelf()
|
||||||
|
this.setUser(user)
|
||||||
|
} catch (error) {
|
||||||
|
this.setUser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async login(username: string, password: string) {
|
||||||
|
const tenantId = get(this.store).tenantId
|
||||||
|
await API.logIn(tenantId, username, password)
|
||||||
|
await this.getSelf()
|
||||||
|
}
|
||||||
|
|
||||||
|
async logout() {
|
||||||
|
await API.logOut()
|
||||||
|
this.setPostLogout()
|
||||||
|
this.setUser()
|
||||||
|
await this.setInitInfo({})
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateSelf(fields: UpdateSelfRequest) {
|
||||||
|
await API.updateSelf(fields)
|
||||||
|
// Refetch to enrich after update.
|
||||||
|
try {
|
||||||
|
const user = await API.fetchBuilderSelf()
|
||||||
|
this.setUser(user)
|
||||||
|
} catch (error) {
|
||||||
|
this.setUser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async forgotPassword(email: string) {
|
||||||
|
const tenantId = get(this.store).tenantId
|
||||||
|
await API.requestForgotPassword(tenantId, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
async resetPassword(password: string, resetCode: string) {
|
||||||
|
const tenantId = get(this.store).tenantId
|
||||||
|
await API.resetPassword(tenantId, password, resetCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateAPIKey() {
|
||||||
|
return API.generateAPIKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchAPIKey() {
|
||||||
|
const info = await API.fetchDeveloperInfo()
|
||||||
|
return info?.apiKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const auth = new AuthStore()
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ export const createLicensingStore = () => {
|
||||||
scimEnabled: false,
|
scimEnabled: false,
|
||||||
budibaseAIEnabled: false,
|
budibaseAIEnabled: false,
|
||||||
customAIConfigsEnabled: false,
|
customAIConfigsEnabled: false,
|
||||||
|
auditLogsEnabled: false,
|
||||||
// the currently used quotas from the db
|
// the currently used quotas from the db
|
||||||
quotaUsage: undefined,
|
quotaUsage: undefined,
|
||||||
// derived quota metrics for percentages used
|
// derived quota metrics for percentages used
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 []
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 || []),
|
||||||
{
|
{
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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": "*",
|
||||||
|
|
|
@ -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 },
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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 },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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 },
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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,
|
|
@ -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,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -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}`
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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}`
|
||||||
|
},
|
||||||
|
})
|
|
@ -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,
|
|
@ -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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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 },
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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 },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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,
|
|
@ -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`,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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}`,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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`,
|
||||||
})
|
})
|
|
@ -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",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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,32 +189,41 @@ 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 =
|
||||||
|
(method: HTTPMethod) =>
|
||||||
|
async <RequestT = null, ResponseT = void>(
|
||||||
|
params: APICallParams<RequestT, ResponseT>
|
||||||
|
): Promise<ResponseT> => {
|
||||||
try {
|
try {
|
||||||
let { url, cache = false, external = false } = params
|
let callConfig: APICallConfig<RequestT, ResponseT> = {
|
||||||
|
json: true,
|
||||||
|
external: false,
|
||||||
|
suppressErrors: false,
|
||||||
|
cache: false,
|
||||||
|
method,
|
||||||
|
...params,
|
||||||
|
}
|
||||||
|
let { url, cache, external } = callConfig
|
||||||
if (!external) {
|
if (!external) {
|
||||||
url = `/${url}`.replace("//", "/")
|
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 }
|
|
||||||
return await handler(enrichedParams)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (config?.onError) {
|
if (config?.onError) {
|
||||||
config.onError(error)
|
config.onError(error)
|
||||||
|
@ -245,13 +233,13 @@ export const createAPIClient = config => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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),
|
|
@ -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}`,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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.
|
||||||
*/
|
*/
|
|
@ -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",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
})
|
|
@ -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}`,
|
||||||
})
|
})
|
|
@ -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
|
|
@ -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,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -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] || []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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] || []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
|
@ -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}`,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -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",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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 (
|
||||||
|
await API.get<RowActionsResponse>({
|
||||||
url: `/api/tables/${tableId}/actions`,
|
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,
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue