From fdc6e53495d793e904ee2b470afa5325adfafc3d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 25 Nov 2024 16:42:20 +0000 Subject: [PATCH 001/103] Improve typing of core API client functions --- packages/frontend-core/package.json | 2 +- .../src/api/{index.js => index.ts} | 159 ++++++++---------- packages/frontend-core/src/api/types.ts | 36 ++++ .../frontend-core/src/{index.js => index.ts} | 0 4 files changed, 104 insertions(+), 93 deletions(-) rename packages/frontend-core/src/api/{index.js => index.ts} (69%) create mode 100644 packages/frontend-core/src/api/types.ts rename packages/frontend-core/src/{index.js => index.ts} (100%) diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index 2eed29f77c..54054461a0 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -4,7 +4,7 @@ "description": "Budibase frontend core libraries used in builder and client", "author": "Budibase", "license": "MPL-2.0", - "svelte": "src/index.js", + "svelte": "./src/index.ts", "dependencies": { "@budibase/bbui": "0.0.0", "@budibase/shared-core": "0.0.0", diff --git a/packages/frontend-core/src/api/index.js b/packages/frontend-core/src/api/index.ts similarity index 69% rename from packages/frontend-core/src/api/index.js rename to packages/frontend-core/src/api/index.ts index 1ca8b7c3f4..cb4753d7cd 100644 --- a/packages/frontend-core/src/api/index.js +++ b/packages/frontend-core/src/api/index.ts @@ -1,3 +1,10 @@ +import { + HTTPMethod, + APICallParams, + APIClientConfig, + APIClient, + APICallConfig, +} from "../types" import { Helpers } from "@budibase/bbui" import { Header } from "@budibase/shared-core" import { ApiVersion } from "../constants" @@ -45,55 +52,21 @@ import { buildRowActionEndpoints } from "./rowActions" */ export const APISessionID = Helpers.uuid() -const defaultAPIClientConfig = { - /** - * Certain definitions can't change at runtime for client apps, such as the - * schema of tables. The endpoints that are cacheable can be cached by passing - * in this flag. It's disabled by default to avoid bugs with stale data. - */ - enableCaching: false, - - /** - * A function can be passed in to attach headers to all outgoing requests. - * This function is passed in the headers object, which should be directly - * mutated. No return value is required. - */ - attachHeaders: null, - - /** - * A function can be passed in which will be invoked any time an API error - * occurs. An error is defined as a status code >= 400. This function is - * invoked before the actual JS error is thrown up the stack. - */ - onError: null, - - /** - * A function can be passed to be called when an API call returns info about a migration running for a specific app - */ - onMigrationDetected: null, -} - /** * Constructs an API client with the provided configuration. - * @param config the API client configuration - * @return {object} the API client */ -export const createAPIClient = config => { - config = { - ...defaultAPIClientConfig, - ...config, - } - let cache = {} +export const createAPIClient = (config: APIClientConfig = {}) => { + let cache: Record = {} // Generates an error object from an API response const makeErrorFromResponse = async ( - response, - method, + response: Response, + method: HTTPMethod, suppressErrors = false ) => { // Try to read a message from the error let message = response.statusText - let json = null + let json: any = null try { json = await response.json() if (json?.message) { @@ -116,29 +89,24 @@ export const createAPIClient = config => { } // Generates an error object from a string - const makeError = (message, request) => { + const makeError = (message: string, url?: string, method?: HTTPMethod) => { return { message, json: null, status: 400, - url: request?.url, - method: request?.method, + url: url, + method: method, handled: true, } } // Performs an API call to the server. - const makeApiCall = async ({ - method, - url, - body, - json = true, - external = false, - parseResponse, - suppressErrors = false, - }) => { + const makeApiCall = async (callConfig: APICallConfig): Promise => { + let { json, method, external, body, url, parseResponse, suppressErrors } = + callConfig + // Ensure we don't do JSON processing if sending a GET request - json = json && method !== "GET" + json = json && method !== HTTPMethod.GET // Build headers let headers = { Accept: "application/json" } @@ -159,12 +127,12 @@ export const createAPIClient = config => { try { requestBody = JSON.stringify(body) } catch (error) { - throw makeError("Invalid JSON body", { url, method }) + throw makeError("Invalid JSON body", url, method) } } // Make request - let response + let response: Response try { response = await fetch(url, { method, @@ -174,7 +142,7 @@ export const createAPIClient = config => { }) } catch (error) { delete cache[url] - throw makeError("Failed to send request", { url, method }) + throw makeError("Failed to send request", url, method) } // Handle response @@ -182,13 +150,13 @@ export const createAPIClient = config => { handleMigrations(response) try { if (parseResponse) { - return await parseResponse(response) + return await parseResponse(response) } else { - return await response.json() + return (await response.json()) as T } } catch (error) { delete cache[url] - return null + throw `Failed to parse response: ${error}` } } else { delete cache[url] @@ -196,7 +164,7 @@ export const createAPIClient = config => { } } - const handleMigrations = response => { + const handleMigrations = (response: Response) => { if (!config.onMigrationDetected) { return } @@ -210,48 +178,55 @@ export const createAPIClient = config => { // Performs an API call to the server and caches the response. // Future invocation for this URL will return the cached result instead of // hitting the server again. - const makeCachedApiCall = async params => { - const identifier = params.url - if (!identifier) { - return null - } + const makeCachedApiCall = async ( + callConfig: APICallConfig + ): Promise => { + const identifier = callConfig.url if (!cache[identifier]) { - cache[identifier] = makeApiCall(params) + cache[identifier] = makeApiCall(callConfig) cache[identifier] = await cache[identifier] } - return await cache[identifier] + return (await cache[identifier]) as T } // Constructs an API call function for a particular HTTP method - const requestApiCall = method => async params => { - try { - let { url, cache = false, external = false } = params - if (!external) { - url = `/${url}`.replace("//", "/") - } + const requestApiCall = + (method: HTTPMethod) => + async (params: APICallParams): Promise => { + try { + let callConfig: APICallConfig = { + json: true, + external: false, + suppressErrors: false, + cache: false, + method, + ...params, + } + let { url, cache, external } = callConfig + if (!external) { + callConfig.url = `/${url}`.replace("//", "/") + } - // Cache the request if possible and desired - const cacheRequest = cache && config?.enableCaching - const handler = cacheRequest ? makeCachedApiCall : makeApiCall - - const enrichedParams = { ...params, method, url } - return await handler(enrichedParams) - } catch (error) { - if (config?.onError) { - config.onError(error) + // Cache the request if possible and desired + const cacheRequest = cache && config?.enableCaching + const handler = cacheRequest ? makeCachedApiCall : makeApiCall + return await handler(callConfig) + } catch (error) { + if (config?.onError) { + config.onError(error) + } + throw error } - throw error } - } // Build the underlying core API methods - let API = { - post: requestApiCall("POST"), - get: requestApiCall("GET"), - patch: requestApiCall("PATCH"), - delete: requestApiCall("DELETE"), - put: requestApiCall("PUT"), - error: message => { + let API: APIClient = { + post: requestApiCall(HTTPMethod.POST), + get: requestApiCall(HTTPMethod.GET), + patch: requestApiCall(HTTPMethod.PATCH), + delete: requestApiCall(HTTPMethod.DELETE), + put: requestApiCall(HTTPMethod.PUT), + error: (message: string) => { throw makeError(message) }, invalidateCache: () => { @@ -260,9 +235,9 @@ export const createAPIClient = config => { // Generic utility to extract the current app ID. Assumes that any client // that exists in an app context will be attaching our app ID header. - getAppID: () => { + getAppID: (): string => { let headers = {} - config?.attachHeaders(headers) + config?.attachHeaders?.(headers) return headers?.[Header.APP_ID] }, } diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts new file mode 100644 index 0000000000..4406deb31b --- /dev/null +++ b/packages/frontend-core/src/api/types.ts @@ -0,0 +1,36 @@ +export enum HTTPMethod { + POST = "POST", + PATCH = "PATCH", + GET = "GET", + PUT = "PUT", + DELETE = "DELETE", +} + +export type APIClientConfig = { + enableCaching?: boolean + attachHeaders?: Function + onError?: Function + onMigrationDetected?: Function +} + +export type APICallConfig = { + method: HTTPMethod + url: string + json: boolean + external: boolean + suppressErrors: boolean + cache: boolean + body?: any + parseResponse?: (response: Response) => Promise +} + +export type APICallParams = Pick & Partial + +export type APIClient = { + post: (params: APICallParams) => Promise + get: (params: APICallParams) => Promise + put: (params: APICallParams) => Promise + delete: (params: APICallParams) => Promise + patch: (params: APICallParams) => Promise + [key: string]: any +} diff --git a/packages/frontend-core/src/index.js b/packages/frontend-core/src/index.ts similarity index 100% rename from packages/frontend-core/src/index.js rename to packages/frontend-core/src/index.ts From 6776a55356505aa33107e18ea5630c231f39158d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 25 Nov 2024 16:42:33 +0000 Subject: [PATCH 002/103] Update lock --- yarn.lock | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yarn.lock b/yarn.lock index 506cea0f03..cb98346bfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12511,6 +12511,11 @@ husky@^8.0.3: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== +husky@^9.1.4: + version "9.1.7" + resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" + integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== + ical-generator@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ical-generator/-/ical-generator-4.1.0.tgz#2a336c951864c5583a2aa715d16f2edcdfd2d90b" From a26eb571152bb0aa9b3fbb0cf6a9289239e24b2a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 26 Nov 2024 09:50:53 +0000 Subject: [PATCH 003/103] Type frontend core app endpoints --- .../frontend-core/src/api/{app.js => app.ts} | 66 ++++++++++++++++++- packages/frontend-core/src/api/index.ts | 7 +- packages/frontend-core/src/api/types.ts | 10 ++- 3 files changed, 75 insertions(+), 8 deletions(-) rename packages/frontend-core/src/api/{app.js => app.ts} (69%) diff --git a/packages/frontend-core/src/api/app.js b/packages/frontend-core/src/api/app.ts similarity index 69% rename from packages/frontend-core/src/api/app.js rename to packages/frontend-core/src/api/app.ts index de1703373b..b884f618bb 100644 --- a/packages/frontend-core/src/api/app.js +++ b/packages/frontend-core/src/api/app.ts @@ -1,6 +1,56 @@ import { sdk } from "@budibase/shared-core" +import { BaseAPIClient } from "./types" +import { + App, + CreateAppRequest, + DuplicateAppRequest, + DuplicateAppResponse, + FetchAppPackageResponse, + GetDiagnosticsResponse, + UpdateAppRequest, + UpdateAppResponse, +} from "@budibase/types" -export const buildAppEndpoints = API => ({ +export type AppEndpoints = { + fetchAppPackage: (appId: string) => Promise + saveAppMetadata: ( + appId: string, + metadata: UpdateAppRequest + ) => Promise + unpublishApp: (appId: string) => Promise + createApp: (app: CreateAppRequest) => Promise + deleteApp: (appId: string) => Promise + duplicateApp: ( + appId: string, + app: DuplicateAppRequest + ) => Promise + updateAppFromExport: ( + appId: string, + body: any + ) => Promise<{ message: string }> + fetchSystemDebugInfo: () => Promise + syncApp: (appId: string) => Promise<{ message: string }> + getApps: () => Promise + fetchComponentLibDefinitions: ( + appId: string + ) => Promise<{ [key: string]: any }> + setRevertableVersion: ( + appId: string, + revertableVersion: string + ) => Promise + addSampleData: (appId: string) => Promise + + // Untyped - TODO: + publishAppChanges: (appId: string) => Promise + revertAppChanges: (appId: string) => Promise + updateAppClientVersion: (appId: string) => Promise + revertAppClientVersion: (appId: string) => Promise + importApps: (apps: any) => Promise + releaseAppLock: (appId: string) => Promise + getAppDeployments: () => Promise +} + +export const buildAppEndpoints = (API: BaseAPIClient): AppEndpoints => ({ /** * Fetches screen definition for an app. * @param appId the ID of the app to fetch from @@ -16,7 +66,7 @@ export const buildAppEndpoints = API => ({ * @param appId the ID of the app to update * @param metadata the app metadata to save */ - saveAppMetadata: async ({ appId, metadata }) => { + saveAppMetadata: async (appId, metadata) => { return await API.put({ url: `/api/applications/${appId}`, body: metadata, @@ -87,7 +137,7 @@ export const buildAppEndpoints = API => ({ * Duplicate an existing app * @param app the app to dupe */ - duplicateApp: async (app, appId) => { + duplicateApp: async (appId, app) => { return await API.post({ url: `/api/applications/${appId}/duplicate`, body: app, @@ -192,12 +242,22 @@ export const buildAppEndpoints = API => ({ }) }, + /** + * Adds sample data to an app + * @param appId the app ID + */ addSampleData: async appId => { return await API.post({ url: `/api/applications/${appId}/sample`, }) }, + /** + * Sets the revertable version of an app. + * Used when manually reverting to older client versions. + * @param appId the app ID + * @param revertableVersion the version number + */ setRevertableVersion: async (appId, revertableVersion) => { return await API.post({ url: `/api/applications/${appId}/setRevertableVersion`, diff --git a/packages/frontend-core/src/api/index.ts b/packages/frontend-core/src/api/index.ts index cb4753d7cd..47900ba029 100644 --- a/packages/frontend-core/src/api/index.ts +++ b/packages/frontend-core/src/api/index.ts @@ -4,7 +4,8 @@ import { APIClientConfig, APIClient, APICallConfig, -} from "../types" + BaseAPIClient, +} from "./types" import { Helpers } from "@budibase/bbui" import { Header } from "@budibase/shared-core" import { ApiVersion } from "../constants" @@ -55,7 +56,7 @@ export const APISessionID = Helpers.uuid() /** * Constructs an API client with the provided configuration. */ -export const createAPIClient = (config: APIClientConfig = {}) => { +export const createAPIClient = (config: APIClientConfig = {}): APIClient => { let cache: Record = {} // Generates an error object from an API response @@ -220,7 +221,7 @@ export const createAPIClient = (config: APIClientConfig = {}) => { } // Build the underlying core API methods - let API: APIClient = { + let API: BaseAPIClient = { post: requestApiCall(HTTPMethod.POST), get: requestApiCall(HTTPMethod.GET), patch: requestApiCall(HTTPMethod.PATCH), diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 4406deb31b..ec6b604340 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -1,3 +1,5 @@ +import { AppEndpoints } from "./app" + export enum HTTPMethod { POST = "POST", PATCH = "PATCH", @@ -26,11 +28,15 @@ export type APICallConfig = { export type APICallParams = Pick & Partial -export type APIClient = { +export type BaseAPIClient = { post: (params: APICallParams) => Promise get: (params: APICallParams) => Promise put: (params: APICallParams) => Promise delete: (params: APICallParams) => Promise patch: (params: APICallParams) => Promise - [key: string]: any + error: (message: string) => void + invalidateCache: () => void + getAppID: () => string } + +export type APIClient = BaseAPIClient & AppEndpoints & { [key: string]: any } From a7873caca82e97d7e8b468d53f2d65695118f92e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 26 Nov 2024 10:32:50 +0000 Subject: [PATCH 004/103] Update usages of app metadata calls --- packages/builder/src/stores/builder/navigation.js | 5 +---- packages/builder/src/stores/builder/snippets.js | 10 ++-------- packages/builder/src/stores/builder/theme.js | 10 ++-------- packages/builder/src/stores/portal/apps.js | 5 +---- 4 files changed, 6 insertions(+), 24 deletions(-) diff --git a/packages/builder/src/stores/builder/navigation.js b/packages/builder/src/stores/builder/navigation.js index 86e484b0a6..c3e19a4327 100644 --- a/packages/builder/src/stores/builder/navigation.js +++ b/packages/builder/src/stores/builder/navigation.js @@ -35,10 +35,7 @@ export class NavigationStore extends BudiStore { async save(navigation) { const appId = get(appStore).appId - const app = await API.saveAppMetadata({ - appId, - metadata: { navigation }, - }) + const app = await API.saveAppMetadata(appId, { navigation }) this.syncAppNavigation(app.navigation) } diff --git a/packages/builder/src/stores/builder/snippets.js b/packages/builder/src/stores/builder/snippets.js index 72ab274730..d5f9a0b2a8 100644 --- a/packages/builder/src/stores/builder/snippets.js +++ b/packages/builder/src/stores/builder/snippets.js @@ -14,19 +14,13 @@ const createsnippets = () => { ...get(store).filter(snippet => snippet.name !== updatedSnippet.name), updatedSnippet, ] - const app = await API.saveAppMetadata({ - appId: get(appStore).appId, - metadata: { snippets }, - }) + const app = await API.saveAppMetadata(get(appStore).appId, { snippets }) syncMetadata(app) } const deleteSnippet = async snippetName => { const snippets = get(store).filter(snippet => snippet.name !== snippetName) - const app = await API.saveAppMetadata({ - appId: get(appStore).appId, - metadata: { snippets }, - }) + const app = await API.saveAppMetadata(get(appStore).appId, { snippets }) syncMetadata(app) } diff --git a/packages/builder/src/stores/builder/theme.js b/packages/builder/src/stores/builder/theme.js index 8e03e856b3..d6ec643e07 100644 --- a/packages/builder/src/stores/builder/theme.js +++ b/packages/builder/src/stores/builder/theme.js @@ -20,10 +20,7 @@ export const createThemeStore = () => { } const save = async (theme, appId) => { - const app = await API.saveAppMetadata({ - appId, - metadata: { theme }, - }) + const app = await API.saveAppMetadata(appId, { theme }) store.update(state => { state.theme = app.theme return state @@ -32,10 +29,7 @@ export const createThemeStore = () => { const saveCustom = async (theme, appId) => { const updated = { ...get(store).customTheme, ...theme } - const app = await API.saveAppMetadata({ - appId, - metadata: { customTheme: updated }, - }) + const app = await API.saveAppMetadata(appId, { customTheme: updated }) store.update(state => { state.customTheme = app.customTheme return state diff --git a/packages/builder/src/stores/portal/apps.js b/packages/builder/src/stores/portal/apps.js index 60f17da481..c543722604 100644 --- a/packages/builder/src/stores/portal/apps.js +++ b/packages/builder/src/stores/portal/apps.js @@ -128,10 +128,7 @@ export class AppsStore extends BudiStore { } async save(appId, value) { - await API.saveAppMetadata({ - appId, - metadata: value, - }) + await API.saveAppMetadata(appId, value) this.update(state => { const updatedAppIndex = state.apps.findIndex( app => app.instance._id === appId From 785938b78a5e7f9095d3f65e06f6f6ec5122d8fd Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 26 Nov 2024 10:37:01 +0000 Subject: [PATCH 005/103] Improve typing of API client callback functions --- packages/frontend-core/src/api/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index ec6b604340..f083d14dd7 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -10,9 +10,9 @@ export enum HTTPMethod { export type APIClientConfig = { enableCaching?: boolean - attachHeaders?: Function - onError?: Function - onMigrationDetected?: Function + attachHeaders?: (headers: Record) => void + onError?: (error: any) => void + onMigrationDetected?: (migration: string) => void } export type APICallConfig = { From 2c40b9a6b2dfe514be4455086f7a0980d9a1622c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 26 Nov 2024 14:53:47 +0000 Subject: [PATCH 006/103] Add better typing for headers --- packages/frontend-core/src/api/index.ts | 5 +++-- packages/frontend-core/src/api/types.ts | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/frontend-core/src/api/index.ts b/packages/frontend-core/src/api/index.ts index 47900ba029..670d2cb19f 100644 --- a/packages/frontend-core/src/api/index.ts +++ b/packages/frontend-core/src/api/index.ts @@ -5,6 +5,7 @@ import { APIClient, APICallConfig, BaseAPIClient, + Headers, } from "./types" import { Helpers } from "@budibase/bbui" import { Header } from "@budibase/shared-core" @@ -110,7 +111,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { json = json && method !== HTTPMethod.GET // Build headers - let headers = { Accept: "application/json" } + let headers: Headers = { Accept: "application/json" } headers[Header.SESSION_ID] = APISessionID if (!external) { headers[Header.API_VER] = ApiVersion @@ -237,7 +238,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { // Generic utility to extract the current app ID. Assumes that any client // that exists in an app context will be attaching our app ID header. getAppID: (): string => { - let headers = {} + let headers: Headers = {} config?.attachHeaders?.(headers) return headers?.[Header.APP_ID] }, diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index f083d14dd7..a5f702053c 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -8,9 +8,11 @@ export enum HTTPMethod { DELETE = "DELETE", } +export type Headers = Record + export type APIClientConfig = { enableCaching?: boolean - attachHeaders?: (headers: Record) => void + attachHeaders?: (headers: Headers) => void onError?: (error: any) => void onMigrationDetected?: (migration: string) => void } From 1e535d36b7889e00679f254a1e29606199efcb37 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 26 Nov 2024 16:51:02 +0000 Subject: [PATCH 007/103] Update AI and analytics endpoints to TS --- .../automation/SetupPanel/CronBuilder.svelte | 4 +-- packages/frontend-core/src/api/ai.js | 11 ------ packages/frontend-core/src/api/ai.ts | 17 ++++++++++ packages/frontend-core/src/api/analytics.js | 17 ---------- packages/frontend-core/src/api/analytics.ts | 34 +++++++++++++++++++ packages/frontend-core/src/api/app.ts | 4 +-- packages/frontend-core/src/api/types.ts | 7 +++- 7 files changed, 60 insertions(+), 34 deletions(-) delete mode 100644 packages/frontend-core/src/api/ai.js create mode 100644 packages/frontend-core/src/api/ai.ts delete mode 100644 packages/frontend-core/src/api/analytics.js create mode 100644 packages/frontend-core/src/api/analytics.ts diff --git a/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte b/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte index fd235a70f2..133d5c9eb1 100644 --- a/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte +++ b/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte @@ -98,9 +98,7 @@ async function generateAICronExpression() { loadingAICronExpression = true try { - const response = await API.generateCronExpression({ - prompt: aiCronPrompt, - }) + const response = await API.generateCronExpression(aiCronPrompt) cronExpression = response.message dispatch("change", response.message) } catch (err) { diff --git a/packages/frontend-core/src/api/ai.js b/packages/frontend-core/src/api/ai.js deleted file mode 100644 index 7fa756a19e..0000000000 --- a/packages/frontend-core/src/api/ai.js +++ /dev/null @@ -1,11 +0,0 @@ -export const buildAIEndpoints = API => ({ - /** - * Generates a cron expression from a prompt - */ - generateCronExpression: async ({ prompt }) => { - return await API.post({ - url: "/api/ai/cron", - body: { prompt }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/ai.ts b/packages/frontend-core/src/api/ai.ts new file mode 100644 index 0000000000..c6adb45b3c --- /dev/null +++ b/packages/frontend-core/src/api/ai.ts @@ -0,0 +1,17 @@ +import { BaseAPIClient } from "./types" + +export interface AIEndpoints { + generateCronExpression: (prompt: string) => Promise<{ message: string }> +} + +export const buildAIEndpoints = (API: BaseAPIClient): AIEndpoints => ({ + /** + * Generates a cron expression from a prompt + */ + generateCronExpression: async (prompt: string) => { + return await API.post({ + url: "/api/ai/cron", + body: { prompt }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/analytics.js b/packages/frontend-core/src/api/analytics.js deleted file mode 100644 index df56bc938e..0000000000 --- a/packages/frontend-core/src/api/analytics.js +++ /dev/null @@ -1,17 +0,0 @@ -export const buildAnalyticsEndpoints = API => ({ - /** - * Gets the current status of analytics for this environment - */ - getAnalyticsStatus: async () => { - return await API.get({ - url: "/api/bbtel", - }) - }, - analyticsPing: async ({ source, embedded }) => { - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone - return await API.post({ - url: "/api/bbtel/ping", - body: { source, timezone, embedded }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/analytics.ts b/packages/frontend-core/src/api/analytics.ts new file mode 100644 index 0000000000..fb97599b6e --- /dev/null +++ b/packages/frontend-core/src/api/analytics.ts @@ -0,0 +1,34 @@ +import { BaseAPIClient } from "./types" + +type AnalyticsPingRequest = { + source?: string + embedded?: boolean +} + +export interface AnalyticsEndpoints { + getAnalyticsStatus: () => Promise<{ enabled: boolean }> + analyticsPing: (payload: AnalyticsPingRequest) => Promise +} + +export const buildAnalyticsEndpoints = ( + API: BaseAPIClient +): AnalyticsEndpoints => ({ + /** + * Gets the current status of analytics for this environment + */ + getAnalyticsStatus: async () => { + return await API.get({ + url: "/api/bbtel", + }) + }, + /** + * Notifies analytics of a certain environment + */ + analyticsPing: async (payload: AnalyticsPingRequest) => { + const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone + return await API.post({ + url: "/api/bbtel/ping", + body: { source: payload.source, embedded: payload.embedded, timezone }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/app.ts b/packages/frontend-core/src/api/app.ts index b884f618bb..12e1500cfe 100644 --- a/packages/frontend-core/src/api/app.ts +++ b/packages/frontend-core/src/api/app.ts @@ -11,7 +11,7 @@ import { UpdateAppResponse, } from "@budibase/types" -export type AppEndpoints = { +export interface AppEndpoints { fetchAppPackage: (appId: string) => Promise saveAppMetadata: ( appId: string, @@ -40,7 +40,7 @@ export type AppEndpoints = { ) => Promise addSampleData: (appId: string) => Promise - // Untyped - TODO: + // TODO publishAppChanges: (appId: string) => Promise revertAppChanges: (appId: string) => Promise updateAppClientVersion: (appId: string) => Promise diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index a5f702053c..257e34d3a3 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -1,3 +1,5 @@ +import { AIEndpoints } from "./ai" +import { AnalyticsEndpoints } from "./analytics" import { AppEndpoints } from "./app" export enum HTTPMethod { @@ -41,4 +43,7 @@ export type BaseAPIClient = { getAppID: () => string } -export type APIClient = BaseAPIClient & AppEndpoints & { [key: string]: any } +export type APIClient = BaseAPIClient & + AIEndpoints & + AnalyticsEndpoints & + AppEndpoints & { [key: string]: any } From 9c35a7758c9b73db65326c7325611678f540cce1 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 27 Nov 2024 10:26:17 +0000 Subject: [PATCH 008/103] Update attachment endpoints to use TS --- .../app/forms/AttachmentField.svelte | 5 +- .../src/components/app/forms/S3Upload.svelte | 7 +- .../app/forms/SignatureField.svelte | 8 +- packages/client/src/utils/buttonActions.js | 7 +- packages/frontend-core/src/api/attachments.js | 78 ------------- packages/frontend-core/src/api/attachments.ts | 109 ++++++++++++++++++ packages/frontend-core/src/api/types.ts | 2 +- 7 files changed, 117 insertions(+), 99 deletions(-) delete mode 100644 packages/frontend-core/src/api/attachments.js create mode 100644 packages/frontend-core/src/api/attachments.ts diff --git a/packages/client/src/components/app/forms/AttachmentField.svelte b/packages/client/src/components/app/forms/AttachmentField.svelte index 27286a8666..d9a91016cb 100644 --- a/packages/client/src/components/app/forms/AttachmentField.svelte +++ b/packages/client/src/components/app/forms/AttachmentField.svelte @@ -49,10 +49,7 @@ data.append("file", fileList[i]) } try { - return await API.uploadAttachment({ - data, - tableId: formContext?.dataSource?.tableId, - }) + return await API.uploadAttachment(formContext?.dataSource?.tableId, data) } catch (error) { return [] } diff --git a/packages/client/src/components/app/forms/S3Upload.svelte b/packages/client/src/components/app/forms/S3Upload.svelte index 0147cbca6e..936eb14f91 100644 --- a/packages/client/src/components/app/forms/S3Upload.svelte +++ b/packages/client/src/components/app/forms/S3Upload.svelte @@ -80,12 +80,7 @@ const upload = async () => { loading = true try { - const res = await API.externalUpload({ - datasourceId, - bucket, - key, - data, - }) + const res = await API.externalUpload(datasourceId, bucket, key, data) notificationStore.actions.success("File uploaded successfully") loading = false return res diff --git a/packages/client/src/components/app/forms/SignatureField.svelte b/packages/client/src/components/app/forms/SignatureField.svelte index bdae148368..a7a7dc3206 100644 --- a/packages/client/src/components/app/forms/SignatureField.svelte +++ b/packages/client/src/components/app/forms/SignatureField.svelte @@ -31,10 +31,10 @@ let attachRequest = new FormData() attachRequest.append("file", signatureFile) - const resp = await API.uploadAttachment({ - data: attachRequest, - tableId: formContext?.dataSource?.tableId, - }) + const resp = await API.uploadAttachment( + formContext?.dataSource?.tableId, + attachRequest + ) const [signatureAttachment] = resp updateValue = signatureAttachment } else { diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index e2b0071042..63354d3119 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -454,12 +454,7 @@ const downloadFileHandler = async action => { const { type } = action.parameters if (type === "attachment") { const { tableId, rowId, attachmentColumn } = action.parameters - const res = await API.downloadAttachment( - tableId, - rowId, - attachmentColumn, - { suppressErrors: true } - ) + const res = await API.downloadAttachment(tableId, rowId, attachmentColumn) await downloadStream(res) return } diff --git a/packages/frontend-core/src/api/attachments.js b/packages/frontend-core/src/api/attachments.js deleted file mode 100644 index 72f280d99d..0000000000 --- a/packages/frontend-core/src/api/attachments.js +++ /dev/null @@ -1,78 +0,0 @@ -export const buildAttachmentEndpoints = API => { - /** - * Generates a signed URL to upload a file to an external datasource. - * @param datasourceId the ID of the datasource to upload to - * @param bucket the name of the bucket to upload to - * @param key the name of the file to upload to - */ - const getSignedDatasourceURL = async ({ datasourceId, bucket, key }) => { - return await API.post({ - url: `/api/attachments/${datasourceId}/url`, - body: { bucket, key }, - }) - } - - return { - getSignedDatasourceURL, - - /** - * Uploads an attachment to the server. - * @param data the attachment to upload - * @param tableId the table ID to upload to - */ - uploadAttachment: async ({ data, tableId }) => { - return await API.post({ - url: `/api/attachments/${tableId}/upload`, - body: data, - json: false, - }) - }, - - /** - * Uploads an attachment to the server as a builder user from the builder. - * @param data the data to upload - */ - uploadBuilderAttachment: async data => { - return await API.post({ - url: "/api/attachments/process", - body: data, - json: false, - }) - }, - - /** - * Uploads a file to an external datasource. - * @param datasourceId the ID of the datasource to upload to - * @param bucket the name of the bucket to upload to - * @param key the name of the file to upload to - * @param data the file to upload - */ - externalUpload: async ({ datasourceId, bucket, key, data }) => { - const { signedUrl, publicUrl } = await getSignedDatasourceURL({ - datasourceId, - bucket, - key, - }) - await API.put({ - url: signedUrl, - body: data, - json: false, - external: true, - }) - return { publicUrl } - }, - /** - * Download an attachment from a row given its column name. - * @param datasourceId the ID of the datasource to download from - * @param rowId the ID of the row to download from - * @param columnName the column name to download - */ - downloadAttachment: async (datasourceId, rowId, columnName, options) => { - return await API.get({ - url: `/api/${datasourceId}/rows/${rowId}/attachment/${columnName}`, - parseResponse: response => response, - suppressErrors: options?.suppressErrors, - }) - }, - } -} diff --git a/packages/frontend-core/src/api/attachments.ts b/packages/frontend-core/src/api/attachments.ts new file mode 100644 index 0000000000..a57e545111 --- /dev/null +++ b/packages/frontend-core/src/api/attachments.ts @@ -0,0 +1,109 @@ +import { ProcessAttachmentResponse } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface AttachmentEndpoints { + getSignedDatasourceURL: ( + datasourceId: string, + bucket: string, + key: string + ) => Promise<{ signedUrl: string; publicUrl: string }> + uploadAttachment: ( + tableId: string, + data: any + ) => Promise + uploadBuilderAttachment: (data: any) => Promise + externalUpload: ( + datasourceId: string, + bucket: string, + key: string, + data: any + ) => Promise<{ publicUrl: string }> + downloadAttachment: ( + datasourceId: string, + rowId: string, + columnName: string + ) => Promise +} + +export const buildAttachmentEndpoints = ( + API: BaseAPIClient +): AttachmentEndpoints => { + const endpoints: Pick = { + /** + * Generates a signed URL to upload a file to an external datasource. + * @param datasourceId the ID of the datasource to upload to + * @param bucket the name of the bucket to upload to + * @param key the name of the file to upload to + */ + getSignedDatasourceURL: async (datasourceId, bucket, key) => { + return await API.post({ + 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 + ) + await API.put({ + url: signedUrl, + body: data, + json: false, + external: true, + }) + return { publicUrl } + }, + /** + * Download an attachment from a row given its column name. + * @param datasourceId the ID of the datasource to download from + * @param rowId the ID of the row to download from + * @param columnName the column name to download + */ + downloadAttachment: async (datasourceId, rowId, columnName) => { + return await API.get({ + url: `/api/${datasourceId}/rows/${rowId}/attachment/${columnName}`, + parseResponse: response => response as any, + suppressErrors: true, + }) + }, + } +} diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 257e34d3a3..350deb4e90 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -27,7 +27,7 @@ export type APICallConfig = { suppressErrors: boolean cache: boolean body?: any - parseResponse?: (response: Response) => Promise + parseResponse?: (response: Response) => Promise | T } export type APICallParams = Pick & Partial From 544e3a9da46643dd010cabd4f468eb69ec77265c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 27 Nov 2024 10:37:37 +0000 Subject: [PATCH 009/103] Update audit logs endpoints to use TS --- packages/frontend-core/src/api/auditLogs.js | 63 ------------------- packages/frontend-core/src/api/auditLogs.ts | 70 +++++++++++++++++++++ packages/frontend-core/src/api/types.ts | 6 +- 3 files changed, 75 insertions(+), 64 deletions(-) delete mode 100644 packages/frontend-core/src/api/auditLogs.js create mode 100644 packages/frontend-core/src/api/auditLogs.ts diff --git a/packages/frontend-core/src/api/auditLogs.js b/packages/frontend-core/src/api/auditLogs.js deleted file mode 100644 index c4230df6d9..0000000000 --- a/packages/frontend-core/src/api/auditLogs.js +++ /dev/null @@ -1,63 +0,0 @@ -const buildOpts = ({ - bookmark, - userIds, - appIds, - startDate, - endDate, - fullSearch, - events, -}) => { - const opts = {} - - if (bookmark) { - opts.bookmark = bookmark - } - - if (startDate && endDate) { - opts.startDate = startDate - opts.endDate = endDate - } else if (startDate && !endDate) { - opts.startDate = startDate - } - - if (fullSearch) { - opts.fullSearch = fullSearch - } - - if (events.length) { - opts.events = events - } - - if (userIds.length) { - opts.userIds = userIds - } - - if (appIds.length) { - opts.appIds = appIds - } - - return opts -} - -export const buildAuditLogsEndpoints = API => ({ - /** - * Gets a list of users in the current tenant. - */ - searchAuditLogs: async opts => { - return await API.post({ - url: `/api/global/auditlogs/search`, - body: buildOpts(opts), - }) - }, - - getEventDefinitions: async () => { - return await API.get({ - url: `/api/global/auditlogs/definitions`, - }) - }, - - getDownloadUrl: opts => { - const query = encodeURIComponent(JSON.stringify(opts)) - return `/api/global/auditlogs/download?query=${query}` - }, -}) diff --git a/packages/frontend-core/src/api/auditLogs.ts b/packages/frontend-core/src/api/auditLogs.ts new file mode 100644 index 0000000000..eeb922f981 --- /dev/null +++ b/packages/frontend-core/src/api/auditLogs.ts @@ -0,0 +1,70 @@ +import { + SearchAuditLogsRequest, + SearchAuditLogsResponse, + DefinitionsAuditLogsResponse, + DownloadAuditLogsRequest, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface AuditLogsEndpoints { + searchAuditLogs: ( + opts: SearchAuditLogsRequest + ) => Promise + getEventDefinitions: () => Promise + getDownloadUrl: (opts: DownloadAuditLogsRequest) => string +} + +const buildOpts = (opts: SearchAuditLogsRequest) => { + const { bookmark, startDate, endDate, fullSearch, events, userIds, appIds } = + opts + const parsedOpts: SearchAuditLogsRequest = {} + + if (bookmark) { + parsedOpts.bookmark = bookmark + } + + if (opts.startDate && endDate) { + parsedOpts.startDate = startDate + parsedOpts.endDate = endDate + } else if (startDate && !endDate) { + parsedOpts.startDate = startDate + } + + if (fullSearch) { + parsedOpts.fullSearch = fullSearch + } + + if (events?.length) { + parsedOpts.events = events + } + + if (userIds?.length) { + parsedOpts.userIds = userIds + } + + if (appIds?.length) { + parsedOpts.appIds = appIds + } + + return parsedOpts +} + +export const buildAuditLogsEndpoints = ( + API: BaseAPIClient +): AuditLogsEndpoints => ({ + searchAuditLogs: async opts => { + return await API.post({ + url: `/api/global/auditlogs/search`, + body: buildOpts(opts), + }) + }, + getEventDefinitions: async () => { + return await API.get({ + url: `/api/global/auditlogs/definitions`, + }) + }, + getDownloadUrl: opts => { + const query = encodeURIComponent(JSON.stringify(opts)) + return `/api/global/auditlogs/download?query=${query}` + }, +}) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 350deb4e90..db8cb4578c 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -1,6 +1,8 @@ import { AIEndpoints } from "./ai" import { AnalyticsEndpoints } from "./analytics" import { AppEndpoints } from "./app" +import { AttachmentEndpoints } from "./attachments" +import { AuditLogsEndpoints } from "./auditLogs" export enum HTTPMethod { POST = "POST", @@ -46,4 +48,6 @@ export type BaseAPIClient = { export type APIClient = BaseAPIClient & AIEndpoints & AnalyticsEndpoints & - AppEndpoints & { [key: string]: any } + AppEndpoints & + AttachmentEndpoints & + AuditLogsEndpoints & { [key: string]: any } From 679f08dcc45749c7e8cfa6b067160db0ec0cd792 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 27 Nov 2024 11:02:16 +0000 Subject: [PATCH 010/103] Update auth endpoints to use TS --- packages/builder/src/stores/portal/auth.js | 17 ++--------- .../src/api/{auth.js => auth.ts} | 28 ++++++++++++++++--- packages/frontend-core/src/api/types.ts | 4 ++- 3 files changed, 30 insertions(+), 19 deletions(-) rename packages/frontend-core/src/api/{auth.js => auth.ts} (66%) diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index 2d2b455b37..71f4841053 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -113,11 +113,7 @@ export function createAuthStore() { }, login: async creds => { const tenantId = get(store).tenantId - await API.logIn({ - username: creds.username, - password: creds.password, - tenantId, - }) + await API.logIn(tenantId, creds.username, creds.password) await actions.getSelf() }, logout: async () => { @@ -138,18 +134,11 @@ export function createAuthStore() { }, forgotPassword: async email => { const tenantId = get(store).tenantId - await API.requestForgotPassword({ - tenantId, - email, - }) + await API.requestForgotPassword(tenantId, email) }, resetPassword: async (password, resetCode) => { const tenantId = get(store).tenantId - await API.resetPassword({ - tenantId, - password, - resetCode, - }) + await API.resetPassword(tenantId, password, resetCode) }, generateAPIKey: async () => { return API.generateAPIKey() diff --git a/packages/frontend-core/src/api/auth.js b/packages/frontend-core/src/api/auth.ts similarity index 66% rename from packages/frontend-core/src/api/auth.js rename to packages/frontend-core/src/api/auth.ts index 9289d71239..a6f8cc1fc9 100644 --- a/packages/frontend-core/src/api/auth.js +++ b/packages/frontend-core/src/api/auth.ts @@ -1,11 +1,31 @@ -export const buildAuthEndpoints = API => ({ +import { BaseAPIClient } from "./types" + +export interface AuthEndpoints { + logIn: (tenantId: string, username: string, password: string) => Promise + logOut: () => Promise<{ message: string }> + requestForgotPassword: ( + tenantId: string, + email: string + ) => Promise<{ message: string }> + resetPassword: ( + tenantId: string, + password: string, + resetCode: string + ) => Promise<{ message: string }> + + // TODO + setInitInfo: (info: any) => Promise + getInitInfo: () => Promise +} + +export const buildAuthEndpoints = (API: BaseAPIClient): AuthEndpoints => ({ /** * Performs a login request. * @param tenantId the ID of the tenant to log in to * @param username the username (email) * @param password the password */ - logIn: async ({ tenantId, username, password }) => { + logIn: async (tenantId, username, password) => { return await API.post({ url: `/api/global/auth/${tenantId}/login`, body: { @@ -49,7 +69,7 @@ export const buildAuthEndpoints = API => ({ * @param tenantId the ID of the tenant the user is in * @param email the email address of the user */ - requestForgotPassword: async ({ tenantId, email }) => { + requestForgotPassword: async (tenantId, email) => { return await API.post({ url: `/api/global/auth/${tenantId}/reset`, body: { @@ -64,7 +84,7 @@ export const buildAuthEndpoints = API => ({ * @param password the new password to set * @param resetCode the reset code to authenticate the request */ - resetPassword: async ({ tenantId, password, resetCode }) => { + resetPassword: async (tenantId, password, resetCode) => { return await API.post({ url: `/api/global/auth/${tenantId}/reset/update`, body: { diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index db8cb4578c..5df7f777c0 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -3,6 +3,7 @@ import { AnalyticsEndpoints } from "./analytics" import { AppEndpoints } from "./app" import { AttachmentEndpoints } from "./attachments" import { AuditLogsEndpoints } from "./auditLogs" +import { AuthEndpoints } from "./auth" export enum HTTPMethod { POST = "POST", @@ -50,4 +51,5 @@ export type APIClient = BaseAPIClient & AnalyticsEndpoints & AppEndpoints & AttachmentEndpoints & - AuditLogsEndpoints & { [key: string]: any } + AuditLogsEndpoints & + AuthEndpoints & { [key: string]: any } From 7ab6a1bbeca54b751de25a1acc42002714f22e58 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 27 Nov 2024 16:08:24 +0000 Subject: [PATCH 011/103] Update automation endpoints to use TS --- .../builder/src/stores/builder/automations.js | 15 ++---- .../api/{automations.js => automations.ts} | 52 +++++++++++++++++-- packages/frontend-core/src/api/types.ts | 4 +- packages/frontend-core/tsconfig.json | 8 +++ 4 files changed, 61 insertions(+), 18 deletions(-) rename packages/frontend-core/src/api/{automations.js => automations.ts} (64%) create mode 100644 packages/frontend-core/tsconfig.json diff --git a/packages/builder/src/stores/builder/automations.js b/packages/builder/src/stores/builder/automations.js index b7d55ee2d4..1d1a758c13 100644 --- a/packages/builder/src/stores/builder/automations.js +++ b/packages/builder/src/stores/builder/automations.js @@ -733,10 +733,7 @@ const automationActions = store => ({ automation.definition.trigger.inputs.rowActionId ) } else { - await API.deleteAutomation({ - automationId: automation?._id, - automationRev: automation?._rev, - }) + await API.deleteAutomation(automation?._id, automation?._rev) } store.update(state => { @@ -818,10 +815,7 @@ const automationActions = store => ({ test: async (automation, testData) => { let result try { - result = await API.testAutomation({ - automationId: automation?._id, - testData, - }) + result = await API.testAutomation(automation?._id, testData) } catch (err) { const message = err.message || err.status || JSON.stringify(err) throw `Automation test failed - ${message}` @@ -875,10 +869,7 @@ const automationActions = store => ({ }) }, clearLogErrors: async ({ automationId, appId } = {}) => { - return await API.clearAutomationLogErrors({ - automationId, - appId, - }) + return await API.clearAutomationLogErrors(automationId, appId) }, addTestDataToAutomation: async data => { let newAutomation = cloneDeep(get(selectedAutomation).data) diff --git a/packages/frontend-core/src/api/automations.js b/packages/frontend-core/src/api/automations.ts similarity index 64% rename from packages/frontend-core/src/api/automations.js rename to packages/frontend-core/src/api/automations.ts index 37a834cf04..a348dc52a7 100644 --- a/packages/frontend-core/src/api/automations.js +++ b/packages/frontend-core/src/api/automations.ts @@ -1,10 +1,52 @@ -export const buildAutomationEndpoints = API => ({ +import { + Automation, + AutomationLogPage, + DeleteAutomationResponse, + FetchAutomationResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface AutomationEndpoints { + getAutomations: () => Promise + createAutomation: ( + automation: Automation + ) => Promise<{ message: string; automation: Automation }> + updateAutomation: ( + automation: Automation + ) => Promise<{ message: string; automation: Automation }> + deleteAutomation: ( + automationId: string, + automationRev: string + ) => Promise + clearAutomationLogErrors: ( + automationId: string, + appId: string + ) => Promise<{ message: string }> + + // Missing request or response types + triggerAutomation: ( + automationId: string, + fields: Record, + timeout?: number + ) => Promise + testAutomation: ( + automationdId: string, + testData: Record + ) => Promise + getAutomationDefinitions: () => Promise + getAutomationLogs: (options: any) => Promise +} + +export const buildAutomationEndpoints = ( + API: BaseAPIClient +): AutomationEndpoints => ({ /** * Executes an automation. Must have "App Action" trigger. * @param automationId the ID of the automation to trigger * @param fields the fields to trigger the automation with + * @param timeout an optional timeout override */ - triggerAutomation: async ({ automationId, fields, timeout }) => { + triggerAutomation: async (automationId, fields, timeout) => { return await API.post({ url: `/api/automations/${automationId}/trigger`, body: { fields, timeout }, @@ -16,7 +58,7 @@ export const buildAutomationEndpoints = API => ({ * @param automationId the ID of the automation to test * @param testData the test data to run against the automation */ - testAutomation: async ({ automationId, testData }) => { + testAutomation: async (automationId, testData) => { return await API.post({ url: `/api/automations/${automationId}/test`, body: testData, @@ -68,7 +110,7 @@ export const buildAutomationEndpoints = API => ({ * @param automationId the ID of the automation to delete * @param automationRev the rev of the automation to delete */ - deleteAutomation: async ({ automationId, automationRev }) => { + deleteAutomation: async (automationId, automationRev) => { return await API.delete({ url: `/api/automations/${automationId}/${automationRev}`, }) @@ -99,7 +141,7 @@ export const buildAutomationEndpoints = API => ({ * @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 }) => { + clearAutomationLogErrors: async (automationId, appId) => { return await API.delete({ url: "/api/automations/logs", body: { diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 5df7f777c0..1e027b9157 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -4,6 +4,7 @@ import { AppEndpoints } from "./app" import { AttachmentEndpoints } from "./attachments" import { AuditLogsEndpoints } from "./auditLogs" import { AuthEndpoints } from "./auth" +import { AutomationEndpoints } from "./automations" export enum HTTPMethod { POST = "POST", @@ -52,4 +53,5 @@ export type APIClient = BaseAPIClient & AppEndpoints & AttachmentEndpoints & AuditLogsEndpoints & - AuthEndpoints & { [key: string]: any } + AuthEndpoints & + AutomationEndpoints & { [key: string]: any } diff --git a/packages/frontend-core/tsconfig.json b/packages/frontend-core/tsconfig.json new file mode 100644 index 0000000000..6e0cb00434 --- /dev/null +++ b/packages/frontend-core/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "paths": { + "@budibase/types": ["../types/src"], + "@budibase/shared-core": ["../shared-core/src"] + } + } +} From fd1998616e8fb7f1e89667051b3624671a5b790f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 27 Nov 2024 16:17:22 +0000 Subject: [PATCH 012/103] Update backups endpoints to use TS --- packages/frontend-core/src/api/backups.js | 46 --------------- packages/frontend-core/src/api/backups.ts | 70 +++++++++++++++++++++++ 2 files changed, 70 insertions(+), 46 deletions(-) delete mode 100644 packages/frontend-core/src/api/backups.js create mode 100644 packages/frontend-core/src/api/backups.ts diff --git a/packages/frontend-core/src/api/backups.js b/packages/frontend-core/src/api/backups.js deleted file mode 100644 index 40546b6f66..0000000000 --- a/packages/frontend-core/src/api/backups.js +++ /dev/null @@ -1,46 +0,0 @@ -export const buildBackupsEndpoints = API => ({ - searchBackups: async ({ appId, trigger, type, page, startDate, endDate }) => { - const opts = {} - if (page) { - opts.page = page - } - if (trigger && type) { - opts.trigger = trigger.toLowerCase() - opts.type = type.toLowerCase() - } - if (startDate && endDate) { - opts.startDate = startDate - opts.endDate = endDate - } - return await API.post({ - url: `/api/apps/${appId}/backups/search`, - body: opts, - }) - }, - - createManualBackup: async ({ appId }) => { - return await API.post({ - url: `/api/apps/${appId}/backups`, - }) - }, - - deleteBackup: async ({ appId, backupId }) => { - return await API.delete({ - url: `/api/apps/${appId}/backups/${backupId}`, - }) - }, - - updateBackup: async ({ appId, backupId, name }) => { - return await API.patch({ - url: `/api/apps/${appId}/backups/${backupId}`, - body: { name }, - }) - }, - - restoreBackup: async ({ appId, backupId, name }) => { - return await API.post({ - url: `/api/apps/${appId}/backups/${backupId}/import`, - body: { name }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/backups.ts b/packages/frontend-core/src/api/backups.ts new file mode 100644 index 0000000000..4010f17a74 --- /dev/null +++ b/packages/frontend-core/src/api/backups.ts @@ -0,0 +1,70 @@ +import { PutResponse } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface BackupsEndpoints { + createManualBackup: ( + appId: string + ) => Promise<{ backupId: string; message: string }> + deleteBackup: ( + appId: string, + backupId: string + ) => Promise<{ message: string }> + updateBackup: ( + appId: string, + backupId: string, + name: string + ) => Promise + restoreBackup: ( + appId: string, + backupId: string, + name: string + ) => Promise<{ restoreId: string; message: string }> + + // Missing request or response types + searchBackups: (opts: any) => Promise +} + +export const buildBackupsEndpoints = ( + API: BaseAPIClient +): BackupsEndpoints => ({ + searchBackups: async ({ appId, trigger, type, page, startDate, endDate }) => { + const opts: any = {} + 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 }, + }) + }, +}) From aeb0af6c881f5835b45b69e79546c143f0a3749c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 27 Nov 2024 16:21:05 +0000 Subject: [PATCH 013/103] Standardise plurality and comments for untyped endpoints --- packages/frontend-core/src/api/app.ts | 2 +- packages/frontend-core/src/api/auditLogs.ts | 6 +++--- packages/frontend-core/src/api/auth.ts | 2 +- packages/frontend-core/src/api/backups.ts | 6 ++---- packages/frontend-core/src/api/index.ts | 8 ++++---- packages/frontend-core/src/api/types.ts | 8 +++++--- packages/frontend-core/tsconfig.json | 3 ++- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/frontend-core/src/api/app.ts b/packages/frontend-core/src/api/app.ts index 12e1500cfe..b41c323785 100644 --- a/packages/frontend-core/src/api/app.ts +++ b/packages/frontend-core/src/api/app.ts @@ -40,7 +40,7 @@ export interface AppEndpoints { ) => Promise addSampleData: (appId: string) => Promise - // TODO + // Missing request or response types publishAppChanges: (appId: string) => Promise revertAppChanges: (appId: string) => Promise updateAppClientVersion: (appId: string) => Promise diff --git a/packages/frontend-core/src/api/auditLogs.ts b/packages/frontend-core/src/api/auditLogs.ts index eeb922f981..60caca0c85 100644 --- a/packages/frontend-core/src/api/auditLogs.ts +++ b/packages/frontend-core/src/api/auditLogs.ts @@ -6,7 +6,7 @@ import { } from "@budibase/types" import { BaseAPIClient } from "./types" -export interface AuditLogsEndpoints { +export interface AuditLogEndpoints { searchAuditLogs: ( opts: SearchAuditLogsRequest ) => Promise @@ -49,9 +49,9 @@ const buildOpts = (opts: SearchAuditLogsRequest) => { return parsedOpts } -export const buildAuditLogsEndpoints = ( +export const buildAuditLogEndpoints = ( API: BaseAPIClient -): AuditLogsEndpoints => ({ +): AuditLogEndpoints => ({ searchAuditLogs: async opts => { return await API.post({ url: `/api/global/auditlogs/search`, diff --git a/packages/frontend-core/src/api/auth.ts b/packages/frontend-core/src/api/auth.ts index a6f8cc1fc9..d955af9bd5 100644 --- a/packages/frontend-core/src/api/auth.ts +++ b/packages/frontend-core/src/api/auth.ts @@ -13,7 +13,7 @@ export interface AuthEndpoints { resetCode: string ) => Promise<{ message: string }> - // TODO + // Missing request or response types setInitInfo: (info: any) => Promise getInitInfo: () => Promise } diff --git a/packages/frontend-core/src/api/backups.ts b/packages/frontend-core/src/api/backups.ts index 4010f17a74..98ef0698ff 100644 --- a/packages/frontend-core/src/api/backups.ts +++ b/packages/frontend-core/src/api/backups.ts @@ -1,7 +1,7 @@ import { PutResponse } from "@budibase/types" import { BaseAPIClient } from "./types" -export interface BackupsEndpoints { +export interface BackupEndpoints { createManualBackup: ( appId: string ) => Promise<{ backupId: string; message: string }> @@ -24,9 +24,7 @@ export interface BackupsEndpoints { searchBackups: (opts: any) => Promise } -export const buildBackupsEndpoints = ( - API: BaseAPIClient -): BackupsEndpoints => ({ +export const buildBackupEndpoints = (API: BaseAPIClient): BackupEndpoints => ({ searchBackups: async ({ appId, trigger, type, page, startDate, endDate }) => { const opts: any = {} if (page) { diff --git a/packages/frontend-core/src/api/index.ts b/packages/frontend-core/src/api/index.ts index 670d2cb19f..8778c5be4a 100644 --- a/packages/frontend-core/src/api/index.ts +++ b/packages/frontend-core/src/api/index.ts @@ -38,10 +38,10 @@ import { buildViewV2Endpoints } from "./viewsV2" import { buildLicensingEndpoints } from "./licensing" import { buildGroupsEndpoints } from "./groups" import { buildPluginEndpoints } from "./plugins" -import { buildBackupsEndpoints } from "./backups" +import { buildBackupEndpoints } from "./backups" import { buildEnvironmentVariableEndpoints } from "./environmentVariables" import { buildEventEndpoints } from "./events" -import { buildAuditLogsEndpoints } from "./auditLogs" +import { buildAuditLogEndpoints } from "./auditLogs" import { buildLogsEndpoints } from "./logs" import { buildMigrationEndpoints } from "./migrations" import { buildRowActionEndpoints } from "./rowActions" @@ -274,10 +274,10 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { ...buildLicensingEndpoints(API), ...buildGroupsEndpoints(API), ...buildPluginEndpoints(API), - ...buildBackupsEndpoints(API), + ...buildBackupEndpoints(API), ...buildEnvironmentVariableEndpoints(API), ...buildEventEndpoints(API), - ...buildAuditLogsEndpoints(API), + ...buildAuditLogEndpoints(API), ...buildLogsEndpoints(API), ...buildMigrationEndpoints(API), viewV2: buildViewV2Endpoints(API), diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 1e027b9157..9277d9097a 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -2,9 +2,10 @@ import { AIEndpoints } from "./ai" import { AnalyticsEndpoints } from "./analytics" import { AppEndpoints } from "./app" import { AttachmentEndpoints } from "./attachments" -import { AuditLogsEndpoints } from "./auditLogs" +import { AuditLogEndpoints } from "./auditLogs" import { AuthEndpoints } from "./auth" import { AutomationEndpoints } from "./automations" +import { BackupEndpoints } from "./backups" export enum HTTPMethod { POST = "POST", @@ -52,6 +53,7 @@ export type APIClient = BaseAPIClient & AnalyticsEndpoints & AppEndpoints & AttachmentEndpoints & - AuditLogsEndpoints & + AuditLogEndpoints & AuthEndpoints & - AutomationEndpoints & { [key: string]: any } + AutomationEndpoints & + BackupEndpoints & { [key: string]: any } diff --git a/packages/frontend-core/tsconfig.json b/packages/frontend-core/tsconfig.json index 6e0cb00434..d4d446521a 100644 --- a/packages/frontend-core/tsconfig.json +++ b/packages/frontend-core/tsconfig.json @@ -2,7 +2,8 @@ "compilerOptions": { "paths": { "@budibase/types": ["../types/src"], - "@budibase/shared-core": ["../shared-core/src"] + "@budibase/shared-core": ["../shared-core/src"], + "@budibase/bbui": ["../bbui/src"] } } } From c9daead1b1254fa3a314385b9e719cc6210aa21a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 27 Nov 2024 16:23:51 +0000 Subject: [PATCH 014/103] Update frontend-core constants to TS --- packages/frontend-core/src/{constants.js => constants.ts} | 4 ++-- packages/frontend-core/tsconfig.json | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) rename packages/frontend-core/src/{constants.js => constants.ts} (98%) diff --git a/packages/frontend-core/src/constants.js b/packages/frontend-core/src/constants.ts similarity index 98% rename from packages/frontend-core/src/constants.js rename to packages/frontend-core/src/constants.ts index 662b086915..8a39e8c106 100644 --- a/packages/frontend-core/src/constants.js +++ b/packages/frontend-core/src/constants.ts @@ -76,9 +76,9 @@ export const ExtendedBudibaseRoleOptions = [ value: BudibaseRoles.Owner, sortOrder: 0, }, + ...BudibaseRoleOptions, + ...BudibaseRoleOptionsOld, ] - .concat(BudibaseRoleOptions) - .concat(BudibaseRoleOptionsOld) export const PlanType = { FREE: "free", diff --git a/packages/frontend-core/tsconfig.json b/packages/frontend-core/tsconfig.json index d4d446521a..506c8dfd5a 100644 --- a/packages/frontend-core/tsconfig.json +++ b/packages/frontend-core/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "target": "ESNext", + "moduleResolution": "bundler", "paths": { "@budibase/types": ["../types/src"], "@budibase/shared-core": ["../shared-core/src"], From 152cc3811e741027cb8a82c904b9da4dee50fce3 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 29 Nov 2024 14:14:24 +0000 Subject: [PATCH 015/103] Fix import of frontend-core constants from client --- packages/client/src/utils/blocks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/utils/blocks.js b/packages/client/src/utils/blocks.js index f9452e47a9..88cf4b095b 100644 --- a/packages/client/src/utils/blocks.js +++ b/packages/client/src/utils/blocks.js @@ -1,7 +1,7 @@ import { makePropSafe as safe } from "@budibase/string-templates" import { API } from "../api/index.js" import { UILogicalOperator } from "@budibase/types" -import { OnEmptyFilter } from "@budibase/frontend-core/src/constants.js" +import { Constants } from "@budibase/frontend-core" // Map of data types to component types for search fields inside blocks const schemaComponentMap = { @@ -108,7 +108,7 @@ export const enrichFilter = (filter, columns, formId) => { return { logicalOperator: UILogicalOperator.ALL, - onEmptyFilter: OnEmptyFilter.RETURN_ALL, + onEmptyFilter: Constants.OnEmptyFilter.RETURN_ALL, groups: [ ...(filter?.groups || []), { From 07afd92b1641b193214011d18b66ae6b14353d7a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 29 Nov 2024 14:14:52 +0000 Subject: [PATCH 016/103] Resolve frontend-core to local path from server and update nodemon to only watch src dirs --- packages/server/nodemon.json | 18 ++++++++++++------ packages/server/tsconfig.build.json | 4 +++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/server/nodemon.json b/packages/server/nodemon.json index b0e792d945..ac8c38ccb2 100644 --- a/packages/server/nodemon.json +++ b/packages/server/nodemon.json @@ -1,13 +1,19 @@ { "watch": [ "src", - "../backend-core", - "../pro", - "../types", - "../shared-core", - "../string-templates" + "../backend-core/src", + "../pro/src", + "../types/src", + "../shared-core/src", + "../string-templates/src" ], "ext": "js,ts,json,svelte", - "ignore": ["**/*.spec.ts", "**/*.spec.js", "../*/dist/**/*", "client/**/*", "builder/**/*"], + "ignore": [ + "**/*.spec.ts", + "**/*.spec.js", + "../*/dist/**/*", + "client/**/*", + "builder/**/*" + ], "exec": "yarn build && node --no-node-snapshot ./dist/index.js" } diff --git a/packages/server/tsconfig.build.json b/packages/server/tsconfig.build.json index c4b9b8788e..4b99e298d5 100644 --- a/packages/server/tsconfig.build.json +++ b/packages/server/tsconfig.build.json @@ -19,7 +19,9 @@ "@budibase/shared-core": ["../shared-core/src"], "@budibase/pro": ["../pro/src"], "@budibase/string-templates": ["../string-templates/src"], - "@budibase/string-templates/*": ["../string-templates/*"] + "@budibase/string-templates/*": ["../string-templates/*"], + "@budibase/frontend-core": ["../frontend-core"], + "@budibase/frontend-core/*": ["../frontend-core/*"] }, "allowArbitraryExtensions": true }, From 6d67a10b34a671ffc7224a71cbdafd0da81282bb Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 29 Nov 2024 14:33:47 +0000 Subject: [PATCH 017/103] Convert config endpoints to TS --- .../src/api/{configs.js => configs.ts} | 35 +++++++++++++++++-- packages/frontend-core/src/api/types.ts | 4 ++- 2 files changed, 35 insertions(+), 4 deletions(-) rename packages/frontend-core/src/api/{configs.js => configs.ts} (67%) diff --git a/packages/frontend-core/src/api/configs.js b/packages/frontend-core/src/api/configs.ts similarity index 67% rename from packages/frontend-core/src/api/configs.js rename to packages/frontend-core/src/api/configs.ts index 8d0d176a55..75f4cc66ec 100644 --- a/packages/frontend-core/src/api/configs.js +++ b/packages/frontend-core/src/api/configs.ts @@ -1,4 +1,33 @@ -export const buildConfigEndpoints = API => ({ +import { + Config, + ConfigType, + GetPublicOIDCConfigResponse, + GetPublicSettingsResponse, + OIDCLogosConfig, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface ConfigEndpoints { + saveConfig: ( + config: Config + ) => Promise<{ type: ConfigType; _id: string; _rev: string }> + getConfig: (type: ConfigType) => Promise> + deleteConfig: (id: string, rev: string) => Promise<{ message: string }> + getTenantConfig: (tentantId: string) => Promise + getOIDCConfig: (tenantId: string) => Promise + getOIDCLogos: () => Promise> + + // Missing request or response types + getChecklist: (tenantId: string) => Promise + uploadLogo: (data: any) => Promise<{ message: string; url: string }> + uploadFavicon: (data: any) => Promise<{ message: string; url: string }> + uploadOIDCLogo: ( + name: string, + data: any + ) => Promise<{ message: string; url: string }> +} + +export const buildConfigEndpoints = (API: BaseAPIClient): ConfigEndpoints => ({ /** * Saves a global config. * @param config the config to save @@ -25,7 +54,7 @@ export const buildConfigEndpoints = API => ({ * @param id the id of the config to delete * @param rev the revision of the config to delete */ - deleteConfig: async ({ id, rev }) => { + deleteConfig: async (id, rev) => { return await API.delete({ url: `/api/global/configs/${id}/${rev}`, }) @@ -90,7 +119,7 @@ export const buildConfigEndpoints = API => ({ * @param name the name of the OIDC provider * @param data the logo form data to upload */ - uploadOIDCLogo: async ({ name, data }) => { + uploadOIDCLogo: async (name, data) => { return await API.post({ url: `/api/global/configs/upload/logos_oidc/${name}`, body: data, diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 9277d9097a..acc3586f88 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -6,6 +6,7 @@ import { AuditLogEndpoints } from "./auditLogs" import { AuthEndpoints } from "./auth" import { AutomationEndpoints } from "./automations" import { BackupEndpoints } from "./backups" +import { ConfigEndpoints } from "./configs" export enum HTTPMethod { POST = "POST", @@ -56,4 +57,5 @@ export type APIClient = BaseAPIClient & AuditLogEndpoints & AuthEndpoints & AutomationEndpoints & - BackupEndpoints & { [key: string]: any } + BackupEndpoints & + ConfigEndpoints & { [key: string]: any } From 6d50f70258300289c8b2ab5ae2291c654a3dfc46 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 29 Nov 2024 14:36:16 +0000 Subject: [PATCH 018/103] Flatten some config endpoint params --- .../src/pages/builder/portal/settings/auth/index.svelte | 5 +---- .../src/pages/builder/portal/settings/email/index.svelte | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/builder/src/pages/builder/portal/settings/auth/index.svelte b/packages/builder/src/pages/builder/portal/settings/auth/index.svelte index 3ef3737249..e90cd5440e 100644 --- a/packages/builder/src/pages/builder/portal/settings/auth/index.svelte +++ b/packages/builder/src/pages/builder/portal/settings/auth/index.svelte @@ -140,10 +140,7 @@ if (image) { let data = new FormData() data.append("file", image) - await API.uploadOIDCLogo({ - name: image.name, - data, - }) + await API.uploadOIDCLogo(image.name, data) } } diff --git a/packages/builder/src/pages/builder/portal/settings/email/index.svelte b/packages/builder/src/pages/builder/portal/settings/email/index.svelte index 71687bcc70..0b9fb44bac 100644 --- a/packages/builder/src/pages/builder/portal/settings/email/index.svelte +++ b/packages/builder/src/pages/builder/portal/settings/email/index.svelte @@ -69,10 +69,7 @@ async function deleteSmtp() { // Delete the SMTP config try { - await API.deleteConfig({ - id: smtpConfig._id, - rev: smtpConfig._rev, - }) + await API.deleteConfig(smtpConfig._id, smtpConfig._rev) smtpConfig = { type: ConfigTypes.SMTP, config: { From 417040f2d59c2a7245d7580c40dc25d1e7d7f6e4 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Dec 2024 09:49:12 +0000 Subject: [PATCH 019/103] Add optional stronger typing of requests and responses to ensure conformity of flattened function params --- packages/frontend-core/src/api/datasources.js | 92 ------------- packages/frontend-core/src/api/datasources.ts | 125 ++++++++++++++++++ packages/frontend-core/src/api/index.ts | 29 ++-- packages/frontend-core/src/api/types.ts | 39 ++++-- 4 files changed, 171 insertions(+), 114 deletions(-) delete mode 100644 packages/frontend-core/src/api/datasources.js create mode 100644 packages/frontend-core/src/api/datasources.ts diff --git a/packages/frontend-core/src/api/datasources.js b/packages/frontend-core/src/api/datasources.js deleted file mode 100644 index 7cc05960d7..0000000000 --- a/packages/frontend-core/src/api/datasources.js +++ /dev/null @@ -1,92 +0,0 @@ -export const buildDatasourceEndpoints = API => ({ - /** - * Gets a list of datasources. - */ - getDatasources: async () => { - return await API.get({ - url: "/api/datasources", - }) - }, - - /** - * Prompts the server to build the schema for a datasource. - * @param datasourceId the datasource ID to build the schema for - * @param tablesFilter list of specific table names to be build the schema - */ - buildDatasourceSchema: async ({ datasourceId, tablesFilter }) => { - return await API.post({ - url: `/api/datasources/${datasourceId}/schema`, - body: { - tablesFilter, - }, - }) - }, - - /** - * Creates a datasource - * @param datasource the datasource to create - * @param fetchSchema whether to fetch the schema or not - * @param tablesFilter a list of tables to actually fetch rather than simply - * all that are accessible. - */ - createDatasource: async ({ datasource, fetchSchema, tablesFilter }) => { - return await API.post({ - url: "/api/datasources", - body: { - datasource, - fetchSchema, - tablesFilter, - }, - }) - }, - - /** - * Updates a datasource - * @param datasource the datasource to update - */ - updateDatasource: async datasource => { - return await API.put({ - url: `/api/datasources/${datasource._id}`, - body: datasource, - }) - }, - - /** - * Deletes a datasource. - * @param datasourceId the ID of the ddtasource to delete - * @param datasourceRev the rev of the datasource to delete - */ - deleteDatasource: async ({ datasourceId, datasourceRev }) => { - return await API.delete({ - url: `/api/datasources/${datasourceId}/${datasourceRev}`, - }) - }, - - /** - * Validate a datasource configuration - * @param datasource the datasource configuration to validate - */ - validateDatasource: async datasource => { - return await API.post({ - url: `/api/datasources/verify`, - body: { datasource }, - }) - }, - - /** - * Fetch table names available within the datasource, for filtering out undesired tables - * @param datasource the datasource configuration to use for fetching tables - */ - fetchInfoForDatasource: async datasource => { - return await API.post({ - url: `/api/datasources/info`, - body: { datasource }, - }) - }, - - fetchExternalSchema: async datasourceId => { - return await API.get({ - url: `/api/datasources/${datasourceId}/schema/external`, - }) - }, -}) diff --git a/packages/frontend-core/src/api/datasources.ts b/packages/frontend-core/src/api/datasources.ts new file mode 100644 index 0000000000..ef738fc9ff --- /dev/null +++ b/packages/frontend-core/src/api/datasources.ts @@ -0,0 +1,125 @@ +import { + BuildSchemaFromSourceRequest, + BuildSchemaFromSourceResponse, + CreateDatasourceRequest, + CreateDatasourceResponse, + Datasource, + FetchDatasourceInfoRequest, + FetchDatasourceInfoResponse, + UpdateDatasourceRequest, + UpdateDatasourceResponse, + VerifyDatasourceRequest, + VerifyDatasourceResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface DatasourceEndpoints { + getDatasources: () => Promise + buildDatasourceSchema: ( + datasourceId: string, + tablesFilter?: BuildSchemaFromSourceRequest["tablesFilter"] + ) => Promise + createDatasource: ( + request: CreateDatasourceRequest + ) => Promise + updateDatasource: ( + datasource: Datasource + ) => Promise + deleteDatasource: (id: string, rev: string) => Promise + validateDatasource: ( + datasource: Datasource + ) => Promise + fetchInfoForDatasource: ( + datasource: Datasource + ) => Promise + fetchExternalSchema: (datasourceId: string) => Promise<{ schema: string }> +} + +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 request => { + return await API.post({ + url: "/api/datasources", + body: request, + }) + }, + + /** + * Updates a datasource + */ + updateDatasource: async datasource => { + return await API.put({ + url: `/api/datasources/${datasource._id}`, + body: datasource, + }) + }, + + /** + * Deletes a datasource. + */ + deleteDatasource: async (id: string, rev: string) => { + return await API.delete({ + url: `/api/datasources/${id}/${rev}`, + }) + }, + + /** + * Validate a datasource configuration + */ + validateDatasource: async (datasource: Datasource) => { + return await API.post({ + url: `/api/datasources/verify`, + body: { datasource }, + }) + }, + + /** + * Fetch table names available within the datasource, for filtering out undesired tables + */ + fetchInfoForDatasource: async (datasource: Datasource) => { + return await API.post< + FetchDatasourceInfoRequest, + FetchDatasourceInfoResponse + >({ + url: `/api/datasources/info`, + body: { datasource }, + }) + }, + + /** + * Fetches the external schema of a datasource + */ + fetchExternalSchema: async (datasourceId: string) => { + return await API.get({ + url: `/api/datasources/${datasourceId}/schema/external`, + }) + }, +}) diff --git a/packages/frontend-core/src/api/index.ts b/packages/frontend-core/src/api/index.ts index 8778c5be4a..b7951ac227 100644 --- a/packages/frontend-core/src/api/index.ts +++ b/packages/frontend-core/src/api/index.ts @@ -103,7 +103,9 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { } // Performs an API call to the server. - const makeApiCall = async (callConfig: APICallConfig): Promise => { + const makeApiCall = async ( + callConfig: APICallConfig + ): Promise => { let { json, method, external, body, url, parseResponse, suppressErrors } = callConfig @@ -124,7 +126,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { } // Build request body - let requestBody = body + let requestBody: RequestT | string = body if (json) { try { requestBody = JSON.stringify(body) @@ -139,7 +141,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { response = await fetch(url, { method, headers, - body: requestBody, + body: requestBody as any, credentials: "same-origin", }) } catch (error) { @@ -152,9 +154,9 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { handleMigrations(response) try { if (parseResponse) { - return await parseResponse(response) + return await parseResponse(response) } else { - return (await response.json()) as T + return (await response.json()) as ResponseT } } catch (error) { delete cache[url] @@ -180,28 +182,31 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { // Performs an API call to the server and caches the response. // Future invocation for this URL will return the cached result instead of // hitting the server again. - const makeCachedApiCall = async ( - callConfig: APICallConfig - ): Promise => { + const makeCachedApiCall = async ( + callConfig: APICallConfig + ): Promise => { const identifier = callConfig.url if (!cache[identifier]) { cache[identifier] = makeApiCall(callConfig) cache[identifier] = await cache[identifier] } - return (await cache[identifier]) as T + return (await cache[identifier]) as ResponseT } // Constructs an API call function for a particular HTTP method const requestApiCall = (method: HTTPMethod) => - async (params: APICallParams): Promise => { + async ( + params: APICallParams + ): Promise => { try { - let callConfig: APICallConfig = { + let callConfig: APICallConfig = { json: true, external: false, suppressErrors: false, cache: false, method, + body: params.body, ...params, } let { url, cache, external } = callConfig @@ -212,7 +217,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { // Cache the request if possible and desired const cacheRequest = cache && config?.enableCaching const handler = cacheRequest ? makeCachedApiCall : makeApiCall - return await handler(callConfig) + return await handler(callConfig) } catch (error) { if (config?.onError) { config.onError(error) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index acc3586f88..49aa80646f 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -7,6 +7,7 @@ import { AuthEndpoints } from "./auth" import { AutomationEndpoints } from "./automations" import { BackupEndpoints } from "./backups" import { ConfigEndpoints } from "./configs" +import { DatasourceEndpoints } from "./datasources" export enum HTTPMethod { POST = "POST", @@ -25,25 +26,42 @@ export type APIClientConfig = { onMigrationDetected?: (migration: string) => void } -export type APICallConfig = { +export type APICallConfig = { method: HTTPMethod url: string + body: RequestT json: boolean external: boolean suppressErrors: boolean cache: boolean - body?: any - parseResponse?: (response: Response) => Promise | T + parseResponse?: (response: Response) => Promise | ResponseT } -export type APICallParams = Pick & Partial +export type APICallParams< + RequestT = null, + ResponseT = void +> = RequestT extends null + ? Pick, "url"> & + Partial> + : Pick, "url" | "body"> & + Partial> export type BaseAPIClient = { - post: (params: APICallParams) => Promise - get: (params: APICallParams) => Promise - put: (params: APICallParams) => Promise - delete: (params: APICallParams) => Promise - patch: (params: APICallParams) => Promise + post: ( + params: APICallParams + ) => Promise + get: ( + params: APICallParams + ) => Promise + put: ( + params: APICallParams + ) => Promise + delete: ( + params: APICallParams + ) => Promise + patch: ( + params: APICallParams + ) => Promise error: (message: string) => void invalidateCache: () => void getAppID: () => string @@ -58,4 +76,5 @@ export type APIClient = BaseAPIClient & AuthEndpoints & AutomationEndpoints & BackupEndpoints & - ConfigEndpoints & { [key: string]: any } + ConfigEndpoints & + DatasourceEndpoints & { [key: string]: any } From 00359d8e06b11d27c4035f56094822f8b9f18d54 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Dec 2024 10:02:30 +0000 Subject: [PATCH 020/103] Strengthen types of some endpoints --- packages/frontend-core/src/api/ai.ts | 2 +- packages/frontend-core/src/api/analytics.ts | 21 +++++++++++---------- packages/frontend-core/src/api/app.ts | 2 +- packages/frontend-core/src/api/auth.ts | 11 ++++++++--- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/frontend-core/src/api/ai.ts b/packages/frontend-core/src/api/ai.ts index c6adb45b3c..3160273301 100644 --- a/packages/frontend-core/src/api/ai.ts +++ b/packages/frontend-core/src/api/ai.ts @@ -8,7 +8,7 @@ export const buildAIEndpoints = (API: BaseAPIClient): AIEndpoints => ({ /** * Generates a cron expression from a prompt */ - generateCronExpression: async (prompt: string) => { + generateCronExpression: async prompt => { return await API.post({ url: "/api/ai/cron", body: { prompt }, diff --git a/packages/frontend-core/src/api/analytics.ts b/packages/frontend-core/src/api/analytics.ts index fb97599b6e..efb70574e2 100644 --- a/packages/frontend-core/src/api/analytics.ts +++ b/packages/frontend-core/src/api/analytics.ts @@ -1,13 +1,11 @@ import { BaseAPIClient } from "./types" - -type AnalyticsPingRequest = { - source?: string - embedded?: boolean -} +import { AnalyticsPingRequest } from "@budibase/types" export interface AnalyticsEndpoints { getAnalyticsStatus: () => Promise<{ enabled: boolean }> - analyticsPing: (payload: AnalyticsPingRequest) => Promise + analyticsPing: ( + payload: Omit + ) => Promise } export const buildAnalyticsEndpoints = ( @@ -21,14 +19,17 @@ export const buildAnalyticsEndpoints = ( url: "/api/bbtel", }) }, + /** * Notifies analytics of a certain environment */ - analyticsPing: async (payload: AnalyticsPingRequest) => { - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone - return await API.post({ + analyticsPing: async request => { + return await API.post({ url: "/api/bbtel/ping", - body: { source: payload.source, embedded: payload.embedded, timezone }, + body: { + ...request, + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + }, }) }, }) diff --git a/packages/frontend-core/src/api/app.ts b/packages/frontend-core/src/api/app.ts index b41c323785..52847ae0d7 100644 --- a/packages/frontend-core/src/api/app.ts +++ b/packages/frontend-core/src/api/app.ts @@ -234,7 +234,7 @@ export const buildAppEndpoints = (API: BaseAPIClient): AppEndpoints => ({ /** * Fetches the definitions for component library components. This includes * their props and other metadata from components.json. - * @param {string} appId - ID of the currently running app + * @param appId ID of the currently running app */ fetchComponentLibDefinitions: async appId => { return await API.get({ diff --git a/packages/frontend-core/src/api/auth.ts b/packages/frontend-core/src/api/auth.ts index d955af9bd5..406e2ef53b 100644 --- a/packages/frontend-core/src/api/auth.ts +++ b/packages/frontend-core/src/api/auth.ts @@ -1,3 +1,8 @@ +import { + LoginRequest, + PasswordResetRequest, + PasswordResetUpdateRequest, +} from "@budibase/types" import { BaseAPIClient } from "./types" export interface AuthEndpoints { @@ -26,7 +31,7 @@ export const buildAuthEndpoints = (API: BaseAPIClient): AuthEndpoints => ({ * @param password the password */ logIn: async (tenantId, username, password) => { - return await API.post({ + return await API.post({ url: `/api/global/auth/${tenantId}/login`, body: { username, @@ -70,7 +75,7 @@ export const buildAuthEndpoints = (API: BaseAPIClient): AuthEndpoints => ({ * @param email the email address of the user */ requestForgotPassword: async (tenantId, email) => { - return await API.post({ + return await API.post({ url: `/api/global/auth/${tenantId}/reset`, body: { email, @@ -85,7 +90,7 @@ export const buildAuthEndpoints = (API: BaseAPIClient): AuthEndpoints => ({ * @param resetCode the reset code to authenticate the request */ resetPassword: async (tenantId, password, resetCode) => { - return await API.post({ + return await API.post({ url: `/api/global/auth/${tenantId}/reset/update`, body: { password, From dedd5f273a41b7cc0d5de83ea934c4073e90c69f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Dec 2024 15:17:04 +0000 Subject: [PATCH 021/103] Type env variable endpoints --- packages/frontend-core/src/api/datasources.ts | 6 +-- .../src/api/environmentVariables.js | 36 ------------- .../src/api/environmentVariables.ts | 53 +++++++++++++++++++ packages/frontend-core/src/api/types.ts | 4 +- 4 files changed, 59 insertions(+), 40 deletions(-) delete mode 100644 packages/frontend-core/src/api/environmentVariables.js create mode 100644 packages/frontend-core/src/api/environmentVariables.ts diff --git a/packages/frontend-core/src/api/datasources.ts b/packages/frontend-core/src/api/datasources.ts index ef738fc9ff..5f0098b1eb 100644 --- a/packages/frontend-core/src/api/datasources.ts +++ b/packages/frontend-core/src/api/datasources.ts @@ -20,7 +20,7 @@ export interface DatasourceEndpoints { tablesFilter?: BuildSchemaFromSourceRequest["tablesFilter"] ) => Promise createDatasource: ( - request: CreateDatasourceRequest + data: CreateDatasourceRequest ) => Promise updateDatasource: ( datasource: Datasource @@ -65,10 +65,10 @@ export const buildDatasourceEndpoints = ( /** * Creates a datasource */ - createDatasource: async request => { + createDatasource: async data => { return await API.post({ url: "/api/datasources", - body: request, + body: data, }) }, diff --git a/packages/frontend-core/src/api/environmentVariables.js b/packages/frontend-core/src/api/environmentVariables.js deleted file mode 100644 index badd93ad69..0000000000 --- a/packages/frontend-core/src/api/environmentVariables.js +++ /dev/null @@ -1,36 +0,0 @@ -export const buildEnvironmentVariableEndpoints = API => ({ - checkEnvironmentVariableStatus: async () => { - return await API.get({ - url: `/api/env/variables/status`, - }) - }, - - /** - * Fetches a list of environment variables - */ - fetchEnvironmentVariables: async () => { - return await API.get({ - url: `/api/env/variables`, - json: false, - }) - }, - - createEnvironmentVariable: async data => { - return await API.post({ - url: `/api/env/variables`, - body: data, - }) - }, - deleteEnvironmentVariable: async varName => { - return await API.delete({ - url: `/api/env/variables/${varName}`, - }) - }, - - updateEnvironmentVariable: async data => { - return await API.patch({ - url: `/api/env/variables/${data.name}`, - body: data, - }) - }, -}) diff --git a/packages/frontend-core/src/api/environmentVariables.ts b/packages/frontend-core/src/api/environmentVariables.ts new file mode 100644 index 0000000000..ed7a284a4d --- /dev/null +++ b/packages/frontend-core/src/api/environmentVariables.ts @@ -0,0 +1,53 @@ +import { + CreateEnvironmentVariableRequest, + GetEnvironmentVariablesResponse, + StatusEnvironmentVariableResponse, + UpdateEnvironmentVariableRequest, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface EnvironmentVariableEndpoints { + checkEnvironmentVariableStatus: () => Promise + fetchEnvironmentVariables: () => Promise + createEnvironmentVariable: ( + data: CreateEnvironmentVariableRequest + ) => Promise + deleteEnvironmentVariable: (name: string) => Promise + updateEnvironmentVariable: ( + name: string, + data: UpdateEnvironmentVariableRequest + ) => Promise +} + +export const buildEnvironmentVariableEndpoints = ( + API: BaseAPIClient +): EnvironmentVariableEndpoints => ({ + checkEnvironmentVariableStatus: async () => { + return await API.get({ + url: `/api/env/variables/status`, + }) + }, + fetchEnvironmentVariables: async () => { + return await API.get({ + url: `/api/env/variables`, + json: false, + }) + }, + createEnvironmentVariable: async data => { + return await API.post({ + url: `/api/env/variables`, + body: data, + }) + }, + deleteEnvironmentVariable: async name => { + return await API.delete({ + url: `/api/env/variables/${name}`, + }) + }, + updateEnvironmentVariable: async (name, data) => { + return await API.patch({ + url: `/api/env/variables/${name}`, + body: data, + }) + }, +}) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 49aa80646f..192576731c 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -8,6 +8,7 @@ import { AutomationEndpoints } from "./automations" import { BackupEndpoints } from "./backups" import { ConfigEndpoints } from "./configs" import { DatasourceEndpoints } from "./datasources" +import { EnvironmentVariableEndpoints } from "./environmentVariables" export enum HTTPMethod { POST = "POST", @@ -77,4 +78,5 @@ export type APIClient = BaseAPIClient & AutomationEndpoints & BackupEndpoints & ConfigEndpoints & - DatasourceEndpoints & { [key: string]: any } + DatasourceEndpoints & + EnvironmentVariableEndpoints From 417286b16d24f1e00cce26be57e74af115334e47 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Dec 2024 15:19:33 +0000 Subject: [PATCH 022/103] Type event endpoints --- packages/frontend-core/src/api/events.js | 13 ------------- packages/frontend-core/src/api/events.ts | 17 +++++++++++++++++ packages/frontend-core/src/api/types.ts | 4 +++- 3 files changed, 20 insertions(+), 14 deletions(-) delete mode 100644 packages/frontend-core/src/api/events.js create mode 100644 packages/frontend-core/src/api/events.ts diff --git a/packages/frontend-core/src/api/events.js b/packages/frontend-core/src/api/events.js deleted file mode 100644 index 3f17722d3e..0000000000 --- a/packages/frontend-core/src/api/events.js +++ /dev/null @@ -1,13 +0,0 @@ -export const buildEventEndpoints = API => ({ - /** - * Publish a specific event to the backend. - */ - publishEvent: async eventType => { - return await API.post({ - url: `/api/global/event/publish`, - body: { - type: eventType, - }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/events.ts b/packages/frontend-core/src/api/events.ts new file mode 100644 index 0000000000..9e6cc40de3 --- /dev/null +++ b/packages/frontend-core/src/api/events.ts @@ -0,0 +1,17 @@ +import { EventPublishType, PostEventPublishRequest } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface EventEndpoints { + publishEvent: (type: EventPublishType) => Promise +} + +export const buildEventEndpoints = (API: BaseAPIClient): EventEndpoints => ({ + publishEvent: async type => { + return await API.post({ + url: `/api/global/event/publish`, + body: { + type, + }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 192576731c..9c71ed4e25 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -9,6 +9,7 @@ import { BackupEndpoints } from "./backups" import { ConfigEndpoints } from "./configs" import { DatasourceEndpoints } from "./datasources" import { EnvironmentVariableEndpoints } from "./environmentVariables" +import { EventEndpoints } from "./events" export enum HTTPMethod { POST = "POST", @@ -79,4 +80,5 @@ export type APIClient = BaseAPIClient & BackupEndpoints & ConfigEndpoints & DatasourceEndpoints & - EnvironmentVariableEndpoints + EnvironmentVariableEndpoints & + EventEndpoints From 1b6e6ed7db900c36b8956c4bee6404f393b67c05 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Dec 2024 15:23:48 +0000 Subject: [PATCH 023/103] Type flag endpoints --- packages/builder/src/stores/builder/flags.js | 7 ++----- .../src/api/{flags.js => flags.ts} | 17 +++++++++++++---- packages/frontend-core/src/api/types.ts | 4 +++- 3 files changed, 18 insertions(+), 10 deletions(-) rename packages/frontend-core/src/api/{flags.js => flags.ts} (52%) diff --git a/packages/builder/src/stores/builder/flags.js b/packages/builder/src/stores/builder/flags.js index 9f1652676c..23f41808ff 100644 --- a/packages/builder/src/stores/builder/flags.js +++ b/packages/builder/src/stores/builder/flags.js @@ -10,14 +10,11 @@ export function createFlagsStore() { set(flags) }, updateFlag: async (flag, value) => { - await API.updateFlag({ - flag, - value, - }) + await API.updateFlag(flag, value) await actions.fetch() }, toggleUiFeature: async feature => { - await API.toggleUiFeature({ value: feature }) + await API.toggleUiFeature(feature) }, } diff --git a/packages/frontend-core/src/api/flags.js b/packages/frontend-core/src/api/flags.ts similarity index 52% rename from packages/frontend-core/src/api/flags.js rename to packages/frontend-core/src/api/flags.ts index 16adeb7b5d..290cb92629 100644 --- a/packages/frontend-core/src/api/flags.js +++ b/packages/frontend-core/src/api/flags.ts @@ -1,4 +1,13 @@ -export const buildFlagEndpoints = API => ({ +import { Flags, SetFlagRequest } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface FlagEndpoints { + getFlags: () => Promise + updateFlag: (flag: string, value: any) => Promise<{ message: string }> + toggleUiFeature: (value: string) => Promise<{ message: string }> +} + +export const buildFlagEndpoints = (API: BaseAPIClient): FlagEndpoints => ({ /** * Gets the current user flags object. */ @@ -13,8 +22,8 @@ export const buildFlagEndpoints = API => ({ * @param flag the flag to update * @param value the value to set the flag to */ - updateFlag: async ({ flag, value }) => { - return await API.post({ + updateFlag: async (flag, value) => { + return await API.post({ url: "/api/users/flags", body: { flag, @@ -26,7 +35,7 @@ export const buildFlagEndpoints = API => ({ * Allows us to experimentally toggle a beta UI feature through a cookie. * @param value the feature to toggle */ - toggleUiFeature: async ({ value }) => { + toggleUiFeature: async value => { return await API.post({ url: `/api/beta/${value}`, }) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 9c71ed4e25..9281c334fa 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -10,6 +10,7 @@ import { ConfigEndpoints } from "./configs" import { DatasourceEndpoints } from "./datasources" import { EnvironmentVariableEndpoints } from "./environmentVariables" import { EventEndpoints } from "./events" +import { FlagEndpoints } from "./flags" export enum HTTPMethod { POST = "POST", @@ -81,4 +82,5 @@ export type APIClient = BaseAPIClient & ConfigEndpoints & DatasourceEndpoints & EnvironmentVariableEndpoints & - EventEndpoints + EventEndpoints & + FlagEndpoints From 16205644bd8e5afa4aa7455b1f0ae172212d5135 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Dec 2024 15:58:16 +0000 Subject: [PATCH 024/103] Type group endpoints --- packages/builder/src/stores/portal/groups.js | 9 +- .../src/api/{groups.js => groups.ts} | 90 +++++++++++++++---- packages/frontend-core/src/api/types.ts | 4 +- 3 files changed, 79 insertions(+), 24 deletions(-) rename packages/frontend-core/src/api/{groups.js => groups.ts} (53%) diff --git a/packages/builder/src/stores/portal/groups.js b/packages/builder/src/stores/portal/groups.js index 1edc8a461c..f81dee068b 100644 --- a/packages/builder/src/stores/portal/groups.js +++ b/packages/builder/src/stores/portal/groups.js @@ -46,10 +46,7 @@ export function createGroupsStore() { }, delete: async group => { - await API.deleteGroup({ - id: group._id, - rev: group._rev, - }) + await API.deleteGroup(group._id, group._rev) store.update(state => { state = state.filter(state => state._id !== group._id) return state @@ -89,11 +86,11 @@ export function createGroupsStore() { }, addGroupAppBuilder: async (groupId, appId) => { - return await API.addGroupAppBuilder({ groupId, appId }) + return await API.addGroupAppBuilder(groupId, appId) }, removeGroupAppBuilder: async (groupId, appId) => { - return await API.removeGroupAppBuilder({ groupId, appId }) + return await API.removeGroupAppBuilder(groupId, appId) }, } diff --git a/packages/frontend-core/src/api/groups.js b/packages/frontend-core/src/api/groups.ts similarity index 53% rename from packages/frontend-core/src/api/groups.js rename to packages/frontend-core/src/api/groups.ts index b1be230ac6..c09c5284ec 100644 --- a/packages/frontend-core/src/api/groups.js +++ b/packages/frontend-core/src/api/groups.ts @@ -1,13 +1,50 @@ -export const buildGroupsEndpoints = API => { - // underlying functionality of adding/removing users/apps to groups - async function updateGroupResource(groupId, resource, operation, ids) { - if (!Array.isArray(ids)) { - ids = [ids] - } - return await API.post({ +import { SearchUserGroupResponse, UserGroup } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface GroupEndpoints { + saveGroup: (group: UserGroup) => Promise<{ _id: string; _rev: string }> + getGroups: () => Promise + getGroup: (id: string) => Promise + deleteGroup: (id: string, rev: string) => Promise<{ message: string }> + getGroupUsers: ( + data: GetGroupUsersRequest + ) => Promise + addUsersToGroup: (groupId: string, userIds: string[]) => Promise + removeUsersFromGroup: (groupId: string, userIds: string[]) => Promise + addAppsToGroup: (groupId: string, appArray: object[]) => Promise + removeAppsFromGroup: (groupId: string, appArray: object[]) => Promise + addGroupAppBuilder: (groupId: string, appId: string) => Promise + removeGroupAppBuilder: (groupId: string, appId: string) => Promise +} + +enum GroupResource { + USERS = "users", + APPS = "apps", +} + +enum GroupOperation { + ADD = "add", + REMOVE = "remove", +} + +type GetGroupUsersRequest = { + id: string + bookmark?: string + emailSearch?: string +} + +export const buildGroupsEndpoints = (API: BaseAPIClient): GroupEndpoints => { + // Underlying functionality of adding/removing users/apps to groups + async function updateGroupResource( + groupId: string, + resource: GroupResource, + operation: GroupOperation, + resources: string[] | object[] + ) { + return await API.post<{ [key in GroupOperation]?: string[] | object[] }>({ url: `/api/global/groups/${groupId}/${resource}`, body: { - [operation]: ids, + [operation]: resources, }, }) } @@ -46,7 +83,7 @@ export const buildGroupsEndpoints = API => { * @param id the id of the config to delete * @param rev the revision of the config to delete */ - deleteGroup: async ({ id, rev }) => { + deleteGroup: async (id, rev) => { return await API.delete({ url: `/api/global/groups/${id}/${rev}`, }) @@ -61,9 +98,8 @@ export const buildGroupsEndpoints = API => { url += `bookmark=${bookmark}&` } if (emailSearch) { - url += `emailSearch=${emailSearch}&` + url += `emailSearch=${emailSearch}` } - return await API.get({ url, }) @@ -75,7 +111,12 @@ export const buildGroupsEndpoints = API => { * @param userIds The user IDs to be added */ addUsersToGroup: async (groupId, userIds) => { - return updateGroupResource(groupId, "users", "add", userIds) + return updateGroupResource( + groupId, + GroupResource.USERS, + GroupOperation.ADD, + userIds + ) }, /** @@ -84,7 +125,12 @@ export const buildGroupsEndpoints = API => { * @param userIds The user IDs to be removed */ removeUsersFromGroup: async (groupId, userIds) => { - return updateGroupResource(groupId, "users", "remove", userIds) + return updateGroupResource( + groupId, + GroupResource.USERS, + GroupOperation.REMOVE, + userIds + ) }, /** @@ -93,7 +139,12 @@ export const buildGroupsEndpoints = API => { * @param appArray Array of objects, containing the appId and roleId to be added */ addAppsToGroup: async (groupId, appArray) => { - return updateGroupResource(groupId, "apps", "add", appArray) + return updateGroupResource( + groupId, + GroupResource.APPS, + GroupOperation.ADD, + appArray + ) }, /** @@ -102,7 +153,12 @@ export const buildGroupsEndpoints = API => { * @param appArray Array of objects, containing the appId to be removed */ removeAppsFromGroup: async (groupId, appArray) => { - return updateGroupResource(groupId, "apps", "remove", appArray) + return updateGroupResource( + groupId, + GroupResource.APPS, + GroupOperation.REMOVE, + appArray + ) }, /** @@ -110,7 +166,7 @@ export const buildGroupsEndpoints = API => { * @param groupId The group to update * @param appId The app id where the builder will be added */ - addGroupAppBuilder: async ({ groupId, appId }) => { + addGroupAppBuilder: async (groupId, appId) => { return await API.post({ url: `/api/global/groups/${groupId}/app/${appId}/builder`, }) @@ -121,7 +177,7 @@ export const buildGroupsEndpoints = API => { * @param groupId The group to update * @param appId The app id where the builder will be removed */ - removeGroupAppBuilder: async ({ groupId, appId }) => { + removeGroupAppBuilder: async (groupId, appId) => { return await API.delete({ url: `/api/global/groups/${groupId}/app/${appId}/builder`, }) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 9281c334fa..fbf5d97eee 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -11,6 +11,7 @@ import { DatasourceEndpoints } from "./datasources" import { EnvironmentVariableEndpoints } from "./environmentVariables" import { EventEndpoints } from "./events" import { FlagEndpoints } from "./flags" +import { GroupEndpoints } from "./groups" export enum HTTPMethod { POST = "POST", @@ -83,4 +84,5 @@ export type APIClient = BaseAPIClient & DatasourceEndpoints & EnvironmentVariableEndpoints & EventEndpoints & - FlagEndpoints + FlagEndpoints & + GroupEndpoints From d36122b7bd7401efd771bb3dcf01193a55caf78e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Dec 2024 16:02:20 +0000 Subject: [PATCH 025/103] Remove deprecated hosting endpoints --- packages/frontend-core/src/api/hosting.js | 19 ------------------- packages/frontend-core/src/api/index.ts | 2 -- 2 files changed, 21 deletions(-) delete mode 100644 packages/frontend-core/src/api/hosting.js diff --git a/packages/frontend-core/src/api/hosting.js b/packages/frontend-core/src/api/hosting.js deleted file mode 100644 index 8c398f9ae7..0000000000 --- a/packages/frontend-core/src/api/hosting.js +++ /dev/null @@ -1,19 +0,0 @@ -export const buildHostingEndpoints = API => ({ - /** - * Gets the hosting URLs of the environment. - */ - getHostingURLs: async () => { - return await API.get({ - url: "/api/hosting/urls", - }) - }, - - /** - * Gets the list of deployed apps. - */ - getDeployedApps: async () => { - return await API.get({ - url: "/api/hosting/apps", - }) - }, -}) diff --git a/packages/frontend-core/src/api/index.ts b/packages/frontend-core/src/api/index.ts index b7951ac227..398650f2cf 100644 --- a/packages/frontend-core/src/api/index.ts +++ b/packages/frontend-core/src/api/index.ts @@ -19,7 +19,6 @@ import { buildAutomationEndpoints } from "./automations" import { buildConfigEndpoints } from "./configs" import { buildDatasourceEndpoints } from "./datasources" import { buildFlagEndpoints } from "./flags" -import { buildHostingEndpoints } from "./hosting" import { buildLayoutEndpoints } from "./layouts" import { buildOtherEndpoints } from "./other" import { buildPermissionsEndpoints } from "./permissions" @@ -261,7 +260,6 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { ...buildConfigEndpoints(API), ...buildDatasourceEndpoints(API), ...buildFlagEndpoints(API), - ...buildHostingEndpoints(API), ...buildLayoutEndpoints(API), ...buildOtherEndpoints(API), ...buildPermissionsEndpoints(API), From b2b2648798098696084f30afecaca07bb85e9043 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Dec 2024 16:06:12 +0000 Subject: [PATCH 026/103] Type layout endpoints --- .../builder/src/stores/builder/layouts.js | 5 +-- packages/frontend-core/src/api/layouts.js | 23 -------------- packages/frontend-core/src/api/layouts.ts | 31 +++++++++++++++++++ packages/frontend-core/src/api/types.ts | 4 ++- 4 files changed, 35 insertions(+), 28 deletions(-) delete mode 100644 packages/frontend-core/src/api/layouts.js create mode 100644 packages/frontend-core/src/api/layouts.ts diff --git a/packages/builder/src/stores/builder/layouts.js b/packages/builder/src/stores/builder/layouts.js index 30d6935b19..b105989746 100644 --- a/packages/builder/src/stores/builder/layouts.js +++ b/packages/builder/src/stores/builder/layouts.js @@ -59,10 +59,7 @@ export class LayoutStore extends BudiStore { if (!layout?._id) { return } - await API.deleteLayout({ - layoutId: layout._id, - layoutRev: layout._rev, - }) + await API.deleteLayout(layout._id, layout._rev) this.update(state => { state.layouts = state.layouts.filter(x => x._id !== layout._id) return state diff --git a/packages/frontend-core/src/api/layouts.js b/packages/frontend-core/src/api/layouts.js deleted file mode 100644 index 51bce1f533..0000000000 --- a/packages/frontend-core/src/api/layouts.js +++ /dev/null @@ -1,23 +0,0 @@ -export const buildLayoutEndpoints = API => ({ - /** - * Saves a layout. - * @param layout the layout to save - */ - saveLayout: async layout => { - return await API.post({ - url: "/api/layouts", - body: layout, - }) - }, - - /** - * Deletes a layout. - * @param layoutId the ID of the layout to delete - * @param layoutRev the rev of the layout to delete - */ - deleteLayout: async ({ layoutId, layoutRev }) => { - return await API.delete({ - url: `/api/layouts/${layoutId}/${layoutRev}`, - }) - }, -}) diff --git a/packages/frontend-core/src/api/layouts.ts b/packages/frontend-core/src/api/layouts.ts new file mode 100644 index 0000000000..159b6585fe --- /dev/null +++ b/packages/frontend-core/src/api/layouts.ts @@ -0,0 +1,31 @@ +import { Layout, SaveLayoutRequest, SaveLayoutResponse } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface LayoutEndpoints { + saveLayout: (layout: Layout) => Promise + deleteLayout: (id: string, rev: string) => Promise<{ message: string }> +} + +export const buildLayoutEndpoints = (API: BaseAPIClient): LayoutEndpoints => ({ + /** + * Saves a layout. + * @param layout the layout to save + */ + saveLayout: async layout => { + return await API.post({ + url: "/api/layouts", + body: layout, + }) + }, + + /** + * Deletes a layout. + * @param layoutId the ID of the layout to delete + * @param layoutRev the rev of the layout to delete + */ + deleteLayout: async (id: string, rev: string) => { + return await API.delete({ + url: `/api/layouts/${id}/${rev}`, + }) + }, +}) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index fbf5d97eee..73f445376d 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -12,6 +12,7 @@ import { EnvironmentVariableEndpoints } from "./environmentVariables" import { EventEndpoints } from "./events" import { FlagEndpoints } from "./flags" import { GroupEndpoints } from "./groups" +import { LayoutEndpoints } from "./layouts" export enum HTTPMethod { POST = "POST", @@ -85,4 +86,5 @@ export type APIClient = BaseAPIClient & EnvironmentVariableEndpoints & EventEndpoints & FlagEndpoints & - GroupEndpoints + GroupEndpoints & + LayoutEndpoints From 85e86900d0b031713b37df45148c8d12467d0fa3 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Dec 2024 16:14:28 +0000 Subject: [PATCH 027/103] Type licensing endpoints --- .../builder/portal/account/upgrade.svelte | 4 +-- .../src/api/{licensing.js => licensing.ts} | 36 +++++++++++++++---- packages/frontend-core/src/api/types.ts | 4 ++- 3 files changed, 35 insertions(+), 9 deletions(-) rename packages/frontend-core/src/api/{licensing.js => licensing.ts} (53%) diff --git a/packages/builder/src/pages/builder/portal/account/upgrade.svelte b/packages/builder/src/pages/builder/portal/account/upgrade.svelte index dbdedb6805..98af01ccf6 100644 --- a/packages/builder/src/pages/builder/portal/account/upgrade.svelte +++ b/packages/builder/src/pages/builder/portal/account/upgrade.svelte @@ -64,7 +64,7 @@ const activateLicenseKey = async () => { try { - await API.activateLicenseKey({ licenseKey }) + await API.activateLicenseKey(licenseKey) await auth.getSelf() await getLicenseKey() notifications.success("Successfully activated") @@ -119,7 +119,7 @@ async function activateOfflineLicense(offlineLicenseToken) { try { - await API.activateOfflineLicense({ offlineLicenseToken }) + await API.activateOfflineLicense(offlineLicenseToken) await auth.getSelf() await getOfflineLicense() notifications.success("Successfully activated") diff --git a/packages/frontend-core/src/api/licensing.js b/packages/frontend-core/src/api/licensing.ts similarity index 53% rename from packages/frontend-core/src/api/licensing.js rename to packages/frontend-core/src/api/licensing.ts index 987fc34cf5..fb87335cc5 100644 --- a/packages/frontend-core/src/api/licensing.js +++ b/packages/frontend-core/src/api/licensing.ts @@ -1,10 +1,34 @@ -export const buildLicensingEndpoints = API => ({ +import { + ActivateLicenseKeyRequest, + ActivateOfflineLicenseTokenRequest, + GetLicenseKeyResponse, + GetOfflineIdentifierResponse, + GetOfflineLicenseTokenResponse, + QuotaUsage, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface LicensingEndpoints { + activateLicenseKey: (licenseKey: string) => Promise + deleteLicenseKey: () => Promise + getLicenseKey: () => Promise + activateOfflineLicense: (offlineLicenseToken: string) => Promise + deleteOfflineLicense: () => Promise + getOfflineLicense: () => Promise + getOfflineLicenseIdentifier: () => Promise + refreshLicense: () => Promise + getQuotaUsage: () => Promise +} + +export const buildLicensingEndpoints = ( + API: BaseAPIClient +): LicensingEndpoints => ({ // LICENSE KEY - activateLicenseKey: async data => { - return API.post({ + activateLicenseKey: async licenseKey => { + return API.post({ url: `/api/global/license/key`, - body: data, + body: { licenseKey }, }) }, deleteLicenseKey: async () => { @@ -26,8 +50,8 @@ export const buildLicensingEndpoints = API => ({ // OFFLINE LICENSE - activateOfflineLicense: async ({ offlineLicenseToken }) => { - return API.post({ + activateOfflineLicense: async offlineLicenseToken => { + return API.post({ url: "/api/global/license/offline", body: { offlineLicenseToken, diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 73f445376d..030b969bd0 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -13,6 +13,7 @@ import { EventEndpoints } from "./events" import { FlagEndpoints } from "./flags" import { GroupEndpoints } from "./groups" import { LayoutEndpoints } from "./layouts" +import { LicensingEndpoints } from "./licensing" export enum HTTPMethod { POST = "POST", @@ -87,4 +88,5 @@ export type APIClient = BaseAPIClient & EventEndpoints & FlagEndpoints & GroupEndpoints & - LayoutEndpoints + LayoutEndpoints & + LicensingEndpoints From 20384673f208ed985518118b6603eaca3d206882 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 07:13:18 +0000 Subject: [PATCH 028/103] Fix some TS errors and type API errors --- packages/frontend-core/src/api/index.ts | 10 ++++++++-- packages/frontend-core/src/api/licensing.ts | 6 ++---- packages/frontend-core/src/api/types.ts | 12 +++++++++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/frontend-core/src/api/index.ts b/packages/frontend-core/src/api/index.ts index 398650f2cf..bc55913d3a 100644 --- a/packages/frontend-core/src/api/index.ts +++ b/packages/frontend-core/src/api/index.ts @@ -6,6 +6,7 @@ import { APICallConfig, BaseAPIClient, Headers, + APIError, } from "./types" import { Helpers } from "@budibase/bbui" import { Header } from "@budibase/shared-core" @@ -64,7 +65,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { response: Response, method: HTTPMethod, suppressErrors = false - ) => { + ): Promise => { // Try to read a message from the error let message = response.statusText let json: any = null @@ -90,7 +91,11 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { } // Generates an error object from a string - const makeError = (message: string, url?: string, method?: HTTPMethod) => { + const makeError = ( + message: string, + url?: string, + method?: HTTPMethod + ): APIError => { return { message, json: null, @@ -98,6 +103,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { url: url, method: method, handled: true, + suppressErrors: false, } } diff --git a/packages/frontend-core/src/api/licensing.ts b/packages/frontend-core/src/api/licensing.ts index fb87335cc5..9211f5a7b0 100644 --- a/packages/frontend-core/src/api/licensing.ts +++ b/packages/frontend-core/src/api/licensing.ts @@ -11,10 +11,10 @@ import { BaseAPIClient } from "./types" export interface LicensingEndpoints { activateLicenseKey: (licenseKey: string) => Promise deleteLicenseKey: () => Promise - getLicenseKey: () => Promise + getLicenseKey: () => Promise activateOfflineLicense: (offlineLicenseToken: string) => Promise deleteOfflineLicense: () => Promise - getOfflineLicense: () => Promise + getOfflineLicense: () => Promise getOfflineLicenseIdentifier: () => Promise refreshLicense: () => Promise getQuotaUsage: () => Promise @@ -24,7 +24,6 @@ export const buildLicensingEndpoints = ( API: BaseAPIClient ): LicensingEndpoints => ({ // LICENSE KEY - activateLicenseKey: async licenseKey => { return API.post({ url: `/api/global/license/key`, @@ -49,7 +48,6 @@ export const buildLicensingEndpoints = ( }, // OFFLINE LICENSE - activateOfflineLicense: async offlineLicenseToken => { return API.post({ url: "/api/global/license/offline", diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 030b969bd0..69c41c49bf 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -73,6 +73,16 @@ export type BaseAPIClient = { getAppID: () => string } +export type APIError = { + message?: string + json: any + status: number + url: string + method: HTTPMethod + handled: boolean + suppressErrors: boolean +} + export type APIClient = BaseAPIClient & AIEndpoints & AnalyticsEndpoints & @@ -89,4 +99,4 @@ export type APIClient = BaseAPIClient & FlagEndpoints & GroupEndpoints & LayoutEndpoints & - LicensingEndpoints + LicensingEndpoints & { [key: string]: any } From 99c3955e6d580a4b83d01ff0d757db349b646a25 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 07:36:40 +0000 Subject: [PATCH 029/103] Type logs, migrations and other endpoints --- .../src/api/{logs.js => logs.ts} | 8 +++++- packages/frontend-core/src/api/migrations.js | 10 ------- packages/frontend-core/src/api/migrations.ts | 18 +++++++++++++ .../src/api/{other.js => other.ts} | 27 ++++++++++--------- packages/frontend-core/src/api/types.ts | 8 +++++- 5 files changed, 47 insertions(+), 24 deletions(-) rename packages/frontend-core/src/api/{logs.js => logs.ts} (57%) delete mode 100644 packages/frontend-core/src/api/migrations.js create mode 100644 packages/frontend-core/src/api/migrations.ts rename packages/frontend-core/src/api/{other.js => other.ts} (60%) diff --git a/packages/frontend-core/src/api/logs.js b/packages/frontend-core/src/api/logs.ts similarity index 57% rename from packages/frontend-core/src/api/logs.js rename to packages/frontend-core/src/api/logs.ts index b6cb98627c..4c883233f6 100644 --- a/packages/frontend-core/src/api/logs.js +++ b/packages/frontend-core/src/api/logs.ts @@ -1,4 +1,10 @@ -export const buildLogsEndpoints = API => ({ +import { BaseAPIClient } from "./types" + +export interface LogEndpoints { + getSystemLogs: () => Promise +} + +export const buildLogsEndpoints = (API: BaseAPIClient): LogEndpoints => ({ /** * Gets a stream for the system logs. */ diff --git a/packages/frontend-core/src/api/migrations.js b/packages/frontend-core/src/api/migrations.js deleted file mode 100644 index 2da70d6fcb..0000000000 --- a/packages/frontend-core/src/api/migrations.js +++ /dev/null @@ -1,10 +0,0 @@ -export const buildMigrationEndpoints = API => ({ - /** - * Gets the info about the current app migration - */ - getMigrationStatus: async () => { - return await API.get({ - url: "/api/migrations/status", - }) - }, -}) diff --git a/packages/frontend-core/src/api/migrations.ts b/packages/frontend-core/src/api/migrations.ts new file mode 100644 index 0000000000..02fefe7c3d --- /dev/null +++ b/packages/frontend-core/src/api/migrations.ts @@ -0,0 +1,18 @@ +import { BaseAPIClient } from "./types" + +export interface MigrationEndpoints { + getMigrationStatus: () => Promise<{ migrated: boolean }> +} + +export const buildMigrationEndpoints = ( + API: BaseAPIClient +): MigrationEndpoints => ({ + /** + * Gets the info about the current app migration + */ + getMigrationStatus: async () => { + return await API.get({ + url: "/api/migrations/status", + }) + }, +}) diff --git a/packages/frontend-core/src/api/other.js b/packages/frontend-core/src/api/other.ts similarity index 60% rename from packages/frontend-core/src/api/other.js rename to packages/frontend-core/src/api/other.ts index 3d171eaab4..d984df7b06 100644 --- a/packages/frontend-core/src/api/other.js +++ b/packages/frontend-core/src/api/other.ts @@ -1,4 +1,17 @@ -export const buildOtherEndpoints = API => ({ +import { SystemStatusResponse } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface OtherEndpoints { + getSystemStatus: () => Promise + getBudibaseVersion: () => Promise + + // Missing request or response types + getEnvironment: () => Promise> + getIntegrations: () => Promise> + getBasePermissions: () => Promise +} + +export const buildOtherEndpoints = (API: BaseAPIClient): OtherEndpoints => ({ /** * Gets the current environment details. */ @@ -31,7 +44,7 @@ export const buildOtherEndpoints = API => ({ */ getBudibaseVersion: async () => { return ( - await API.get({ + await API.get<{ version: string }>({ url: "/api/dev/version", }) ).version @@ -45,14 +58,4 @@ export const buildOtherEndpoints = API => ({ url: "/api/permission/builtin", }) }, - - /** - * Check if they are part of the budibase beta program. - */ - checkBetaAccess: async email => { - return await API.get({ - url: `/api/beta/access?email=${email}`, - external: true, - }) - }, }) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 69c41c49bf..a121eb2089 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -14,6 +14,9 @@ import { FlagEndpoints } from "./flags" import { GroupEndpoints } from "./groups" import { LayoutEndpoints } from "./layouts" import { LicensingEndpoints } from "./licensing" +import { LogEndpoints } from "./logs" +import { MigrationEndpoints } from "./migrations" +import { OtherEndpoints } from "./other" export enum HTTPMethod { POST = "POST", @@ -99,4 +102,7 @@ export type APIClient = BaseAPIClient & FlagEndpoints & GroupEndpoints & LayoutEndpoints & - LicensingEndpoints & { [key: string]: any } + LicensingEndpoints & + LogEndpoints & + MigrationEndpoints & + OtherEndpoints & { [key: string]: any } From d59a3a0497e1a69f5aae899b092a36ca84ba112d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 07:43:12 +0000 Subject: [PATCH 030/103] Type permission endpoints --- .../builder/app/[application]/_layout.svelte | 3 -- .../builder/src/stores/builder/permissions.js | 12 ++----- .../api/{permissions.js => permissions.ts} | 36 ++++++++++++++++--- packages/frontend-core/src/api/types.ts | 4 ++- 4 files changed, 36 insertions(+), 19 deletions(-) rename packages/frontend-core/src/api/{permissions.js => permissions.ts} (56%) diff --git a/packages/builder/src/pages/builder/app/[application]/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/_layout.svelte index 8e109d3145..bad28f89f7 100644 --- a/packages/builder/src/pages/builder/app/[application]/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_layout.svelte @@ -105,9 +105,6 @@ if (!hasSynced && application) { try { await API.syncApp(application) - // check if user has beta access - // const betaResponse = await API.checkBetaAccess($auth?.user?.email) - // betaAccess = betaResponse.access } catch (error) { notifications.error("Failed to sync with production database") } diff --git a/packages/builder/src/stores/builder/permissions.js b/packages/builder/src/stores/builder/permissions.js index 0113f221c0..2fd94e35b0 100644 --- a/packages/builder/src/stores/builder/permissions.js +++ b/packages/builder/src/stores/builder/permissions.js @@ -7,18 +7,10 @@ export function createPermissionStore() { return { subscribe, save: async ({ level, role, resource }) => { - return await API.updatePermissionForResource({ - resourceId: resource, - roleId: role, - level, - }) + return await API.updatePermissionForResource(resource, role, level) }, remove: async ({ level, role, resource }) => { - return await API.removePermissionFromResource({ - resourceId: resource, - roleId: role, - level, - }) + return await API.removePermissionFromResource(resource, role, level) }, forResource: async resourceId => { return (await API.getPermissionForResource(resourceId)).permissions diff --git a/packages/frontend-core/src/api/permissions.js b/packages/frontend-core/src/api/permissions.ts similarity index 56% rename from packages/frontend-core/src/api/permissions.js rename to packages/frontend-core/src/api/permissions.ts index dd0005aaf2..8a4a99b642 100644 --- a/packages/frontend-core/src/api/permissions.js +++ b/packages/frontend-core/src/api/permissions.ts @@ -1,4 +1,32 @@ -export const buildPermissionsEndpoints = API => ({ +import { + AddPermissionResponse, + GetDependantResourcesResponse, + GetResourcePermsResponse, + PermissionLevel, + RemovePermissionResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface PermissionEndpoints { + getPermissionForResource: ( + resourceId: string + ) => Promise + updatePermissionForResource: ( + resourceId: string, + roleId: string, + level: PermissionLevel + ) => Promise + removePermissionFromResource: ( + resourceId: string, + roleId: string, + level: PermissionLevel + ) => Promise + getDependants: (resourceId: string) => Promise +} + +export const buildPermissionsEndpoints = ( + API: BaseAPIClient +): PermissionEndpoints => ({ /** * Gets the permission required to access a specific resource * @param resourceId the resource ID to check @@ -14,9 +42,8 @@ export const buildPermissionsEndpoints = API => ({ * @param resourceId the ID of the resource to update * @param roleId the ID of the role to update the permissions of * @param level the level to assign the role for this resource - * @return {Promise<*>} */ - updatePermissionForResource: async ({ resourceId, roleId, level }) => { + updatePermissionForResource: async (resourceId, roleId, level) => { return await API.post({ url: `/api/permission/${roleId}/${resourceId}/${level}`, }) @@ -27,9 +54,8 @@ export const buildPermissionsEndpoints = API => ({ * @param resourceId the ID of the resource to update * @param roleId the ID of the role to update the permissions of * @param level the level to remove the role for this resource - * @return {Promise<*>} */ - removePermissionFromResource: async ({ resourceId, roleId, level }) => { + removePermissionFromResource: async (resourceId, roleId, level) => { return await API.delete({ url: `/api/permission/${roleId}/${resourceId}/${level}`, }) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index a121eb2089..9687b7ed2c 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -17,6 +17,7 @@ import { LicensingEndpoints } from "./licensing" import { LogEndpoints } from "./logs" import { MigrationEndpoints } from "./migrations" import { OtherEndpoints } from "./other" +import { PermissionEndpoints } from "./permissions" export enum HTTPMethod { POST = "POST", @@ -105,4 +106,5 @@ export type APIClient = BaseAPIClient & LicensingEndpoints & LogEndpoints & MigrationEndpoints & - OtherEndpoints & { [key: string]: any } + OtherEndpoints & + PermissionEndpoints & { [key: string]: any } From c6f5287014a882a4bc4ca24c0e5c1a6082c782bf Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 09:46:34 +0000 Subject: [PATCH 031/103] Type plugin endpoints --- .../src/api/{plugins.js => plugins.ts} | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) rename packages/frontend-core/src/api/{plugins.js => plugins.ts} (62%) diff --git a/packages/frontend-core/src/api/plugins.js b/packages/frontend-core/src/api/plugins.ts similarity index 62% rename from packages/frontend-core/src/api/plugins.js rename to packages/frontend-core/src/api/plugins.ts index 8735b04caa..3e05046e4f 100644 --- a/packages/frontend-core/src/api/plugins.js +++ b/packages/frontend-core/src/api/plugins.ts @@ -1,4 +1,18 @@ -export const buildPluginEndpoints = API => ({ +import { + CreatePluginRequest, + CreatePluginResponse, + Plugin, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface PluginEndpoins { + uploadPlugin: (data: any) => Promise<{ message: string; plugins: any[] }> + createPlugin: (data: CreatePluginRequest) => Promise + getPlugins: () => Promise + deletePlugin: (pluginId: string) => Promise<{ message: string }> +} + +export const buildPluginEndpoints = (API: BaseAPIClient): PluginEndpoins => ({ /** * Uploads a plugin tarball bundle * @param data the plugin tarball bundle to upload From abddbdeea721f7e9638cb8e97229f1d04c95ee15 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 09:47:35 +0000 Subject: [PATCH 032/103] Add plugin endpoint interface into API client union type --- packages/frontend-core/src/api/types.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 9687b7ed2c..ebea541c34 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -18,6 +18,7 @@ import { LogEndpoints } from "./logs" import { MigrationEndpoints } from "./migrations" import { OtherEndpoints } from "./other" import { PermissionEndpoints } from "./permissions" +import { PluginEndpoins } from "./plugins" export enum HTTPMethod { POST = "POST", @@ -107,4 +108,5 @@ export type APIClient = BaseAPIClient & LogEndpoints & MigrationEndpoints & OtherEndpoints & - PermissionEndpoints & { [key: string]: any } + PermissionEndpoints & + PluginEndpoins & { [key: string]: any } From ac05ccd6a8e2449a50d9c9ddd8373084900a4e21 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 09:59:08 +0000 Subject: [PATCH 033/103] Type query endpoints --- .../builder/src/stores/builder/queries.js | 5 +-- packages/client/src/utils/buttonActions.js | 7 +-- .../src/api/{queries.js => queries.ts} | 43 +++++++++++++++---- packages/frontend-core/src/api/types.ts | 4 +- .../frontend-core/src/fetch/QueryFetch.js | 4 +- 5 files changed, 43 insertions(+), 20 deletions(-) rename packages/frontend-core/src/api/{queries.js => queries.ts} (56%) diff --git a/packages/builder/src/stores/builder/queries.js b/packages/builder/src/stores/builder/queries.js index b717a17f97..a3c599785d 100644 --- a/packages/builder/src/stores/builder/queries.js +++ b/packages/builder/src/stores/builder/queries.js @@ -62,10 +62,7 @@ export function createQueriesStore() { } const importQueries = async ({ data, datasourceId }) => { - return await API.importQueries({ - datasourceId, - data, - }) + return await API.importQueries(datasourceId, data) } const select = id => { diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 63354d3119..561c12556a 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -251,17 +251,14 @@ const navigationHandler = action => { } const queryExecutionHandler = async action => { - const { datasourceId, queryId, queryParams, notificationOverride } = - action.parameters + const { queryId, queryParams, notificationOverride } = action.parameters try { const query = await API.fetchQueryDefinition(queryId) if (query?.datasourceId == null) { notificationStore.actions.error("That query couldn't be found") return false } - const result = await API.executeQuery({ - datasourceId, - queryId, + const result = await API.executeQuery(queryId, { parameters: queryParams, }) diff --git a/packages/frontend-core/src/api/queries.js b/packages/frontend-core/src/api/queries.ts similarity index 56% rename from packages/frontend-core/src/api/queries.js rename to packages/frontend-core/src/api/queries.ts index f18ec7c4ec..02b1dc38ec 100644 --- a/packages/frontend-core/src/api/queries.js +++ b/packages/frontend-core/src/api/queries.ts @@ -1,12 +1,39 @@ -export const buildQueryEndpoints = API => ({ +import { + ExecuteQueryRequest, + ExecuteQueryResponse, + PreviewQueryRequest, + PreviewQueryResponse, + Query, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface QueryEndpoints { + executeQuery: ( + queryId: string, + opts?: ExecuteQueryRequest + ) => Promise[]> + fetchQueryDefinition: (queryId: string) => Promise + getQueries: () => Promise + saveQuery: (query: Query) => Promise + deleteQuery: (id: string, rev: string) => Promise + previewQuery: (query: PreviewQueryRequest) => Promise + + // Missing request or response types + importQueries: (datasourceId: string, data: any) => Promise +} + +export const buildQueryEndpoints = (API: BaseAPIClient): QueryEndpoints => ({ /** * Executes a query against an external data connector. * @param queryId the ID of the query to execute * @param pagination pagination info for the query * @param parameters parameters for the query */ - executeQuery: async ({ queryId, pagination, parameters }) => { - return await API.post({ + executeQuery: async (queryId, { pagination, parameters } = {}) => { + return await API.post< + ExecuteQueryRequest, + ExecuteQueryResponse | Record[] + >({ url: `/api/v2/queries/${queryId}`, body: { parameters, @@ -48,12 +75,12 @@ export const buildQueryEndpoints = API => ({ /** * Deletes a query - * @param queryId the ID of the query to delete - * @param queryRev the rev of the query to delete + * @param id the ID of the query to delete + * @param rev the rev of the query to delete */ - deleteQuery: async ({ queryId, queryRev }) => { + deleteQuery: async (id, rev) => { return await API.delete({ - url: `/api/queries/${queryId}/${queryRev}`, + url: `/api/queries/${id}/${rev}`, }) }, @@ -62,7 +89,7 @@ export const buildQueryEndpoints = API => ({ * @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 (datasourceId, data) => { return await API.post({ url: "/api/queries/import", body: { diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index ebea541c34..5908d37a25 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -19,6 +19,7 @@ import { MigrationEndpoints } from "./migrations" import { OtherEndpoints } from "./other" import { PermissionEndpoints } from "./permissions" import { PluginEndpoins } from "./plugins" +import { QueryEndpoints } from "./queries" export enum HTTPMethod { POST = "POST", @@ -109,4 +110,5 @@ export type APIClient = BaseAPIClient & MigrationEndpoints & OtherEndpoints & PermissionEndpoints & - PluginEndpoins & { [key: string]: any } + PluginEndpoins & + QueryEndpoints & { [key: string]: any } diff --git a/packages/frontend-core/src/fetch/QueryFetch.js b/packages/frontend-core/src/fetch/QueryFetch.js index 6420893515..9fac9704d3 100644 --- a/packages/frontend-core/src/fetch/QueryFetch.js +++ b/packages/frontend-core/src/fetch/QueryFetch.js @@ -48,7 +48,7 @@ export default class QueryFetch extends DataFetch { } // Add pagination to query if supported - let queryPayload = { queryId: datasource?._id, parameters } + let queryPayload = { parameters } if (paginate && supportsPagination) { const requestCursor = type === "page" ? parseInt(cursor || 1) : cursor queryPayload.pagination = { page: requestCursor, limit } @@ -56,7 +56,7 @@ export default class QueryFetch extends DataFetch { // Execute query try { - const res = await this.API.executeQuery(queryPayload) + const res = await this.API.executeQuery(datasource?._id, queryPayload) const { data, pagination, ...rest } = res // Derive pagination info from response From e938fddeed7ac6fe58ce35955baf6c351e33fd84 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 10:06:57 +0000 Subject: [PATCH 034/103] Type relationship endpoints --- .../frontend-core/src/api/relationships.js | 21 ------------- .../frontend-core/src/api/relationships.ts | 31 +++++++++++++++++++ packages/frontend-core/src/api/types.ts | 4 ++- .../src/fetch/RelationshipFetch.js | 15 +++++---- 4 files changed, 43 insertions(+), 28 deletions(-) delete mode 100644 packages/frontend-core/src/api/relationships.js create mode 100644 packages/frontend-core/src/api/relationships.ts diff --git a/packages/frontend-core/src/api/relationships.js b/packages/frontend-core/src/api/relationships.js deleted file mode 100644 index 45595750a8..0000000000 --- a/packages/frontend-core/src/api/relationships.js +++ /dev/null @@ -1,21 +0,0 @@ -export const buildRelationshipEndpoints = API => ({ - /** - * Fetches related rows for a certain field of a certain row. - * @param tableId the ID of the table to fetch from - * @param rowId the ID of the row to fetch related rows for - * @param fieldName the name of the relationship field - */ - fetchRelationshipData: async ({ tableId, rowId, fieldName }) => { - if (!tableId || !rowId) { - return [] - } - const response = await API.get({ - url: `/api/${tableId}/${rowId}/enrich?field=${fieldName}`, - }) - if (!fieldName) { - return response || [] - } else { - return response[fieldName] || [] - } - }, -}) diff --git a/packages/frontend-core/src/api/relationships.ts b/packages/frontend-core/src/api/relationships.ts new file mode 100644 index 0000000000..4579557a0a --- /dev/null +++ b/packages/frontend-core/src/api/relationships.ts @@ -0,0 +1,31 @@ +import { Row } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface RelationshipEndpoints { + fetchRelationshipData: ( + sourceId: string, + rowId: string, + fieldName?: string + ) => Promise +} + +export const buildRelationshipEndpoints = ( + API: BaseAPIClient +): RelationshipEndpoints => ({ + /** + * Fetches related rows for a certain field of a certain row. + * @param sourceId the ID of the table to fetch from + * @param rowId the ID of the row to fetch related rows for + * @param fieldName the name of the relationship field + */ + fetchRelationshipData: async (sourceId, rowId, fieldName) => { + const response = await API.get({ + url: `/api/${sourceId}/${rowId}/enrich?field=${fieldName}`, + }) + if (!fieldName) { + return [response] + } else { + return response[fieldName] || [] + } + }, +}) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 5908d37a25..6d2088cfba 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -20,6 +20,7 @@ import { OtherEndpoints } from "./other" import { PermissionEndpoints } from "./permissions" import { PluginEndpoins } from "./plugins" import { QueryEndpoints } from "./queries" +import { RelationshipEndpoints } from "./relationships" export enum HTTPMethod { POST = "POST", @@ -111,4 +112,5 @@ export type APIClient = BaseAPIClient & OtherEndpoints & PermissionEndpoints & PluginEndpoins & - QueryEndpoints & { [key: string]: any } + QueryEndpoints & + RelationshipEndpoints & { [key: string]: any } diff --git a/packages/frontend-core/src/fetch/RelationshipFetch.js b/packages/frontend-core/src/fetch/RelationshipFetch.js index 04797fcdf1..0dec535724 100644 --- a/packages/frontend-core/src/fetch/RelationshipFetch.js +++ b/packages/frontend-core/src/fetch/RelationshipFetch.js @@ -3,13 +3,16 @@ import DataFetch from "./DataFetch.js" export default class RelationshipFetch extends DataFetch { async getData() { const { datasource } = this.options + if (!datasource?.rowId || !datasource?.rowTableId) { + return { rows: [] } + } try { - const res = await this.API.fetchRelationshipData({ - rowId: datasource?.rowId, - tableId: datasource?.rowTableId, - fieldName: datasource?.fieldName, - }) - return { rows: res || [] } + const res = await this.API.fetchRelationshipData( + datasource.rowTableId, + datasource.rowId, + datasource.fieldName + ) + return { rows: res } } catch (error) { return { rows: [] } } From 2a7c0ca2761aec4e8c4e5dca4da48490004e9965 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 10:20:05 +0000 Subject: [PATCH 035/103] Type role endpoints --- .../builder/src/stores/builder/queries.js | 5 +--- packages/builder/src/stores/builder/roles.js | 5 +--- .../src/api/{roles.js => roles.ts} | 27 +++++++++++++++---- packages/frontend-core/src/api/types.ts | 4 ++- 4 files changed, 27 insertions(+), 14 deletions(-) rename packages/frontend-core/src/api/{roles.js => roles.ts} (50%) diff --git a/packages/builder/src/stores/builder/queries.js b/packages/builder/src/stores/builder/queries.js index a3c599785d..27607deff5 100644 --- a/packages/builder/src/stores/builder/queries.js +++ b/packages/builder/src/stores/builder/queries.js @@ -84,10 +84,7 @@ export function createQueriesStore() { } const deleteQuery = async query => { - await API.deleteQuery({ - queryId: query?._id, - queryRev: query?._rev, - }) + await API.deleteQuery(query._id, query._rev) store.update(state => { state.list = state.list.filter(existing => existing._id !== query._id) return state diff --git a/packages/builder/src/stores/builder/roles.js b/packages/builder/src/stores/builder/roles.js index fd3581f1d4..4d5e37434a 100644 --- a/packages/builder/src/stores/builder/roles.js +++ b/packages/builder/src/stores/builder/roles.js @@ -43,10 +43,7 @@ export function createRolesStore() { setRoles(roles) }, delete: async role => { - await API.deleteRole({ - roleId: role?._id, - roleRev: role?._rev, - }) + await API.deleteRole(role._id, role._rev) await actions.fetch() }, save: async role => { diff --git a/packages/frontend-core/src/api/roles.js b/packages/frontend-core/src/api/roles.ts similarity index 50% rename from packages/frontend-core/src/api/roles.js rename to packages/frontend-core/src/api/roles.ts index 80b8028c12..6a68706271 100644 --- a/packages/frontend-core/src/api/roles.js +++ b/packages/frontend-core/src/api/roles.ts @@ -1,12 +1,29 @@ -export const buildRoleEndpoints = API => ({ +import { + AccessibleRolesResponse, + DestroyRoleResponse, + FetchRolesResponse, + SaveRoleRequest, + SaveRoleResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface RoleEndpoints { + deleteRole: (id: string, rev: string) => Promise + saveRole: (role: SaveRoleRequest) => Promise + getRoles: () => Promise + getRolesForApp: (appId: string) => Promise + getAccessibleRoles: () => Promise +} + +export const buildRoleEndpoints = (API: BaseAPIClient): RoleEndpoints => ({ /** * Deletes a role. - * @param roleId the ID of the role to delete - * @param roleRev the rev of the role to delete + * @param id the ID of the role to delete + * @param rev the rev of the role to delete */ - deleteRole: async ({ roleId, roleRev }) => { + deleteRole: async (id, rev) => { return await API.delete({ - url: `/api/roles/${roleId}/${roleRev}`, + url: `/api/roles/${id}/${rev}`, }) }, diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 6d2088cfba..176e7f6a32 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -21,6 +21,7 @@ import { PermissionEndpoints } from "./permissions" import { PluginEndpoins } from "./plugins" import { QueryEndpoints } from "./queries" import { RelationshipEndpoints } from "./relationships" +import { RoleEndpoints } from "./roles" export enum HTTPMethod { POST = "POST", @@ -113,4 +114,5 @@ export type APIClient = BaseAPIClient & PermissionEndpoints & PluginEndpoins & QueryEndpoints & - RelationshipEndpoints & { [key: string]: any } + RelationshipEndpoints & + RoleEndpoints & { [key: string]: any } From 9926e8d15cf9c604c908f0eed23c699cf2c0472f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 10:23:38 +0000 Subject: [PATCH 036/103] Type route endpoints --- .../frontend-core/src/api/{routes.js => routes.ts} | 10 +++++++++- packages/frontend-core/src/api/types.ts | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) rename packages/frontend-core/src/api/{routes.js => routes.ts} (52%) diff --git a/packages/frontend-core/src/api/routes.js b/packages/frontend-core/src/api/routes.ts similarity index 52% rename from packages/frontend-core/src/api/routes.js rename to packages/frontend-core/src/api/routes.ts index 28e206debc..7d2df98b57 100644 --- a/packages/frontend-core/src/api/routes.js +++ b/packages/frontend-core/src/api/routes.ts @@ -1,4 +1,12 @@ -export const buildRouteEndpoints = API => ({ +import { BaseAPIClient } from "./types" + +export interface RouteEndpoints { + // Missing request or response types + fetchClientAppRoutes: () => Promise<{ routes: any }> + fetchAppRoutes: () => Promise<{ routes: any }> +} + +export const buildRouteEndpoints = (API: BaseAPIClient): RouteEndpoints => ({ /** * Fetches available routes for the client app. */ diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 176e7f6a32..f3cf6b034f 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -22,6 +22,7 @@ import { PluginEndpoins } from "./plugins" import { QueryEndpoints } from "./queries" import { RelationshipEndpoints } from "./relationships" import { RoleEndpoints } from "./roles" +import { RouteEndpoints } from "./routes" export enum HTTPMethod { POST = "POST", @@ -115,4 +116,5 @@ export type APIClient = BaseAPIClient & PluginEndpoins & QueryEndpoints & RelationshipEndpoints & - RoleEndpoints & { [key: string]: any } + RoleEndpoints & + RouteEndpoints & { [key: string]: any } From ea84af782d9c76cc2bc87460b30818cec671faab Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 10:37:15 +0000 Subject: [PATCH 037/103] Type row action endpoints --- .../buttons/grid/GridRowActionsButton.svelte | 4 +- .../builder/src/stores/builder/rowActions.js | 34 +++------ packages/client/src/utils/buttonActions.js | 6 +- .../src/api/{rowActions.js => rowActions.ts} | 72 ++++++++++++------- packages/frontend-core/src/api/types.ts | 3 +- 5 files changed, 58 insertions(+), 61 deletions(-) rename packages/frontend-core/src/api/{rowActions.js => rowActions.ts} (51%) diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridRowActionsButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridRowActionsButton.svelte index f61e19c19d..496f367c01 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridRowActionsButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridRowActionsButton.svelte @@ -39,9 +39,9 @@ const toggleAction = async (action, enabled) => { if (enabled) { - await rowActions.enableView(tableId, viewId, action.id) + await rowActions.enableView(tableId, action.id, viewId) } else { - await rowActions.disableView(tableId, viewId, action.id) + await rowActions.disableView(tableId, action.id, viewId) } } diff --git a/packages/builder/src/stores/builder/rowActions.js b/packages/builder/src/stores/builder/rowActions.js index b1f4e7067f..a7ed45e707 100644 --- a/packages/builder/src/stores/builder/rowActions.js +++ b/packages/builder/src/stores/builder/rowActions.js @@ -55,15 +55,12 @@ export class RowActionStore extends BudiStore { } // Create the action - const res = await API.rowActions.create({ - name, - tableId, - }) + const res = await API.rowActions.create(tableId, name) // Enable action on this view if adding via a view if (viewId) { await Promise.all([ - this.enableView(tableId, viewId, res.id), + this.enableView(tableId, res.id, viewId), automationStore.actions.fetch(), ]) } else { @@ -76,21 +73,13 @@ export class RowActionStore extends BudiStore { return res } - enableView = async (tableId, viewId, rowActionId) => { - await API.rowActions.enableView({ - tableId, - viewId, - rowActionId, - }) + enableView = async (tableId, rowActionId, viewId) => { + await API.rowActions.enableView(tableId, rowActionId, viewId) await this.refreshRowActions(tableId) } - disableView = async (tableId, viewId, rowActionId) => { - await API.rowActions.disableView({ - tableId, - viewId, - rowActionId, - }) + disableView = async (tableId, rowActionId, viewId) => { + await API.rowActions.disableView(tableId, rowActionId, viewId) await this.refreshRowActions(tableId) } @@ -105,21 +94,14 @@ export class RowActionStore extends BudiStore { } delete = async (tableId, rowActionId) => { - await API.rowActions.delete({ - tableId, - rowActionId, - }) + await API.rowActions.delete(tableId, rowActionId) await this.refreshRowActions(tableId) // We don't need to refresh automations as we can only delete row actions // from the automations store, so we already handle the state update there } trigger = async (sourceId, rowActionId, rowId) => { - await API.rowActions.trigger({ - sourceId, - rowActionId, - rowId, - }) + await API.rowActions.trigger(sourceId, rowActionId, rowId) } } diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 561c12556a..010e1fea43 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -487,11 +487,7 @@ const downloadFileHandler = async action => { const rowActionHandler = async action => { const { resourceId, rowId, rowActionId } = action.parameters - await API.rowActions.trigger({ - rowActionId, - sourceId: resourceId, - rowId, - }) + await API.rowActions.trigger(resourceId, rowActionId, rowId) // Refresh related datasources await dataSourceStore.actions.invalidateDataSource(resourceId, { invalidateRelationships: true, diff --git a/packages/frontend-core/src/api/rowActions.js b/packages/frontend-core/src/api/rowActions.ts similarity index 51% rename from packages/frontend-core/src/api/rowActions.js rename to packages/frontend-core/src/api/rowActions.ts index 071af953ef..eef50ed940 100644 --- a/packages/frontend-core/src/api/rowActions.js +++ b/packages/frontend-core/src/api/rowActions.ts @@ -1,13 +1,46 @@ -export const buildRowActionEndpoints = API => ({ +import { + RowActionsResponse, + RowActionResponse, + CreateRowActionRequest, + RowActionPermissionsResponse, + RowActionTriggerRequest, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface RowActionEndpoints { + fetch: (tableId: string) => Promise> + create: (tableId: string, name: string) => Promise + delete: (tableId: string, rowActionId: string) => Promise + enableView: ( + tableId: string, + rowActionId: string, + viewId: string + ) => Promise + disableView: ( + tableId: string, + rowActionId: string, + viewId: string + ) => Promise + trigger: ( + sourceId: string, + rowActionId: string, + rowId: string + ) => Promise +} + +export const buildRowActionEndpoints = ( + API: BaseAPIClient +): RowActionEndpoints => ({ /** * Gets the available row actions for a table. * @param tableId the ID of the table */ fetch: async tableId => { - const res = await API.get({ - url: `/api/tables/${tableId}/actions`, - }) - return res?.actions || {} + return ( + await API.get({ + url: `/api/tables/${tableId}/actions`, + }) + ).actions }, /** @@ -15,8 +48,8 @@ export const buildRowActionEndpoints = API => ({ * @param name the name of the row action * @param tableId the ID of the table */ - create: async ({ name, tableId }) => { - return await API.post({ + create: async (tableId, name) => { + return await API.post({ url: `/api/tables/${tableId}/actions`, body: { name, @@ -24,27 +57,12 @@ export const buildRowActionEndpoints = API => ({ }) }, - /** - * Updates a row action. - * @param name the new name of the row action - * @param tableId the ID of the table - * @param rowActionId the ID of the row action to update - */ - update: async ({ tableId, rowActionId, name }) => { - return await API.put({ - url: `/api/tables/${tableId}/actions/${rowActionId}`, - body: { - name, - }, - }) - }, - /** * Deletes a row action. * @param tableId the ID of the table * @param rowActionId the ID of the row action to delete */ - delete: async ({ tableId, rowActionId }) => { + delete: async (tableId, rowActionId) => { return await API.delete({ url: `/api/tables/${tableId}/actions/${rowActionId}`, }) @@ -56,7 +74,7 @@ export const buildRowActionEndpoints = API => ({ * @param rowActionId the ID of the row action * @param viewId the ID of the view */ - enableView: async ({ tableId, rowActionId, viewId }) => { + enableView: async (tableId, rowActionId, viewId) => { return await API.post({ url: `/api/tables/${tableId}/actions/${rowActionId}/permissions/${viewId}`, }) @@ -68,7 +86,7 @@ export const buildRowActionEndpoints = API => ({ * @param rowActionId the ID of the row action * @param viewId the ID of the view */ - disableView: async ({ tableId, rowActionId, viewId }) => { + disableView: async (tableId, rowActionId, viewId) => { return await API.delete({ url: `/api/tables/${tableId}/actions/${rowActionId}/permissions/${viewId}`, }) @@ -79,8 +97,8 @@ export const buildRowActionEndpoints = API => ({ * @param tableId the ID of the table * @param rowActionId the ID of the row action to trigger */ - trigger: async ({ sourceId, rowActionId, rowId }) => { - return await API.post({ + trigger: async (sourceId, rowActionId, rowId) => { + return await API.post({ url: `/api/tables/${sourceId}/actions/${rowActionId}/trigger`, body: { rowId, diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index f3cf6b034f..db8c8856ac 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -23,6 +23,7 @@ import { QueryEndpoints } from "./queries" import { RelationshipEndpoints } from "./relationships" import { RoleEndpoints } from "./roles" import { RouteEndpoints } from "./routes" +import { RowActionEndpoints } from "./rowActions" export enum HTTPMethod { POST = "POST", @@ -117,4 +118,4 @@ export type APIClient = BaseAPIClient & QueryEndpoints & RelationshipEndpoints & RoleEndpoints & - RouteEndpoints & { [key: string]: any } + RouteEndpoints & { rowActions: RowActionEndpoints; [key: string]: any } From b546512a2f3941d56d0ad3c07def3802568b9382 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 10:40:00 +0000 Subject: [PATCH 038/103] Fix TS errors --- packages/frontend-core/src/api/licensing.ts | 6 +++--- packages/frontend-core/src/api/types.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/frontend-core/src/api/licensing.ts b/packages/frontend-core/src/api/licensing.ts index 9211f5a7b0..8d226835b5 100644 --- a/packages/frontend-core/src/api/licensing.ts +++ b/packages/frontend-core/src/api/licensing.ts @@ -6,7 +6,7 @@ import { GetOfflineLicenseTokenResponse, QuotaUsage, } from "@budibase/types" -import { BaseAPIClient } from "./types" +import { APIError, BaseAPIClient } from "./types" export interface LicensingEndpoints { activateLicenseKey: (licenseKey: string) => Promise @@ -40,7 +40,7 @@ export const buildLicensingEndpoints = ( return await API.get({ url: "/api/global/license/key", }) - } catch (e) { + } catch (e: any) { if (e.status !== 404) { throw e } @@ -66,7 +66,7 @@ export const buildLicensingEndpoints = ( return await API.get({ url: "/api/global/license/offline", }) - } catch (e) { + } catch (e: any) { if (e.status !== 404) { throw e } diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index db8c8856ac..a887b67750 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -85,10 +85,10 @@ export type BaseAPIClient = { export type APIError = { message?: string + url?: string + method?: HTTPMethod json: any status: number - url: string - method: HTTPMethod handled: boolean suppressErrors: boolean } From 86c34cc5494cce3c930e8da1860f54f0e210c936 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 11:02:31 +0000 Subject: [PATCH 039/103] Fix types --- packages/frontend-core/src/api/index.ts | 11 +++++------ packages/frontend-core/src/api/types.ts | 4 ++-- packages/frontend-core/tsconfig.json | 5 ++++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/frontend-core/src/api/index.ts b/packages/frontend-core/src/api/index.ts index bc55913d3a..23d8576e5b 100644 --- a/packages/frontend-core/src/api/index.ts +++ b/packages/frontend-core/src/api/index.ts @@ -108,7 +108,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { } // Performs an API call to the server. - const makeApiCall = async ( + const makeApiCall = async ( callConfig: APICallConfig ): Promise => { let { json, method, external, body, url, parseResponse, suppressErrors } = @@ -131,7 +131,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { } // Build request body - let requestBody: RequestT | string = body + let requestBody: any = body if (json) { try { requestBody = JSON.stringify(body) @@ -146,7 +146,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { response = await fetch(url, { method, headers, - body: requestBody as any, + body: requestBody, credentials: "same-origin", }) } catch (error) { @@ -187,7 +187,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { // Performs an API call to the server and caches the response. // Future invocation for this URL will return the cached result instead of // hitting the server again. - const makeCachedApiCall = async ( + const makeCachedApiCall = async ( callConfig: APICallConfig ): Promise => { const identifier = callConfig.url @@ -201,7 +201,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { // Constructs an API call function for a particular HTTP method const requestApiCall = (method: HTTPMethod) => - async ( + async ( params: APICallParams ): Promise => { try { @@ -211,7 +211,6 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => { suppressErrors: false, cache: false, method, - body: params.body, ...params, } let { url, cache, external } = callConfig diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index a887b67750..a2604a86fb 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -42,14 +42,14 @@ export type APIClientConfig = { onMigrationDetected?: (migration: string) => void } -export type APICallConfig = { +export type APICallConfig = { method: HTTPMethod url: string - body: RequestT json: boolean external: boolean suppressErrors: boolean cache: boolean + body?: RequestT parseResponse?: (response: Response) => Promise | ResponseT } diff --git a/packages/frontend-core/tsconfig.json b/packages/frontend-core/tsconfig.json index 506c8dfd5a..3900034413 100644 --- a/packages/frontend-core/tsconfig.json +++ b/packages/frontend-core/tsconfig.json @@ -2,10 +2,13 @@ "compilerOptions": { "target": "ESNext", "moduleResolution": "bundler", + "skipLibCheck": true, "paths": { "@budibase/types": ["../types/src"], "@budibase/shared-core": ["../shared-core/src"], "@budibase/bbui": ["../bbui/src"] } - } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] } From 2361155554ec443ece27058a2a2b633e5d6361c3 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 11:06:23 +0000 Subject: [PATCH 040/103] Lint --- packages/frontend-core/src/api/licensing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/api/licensing.ts b/packages/frontend-core/src/api/licensing.ts index 8d226835b5..a9e6b7e7b3 100644 --- a/packages/frontend-core/src/api/licensing.ts +++ b/packages/frontend-core/src/api/licensing.ts @@ -6,7 +6,7 @@ import { GetOfflineLicenseTokenResponse, QuotaUsage, } from "@budibase/types" -import { APIError, BaseAPIClient } from "./types" +import { BaseAPIClient } from "./types" export interface LicensingEndpoints { activateLicenseKey: (licenseKey: string) => Promise From c4caa89338bd9c5d418dcc5e49f6aa0ef7acd840 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 11:19:12 +0000 Subject: [PATCH 041/103] Update lock after removing account portal --- yarn.lock | 6 ------ 1 file changed, 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index e6bbff411e..8fa72c25c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2319,7 +2319,6 @@ version "0.0.0" dependencies: scim-patch "^0.8.1" - zod "^3.23.8" "@bull-board/api@5.10.2": version "5.10.2" @@ -12310,11 +12309,6 @@ husky@^8.0.3: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== -husky@^9.1.4: - version "9.1.7" - resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" - integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== - ical-generator@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ical-generator/-/ical-generator-4.1.0.tgz#2a336c951864c5583a2aa715d16f2edcdfd2d90b" From c6bad69cdae9c70cbf2ada5fe85cbe871357cbe7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 11:29:07 +0000 Subject: [PATCH 042/103] Fix tests --- packages/builder/src/stores/builder/tests/navigation.test.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/builder/src/stores/builder/tests/navigation.test.js b/packages/builder/src/stores/builder/tests/navigation.test.js index 365b7f497b..f3775e1ed5 100644 --- a/packages/builder/src/stores/builder/tests/navigation.test.js +++ b/packages/builder/src/stores/builder/tests/navigation.test.js @@ -264,10 +264,7 @@ describe("Navigation store", () => { await ctx.test.navigationStore.save(update) - expect(saveSpy).toHaveBeenCalledWith({ - appId: "testing_123", - metadata: { navigation: update }, - }) + expect(saveSpy).toHaveBeenCalledWith("testing_123", { navigation: update }) expect(ctx.test.store.links.length).toBe(3) From 2d46939fb184be4be174b24c2046cab5e559869c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 11:50:23 +0000 Subject: [PATCH 043/103] Type row endpoints --- .../DataTable/buttons/ExportButton.svelte | 14 +-- packages/client/src/utils/buttonActions.js | 8 +- .../src/api/{rows.js => rows.ts} | 93 ++++++++++--------- packages/frontend-core/src/api/types.ts | 4 +- .../grid/stores/datasources/table.js | 5 +- .../grid/stores/datasources/viewV2.js | 5 +- 6 files changed, 61 insertions(+), 68 deletions(-) rename packages/frontend-core/src/api/{rows.js => rows.ts} (51%) diff --git a/packages/builder/src/components/backend/DataTable/buttons/ExportButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/ExportButton.svelte index e9352045ea..a43629a5a9 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/ExportButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/ExportButton.svelte @@ -63,21 +63,15 @@ } const exportFilteredData = async () => { - let payload = { - tableId: view, - format: exportFormat, - search: { - paginate: false, - }, - } + let payload = {} if (selectedRows?.length) { payload.rows = selectedRows.map(row => row._id) } if (sorting) { - payload.search.sort = sorting.sortColumn - payload.search.sortOrder = sorting.sortOrder + payload.sort = sorting.sortColumn + payload.sortOrder = sorting.sortOrder } - return await API.exportRows(payload) + return await API.exportRows(view, exportFormat, payload) } const exportData = async () => { diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 010e1fea43..9d0bddcc92 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -147,7 +147,7 @@ const fetchRowHandler = async action => { if (tableId && rowId) { try { - const row = await API.fetchRow({ tableId, rowId }) + const row = await API.fetchRow(tableId, rowId) return { row } } catch (error) { @@ -192,7 +192,7 @@ const deleteRowHandler = async action => { return false } - const resp = await API.deleteRows({ tableId, rows: requestConfig }) + const resp = await API.deleteRows(tableId, requestConfig) if (!notificationOverride) { notificationStore.actions.success( @@ -378,10 +378,8 @@ const exportDataHandler = async action => { if (typeof rows[0] !== "string") { rows = rows.map(row => row._id) } - const data = await API.exportRows({ - tableId, + const data = await API.exportRows(tableId, type, { rows, - format: type, columns: columns?.map(column => column.name || column), delimiter, customHeaders, diff --git a/packages/frontend-core/src/api/rows.js b/packages/frontend-core/src/api/rows.ts similarity index 51% rename from packages/frontend-core/src/api/rows.js rename to packages/frontend-core/src/api/rows.ts index 0a0d48da43..7c61a05246 100644 --- a/packages/frontend-core/src/api/rows.js +++ b/packages/frontend-core/src/api/rows.ts @@ -1,13 +1,41 @@ -export const buildRowEndpoints = API => ({ +import { + DeleteRow, + DeleteRows, + ExportRowsRequest, + ExportRowsResponse, + GetRowResponse, + PatchRowRequest, + PatchRowResponse, + Row, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface RowEndpoints { + fetchRow: (tableId: string, rowId: string) => Promise + saveRow: ( + row: Row, + suppressErrors?: boolean + ) => Promise + patchRow: ( + row: PatchRowRequest, + suppressErrors?: boolean + ) => Promise + deleteRow: (sourceId: string, id: string) => Promise + deleteRows: (sourceId: string, rows: (Row | string)[]) => Promise + exportRows: ( + tableId: string, + format: string, + data: ExportRowsRequest + ) => Promise +} + +export const buildRowEndpoints = (API: BaseAPIClient): RowEndpoints => ({ /** * Fetches data about a certain row in a table. * @param tableId the ID of the table to fetch from * @param rowId the ID of the row to fetch */ - fetchRow: async ({ tableId, rowId }) => { - if (!tableId || !rowId) { - return null - } + fetchRow: async (tableId, rowId) => { return await API.get({ url: `/api/${tableId}/rows/${rowId}`, }) @@ -36,12 +64,8 @@ export const buildRowEndpoints = API => ({ * @param suppressErrors whether or not to suppress error notifications */ patchRow: async (row, suppressErrors = false) => { - const resourceId = row?._viewId || row?.tableId - if (!resourceId) { - return - } return await API.patch({ - url: `/api/${resourceId}/rows`, + url: `/api/${row.tableId}/rows`, body: row, suppressErrors, }) @@ -49,34 +73,31 @@ export const buildRowEndpoints = API => ({ /** * Deletes a row from a table. - * @param tableId the ID of the table or view to delete from + * @param sourceId the ID of the table or view to delete from * @param rowId the ID of the row to delete - * @param revId the rev of the row to delete */ - deleteRow: async ({ tableId, rowId, revId }) => { - if (!tableId || !rowId) { - return - } - return await API.delete({ - url: `/api/${tableId}/rows`, + deleteRow: async (sourceId, rowId) => { + return await API.delete({ + url: `/api/${sourceId}/rows`, body: { _id: rowId, - _rev: revId, }, }) }, /** * Deletes multiple rows from a table. - * @param tableId the table or view ID to delete the rows from + * @param sourceId the table or view ID to delete the rows from * @param rows the array of rows to delete */ - deleteRows: async ({ tableId, rows }) => { - rows?.forEach(row => { - delete row?._viewId + deleteRows: async (sourceId, rows) => { + rows?.forEach((row: Row | string) => { + if (typeof row === "object") { + delete row?._viewId + } }) - return await API.delete({ - url: `/api/${tableId}/rows`, + return await API.delete({ + url: `/api/${sourceId}/rows`, body: { rows, }, @@ -86,29 +107,13 @@ export const buildRowEndpoints = API => ({ /** * Exports rows. * @param tableId the table ID to export the rows from - * @param rows the array of rows to export * @param format the format to export (csv or json) - * @param columns which columns to export (all if undefined) - * @param delimiter how values should be separated in a CSV (default is comma) + * @param data the export options */ - exportRows: async ({ - tableId, - rows, - format, - columns, - search, - delimiter, - customHeaders, - }) => { + exportRows: async (tableId, format, data) => { return await API.post({ url: `/api/${tableId}/rows/exportRows?format=${format}`, - body: { - rows, - columns, - delimiter, - customHeaders, - ...search, - }, + body: data, parseResponse: async response => { return await response.text() }, diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index a2604a86fb..559bb6adfa 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -24,6 +24,7 @@ import { RelationshipEndpoints } from "./relationships" import { RoleEndpoints } from "./roles" import { RouteEndpoints } from "./routes" import { RowActionEndpoints } from "./rowActions" +import { RowEndpoints } from "./rows" export enum HTTPMethod { POST = "POST", @@ -118,4 +119,5 @@ export type APIClient = BaseAPIClient & QueryEndpoints & RelationshipEndpoints & RoleEndpoints & - RouteEndpoints & { rowActions: RowActionEndpoints; [key: string]: any } + RouteEndpoints & + RowEndpoints & { rowActions: RowActionEndpoints; [key: string]: any } diff --git a/packages/frontend-core/src/components/grid/stores/datasources/table.js b/packages/frontend-core/src/components/grid/stores/datasources/table.js index e415f5914b..847c4f00b7 100644 --- a/packages/frontend-core/src/components/grid/stores/datasources/table.js +++ b/packages/frontend-core/src/components/grid/stores/datasources/table.js @@ -19,10 +19,7 @@ export const createActions = context => { } const deleteRows = async rows => { - await API.deleteRows({ - tableId: get(datasource).tableId, - rows, - }) + await API.deleteRows(get(datasource).tableId, rows) } const isDatasourceValid = datasource => { diff --git a/packages/frontend-core/src/components/grid/stores/datasources/viewV2.js b/packages/frontend-core/src/components/grid/stores/datasources/viewV2.js index 4a4a91658c..4df707d1c8 100644 --- a/packages/frontend-core/src/components/grid/stores/datasources/viewV2.js +++ b/packages/frontend-core/src/components/grid/stores/datasources/viewV2.js @@ -24,10 +24,7 @@ export const createActions = context => { } const deleteRows = async rows => { - await API.deleteRows({ - tableId: get(datasource).id, - rows, - }) + await API.deleteRows(get(datasource).id, rows) } const getRow = async id => { From 9b16849fe6bf0b534f9d8c3311f28887529b590e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 11:53:07 +0000 Subject: [PATCH 044/103] Type screen endpoints --- .../builder/src/stores/builder/screens.js | 7 +---- packages/frontend-core/src/api/screens.js | 23 -------------- packages/frontend-core/src/api/screens.ts | 31 +++++++++++++++++++ packages/frontend-core/src/api/types.ts | 4 ++- 4 files changed, 35 insertions(+), 30 deletions(-) delete mode 100644 packages/frontend-core/src/api/screens.js create mode 100644 packages/frontend-core/src/api/screens.ts diff --git a/packages/builder/src/stores/builder/screens.js b/packages/builder/src/stores/builder/screens.js index 10c4265e73..55fa3d5433 100644 --- a/packages/builder/src/stores/builder/screens.js +++ b/packages/builder/src/stores/builder/screens.js @@ -344,12 +344,7 @@ export class ScreenStore extends BudiStore { let deleteUrls = [] screensToDelete.forEach(screen => { // Delete the screen - promises.push( - API.deleteScreen({ - screenId: screen._id, - screenRev: screen._rev, - }) - ) + promises.push(API.deleteScreen(screen._id, screen._rev)) // Remove links to this screen deleteUrls.push(screen.routing.route) }) diff --git a/packages/frontend-core/src/api/screens.js b/packages/frontend-core/src/api/screens.js deleted file mode 100644 index 1daa79153b..0000000000 --- a/packages/frontend-core/src/api/screens.js +++ /dev/null @@ -1,23 +0,0 @@ -export const buildScreenEndpoints = API => ({ - /** - * Saves a screen definition - * @param screen the screen to save - */ - saveScreen: async screen => { - return await API.post({ - url: "/api/screens", - body: screen, - }) - }, - - /** - * Deletes a screen. - * @param screenId the ID of the screen to delete - * @param screenRev the rev of the screen to delete - */ - deleteScreen: async ({ screenId, screenRev }) => { - return await API.delete({ - url: `/api/screens/${screenId}/${screenRev}`, - }) - }, -}) diff --git a/packages/frontend-core/src/api/screens.ts b/packages/frontend-core/src/api/screens.ts new file mode 100644 index 0000000000..27b4dc482d --- /dev/null +++ b/packages/frontend-core/src/api/screens.ts @@ -0,0 +1,31 @@ +import { Screen } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface ScreenEndpoints { + saveScreen: (screen: Screen) => Promise + deleteScreen: (id: string, rev: string) => Promise<{ message: string }> +} + +export const buildScreenEndpoints = (API: BaseAPIClient): ScreenEndpoints => ({ + /** + * Saves a screen definition + * @param screen the screen to save + */ + saveScreen: async screen => { + return await API.post({ + url: "/api/screens", + body: screen, + }) + }, + + /** + * Deletes a screen. + * @param id the ID of the screen to delete + * @param rev the rev of the screen to delete + */ + deleteScreen: async (id, rev) => { + return await API.delete({ + url: `/api/screens/${id}/${rev}`, + }) + }, +}) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 559bb6adfa..6b87fcd53e 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -25,6 +25,7 @@ import { RoleEndpoints } from "./roles" import { RouteEndpoints } from "./routes" import { RowActionEndpoints } from "./rowActions" import { RowEndpoints } from "./rows" +import { ScreenEndpoints } from "./screens" export enum HTTPMethod { POST = "POST", @@ -120,4 +121,5 @@ export type APIClient = BaseAPIClient & RelationshipEndpoints & RoleEndpoints & RouteEndpoints & - RowEndpoints & { rowActions: RowActionEndpoints; [key: string]: any } + RowEndpoints & + ScreenEndpoints & { rowActions: RowActionEndpoints; [key: string]: any } From a585ba1785b7397d0d0fc9aa83d317cd21b86af7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 12:00:52 +0000 Subject: [PATCH 045/103] Type self endpoints --- .../src/api/{self.js => self.ts} | 26 ++++++++++++++++--- packages/frontend-core/src/api/types.ts | 4 ++- 2 files changed, 25 insertions(+), 5 deletions(-) rename packages/frontend-core/src/api/{self.js => self.ts} (58%) diff --git a/packages/frontend-core/src/api/self.js b/packages/frontend-core/src/api/self.ts similarity index 58% rename from packages/frontend-core/src/api/self.js rename to packages/frontend-core/src/api/self.ts index cdd5eaba53..9e8c806b2f 100644 --- a/packages/frontend-core/src/api/self.js +++ b/packages/frontend-core/src/api/self.ts @@ -1,11 +1,30 @@ -export const buildSelfEndpoints = API => ({ +import { + ContextUser, + UpdateSelfRequest, + UpdateSelfResponse, + User, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface SelfEndpoints { + updateSelf: (user: UpdateSelfRequest) => Promise + + // Missing request or response types + generateAPIKey: () => Promise + fetchDeveloperInfo: () => Promise + + // There are flags and session attributes mixed in to the user + fetchBuilderSelf: () => Promise + fetchSelf: () => Promise<(ContextUser | {}) & { [key: string]: any }> +} + +export const buildSelfEndpoints = (API: BaseAPIClient): SelfEndpoints => ({ /** * Using the logged in user, this will generate a new API key, * assuming the user is a builder. - * @return {Promise} returns the API response, including an API key. */ generateAPIKey: async () => { - const response = await API.post({ + const response = await API.post({ url: "/api/global/self/api_key", }) return response?.apiKey @@ -13,7 +32,6 @@ export const buildSelfEndpoints = API => ({ /** * retrieves the API key for the logged in user. - * @return {Promise} An object containing the user developer information. */ fetchDeveloperInfo: async () => { return API.get({ diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 6b87fcd53e..75acab3a66 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -26,6 +26,7 @@ import { RouteEndpoints } from "./routes" import { RowActionEndpoints } from "./rowActions" import { RowEndpoints } from "./rows" import { ScreenEndpoints } from "./screens" +import { SelfEndpoints } from "./self" export enum HTTPMethod { POST = "POST", @@ -122,4 +123,5 @@ export type APIClient = BaseAPIClient & RoleEndpoints & RouteEndpoints & RowEndpoints & - ScreenEndpoints & { rowActions: RowActionEndpoints; [key: string]: any } + ScreenEndpoints & + SelfEndpoints & { rowActions: RowActionEndpoints; [key: string]: any } From 3f15216f9a41a65fcca459d0b2970e16bcbded61 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 13:53:54 +0000 Subject: [PATCH 046/103] Lint --- packages/frontend-core/src/api/rows.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/frontend-core/src/api/rows.ts b/packages/frontend-core/src/api/rows.ts index 7c61a05246..ce61c7bfa7 100644 --- a/packages/frontend-core/src/api/rows.ts +++ b/packages/frontend-core/src/api/rows.ts @@ -2,7 +2,6 @@ import { DeleteRow, DeleteRows, ExportRowsRequest, - ExportRowsResponse, GetRowResponse, PatchRowRequest, PatchRowResponse, From 66d7fb23b870eb9e1098b181f4aa1c7765f2c64c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 13:55:15 +0000 Subject: [PATCH 047/103] Fix TS error --- packages/frontend-core/src/api/rows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/api/rows.ts b/packages/frontend-core/src/api/rows.ts index ce61c7bfa7..8c38aea0ba 100644 --- a/packages/frontend-core/src/api/rows.ts +++ b/packages/frontend-core/src/api/rows.ts @@ -14,7 +14,7 @@ export interface RowEndpoints { saveRow: ( row: Row, suppressErrors?: boolean - ) => Promise + ) => Promise patchRow: ( row: PatchRowRequest, suppressErrors?: boolean From b329187591dc3c31b0e2b1ca7a41ae74c054ea44 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 15:09:30 +0000 Subject: [PATCH 048/103] Type table endpoints --- .../DataTable/buttons/ImportButton.svelte | 6 +- .../ExistingTableDataImport.svelte | 6 +- .../TableNavigator/TableDataImport.svelte | 2 +- packages/builder/src/stores/builder/tables.js | 5 +- packages/client/src/api/patches.js | 7 +- packages/frontend-core/src/api/tables.js | 152 -------------- packages/frontend-core/src/api/tables.ts | 190 ++++++++++++++++++ packages/frontend-core/src/api/types.ts | 4 +- .../grid/controls/MigrationModal.svelte | 10 +- .../grid/stores/datasources/table.js | 3 +- .../frontend-core/src/fetch/TableFetch.js | 3 +- 11 files changed, 207 insertions(+), 181 deletions(-) delete mode 100644 packages/frontend-core/src/api/tables.js create mode 100644 packages/frontend-core/src/api/tables.ts diff --git a/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte index a2e7a2a194..f96492a570 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte @@ -30,11 +30,7 @@ const importData = async () => { try { loading = true - await API.importTableData({ - tableId, - rows, - identifierFields, - }) + await API.importTableData(tableId, rows, identifierFields) notifications.success("Rows successfully imported") popover.hide() } catch (error) { diff --git a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte index ce966d0daa..e4601b5372 100644 --- a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte @@ -128,11 +128,7 @@ allValid = false if (rows.length > 0) { - const response = await API.validateExistingTableImport({ - rows, - tableId, - }) - + const response = await API.validateExistingTableImport(rows, tableId) validation = response.schemaValidation invalidColumns = response.invalidColumns allValid = response.allValid diff --git a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte index 1694ec8b0e..80957a90b3 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte @@ -147,7 +147,7 @@ loading = true try { if (rows.length > 0) { - const response = await API.validateNewTableImport({ rows, schema }) + const response = await API.validateNewTableImport(rows, schema) validation = response.schemaValidation allValid = response.allValid errors = response.errors diff --git a/packages/builder/src/stores/builder/tables.js b/packages/builder/src/stores/builder/tables.js index 88b26929ad..8b3424f507 100644 --- a/packages/builder/src/stores/builder/tables.js +++ b/packages/builder/src/stores/builder/tables.js @@ -110,10 +110,7 @@ export function createTablesStore() { if (!table?._id) { return } - await API.deleteTable({ - tableId: table._id, - tableRev: table._rev || "rev", - }) + await API.deleteTable(table._id, table._rev || "rev") replaceTable(table._id, null) } diff --git a/packages/client/src/api/patches.js b/packages/client/src/api/patches.js index faad9c81ec..5413379b72 100644 --- a/packages/client/src/api/patches.js +++ b/packages/client/src/api/patches.js @@ -77,12 +77,11 @@ export const patchAPI = API => { return await enrichRows(rows, tableId) } const searchTable = API.searchTable - API.searchTable = async params => { - const tableId = params?.tableId - const output = await searchTable(params) + API.searchTable = async (sourceId, opts) => { + const output = await searchTable(sourceId, opts) return { ...output, - rows: await enrichRows(output?.rows, tableId), + rows: await enrichRows(output.rows, sourceId), } } const fetchViewData = API.fetchViewData diff --git a/packages/frontend-core/src/api/tables.js b/packages/frontend-core/src/api/tables.js deleted file mode 100644 index dc9008e4eb..0000000000 --- a/packages/frontend-core/src/api/tables.js +++ /dev/null @@ -1,152 +0,0 @@ -export const buildTableEndpoints = API => ({ - /** - * Fetches a table definition. - * Since definitions cannot change at runtime, the result is cached. - * @param tableId the ID of the table to fetch - */ - fetchTableDefinition: async tableId => { - return await API.get({ - url: `/api/tables/${tableId}`, - cache: true, - }) - }, - - /** - * Fetches all rows from a table. - * @param tableId the ID of the table for fetch - */ - fetchTableData: async tableId => { - return await API.get({ url: `/api/${tableId}/rows` }) - }, - - /** - * Searches a table using Lucene. - * @param tableId the ID of the table to search - * @param query the lucene search query - * @param bookmark the current pagination bookmark - * @param limit the number of rows to retrieve - * @param sort the field to sort by - * @param sortOrder the order to sort by - * @param sortType the type to sort by, either numerically or alphabetically - * @param paginate whether to paginate the data - */ - searchTable: async ({ - tableId, - query, - bookmark, - limit, - sort, - sortOrder, - sortType, - paginate, - }) => { - if (!tableId) { - return { - rows: [], - } - } - return await API.post({ - url: `/api/${tableId}/search`, - body: { - ...(query ? { query } : {}), - bookmark, - limit, - sort, - sortOrder, - sortType, - paginate, - }, - }) - }, - - /** - * Imports data into an existing table - * @param tableId the table ID to import to - * @param rows the data import object - * @param identifierFields column names to be used as keys for overwriting existing rows - */ - importTableData: async ({ tableId, rows, identifierFields }) => { - return await API.post({ - url: `/api/tables/${tableId}/import`, - body: { - rows, - identifierFields, - }, - }) - }, - csvToJson: async csvString => { - return await API.post({ - url: "/api/convert/csvToJson", - body: { - csvString, - }, - }) - }, - - /** - * Gets a list of tables. - */ - getTables: async () => { - return await API.get({ - url: "/api/tables", - }) - }, - - /** - * Get a single table based on table ID. - */ - getTable: async tableId => { - return await API.get({ - url: `/api/tables/${tableId}`, - }) - }, - - /** - * Saves a table. - * @param table the table to save - */ - saveTable: async table => { - return await API.post({ - url: "/api/tables", - body: table, - }) - }, - - /** - * Deletes a table. - * @param tableId the ID of the table to delete - * @param tableRev the rev of the table to delete - */ - deleteTable: async ({ tableId, tableRev }) => { - return await API.delete({ - url: `/api/tables/${tableId}/${tableRev}`, - }) - }, - validateNewTableImport: async ({ rows, schema }) => { - return await API.post({ - url: "/api/tables/validateNewTableImport", - body: { - rows, - schema, - }, - }) - }, - validateExistingTableImport: async ({ rows, tableId }) => { - return await API.post({ - url: "/api/tables/validateExistingTableImport", - body: { - rows, - tableId, - }, - }) - }, - migrateColumn: async ({ tableId, oldColumn, newColumn }) => { - return await API.post({ - url: `/api/tables/${tableId}/migrate`, - body: { - oldColumn, - newColumn, - }, - }) - }, -}) diff --git a/packages/frontend-core/src/api/tables.ts b/packages/frontend-core/src/api/tables.ts new file mode 100644 index 0000000000..c37c966f95 --- /dev/null +++ b/packages/frontend-core/src/api/tables.ts @@ -0,0 +1,190 @@ +import { + BulkImportRequest, + BulkImportResponse, + CsvToJsonRequest, + CsvToJsonResponse, + FetchTablesResponse, + MigrateRequest, + MigrateResponse, + Row, + SaveTableRequest, + SaveTableResponse, + SearchRowRequest, + PaginatedSearchRowResponse, + TableResponse, + TableSchema, + ValidateNewTableImportRequest, + ValidateTableImportRequest, + ValidateTableImportResponse, +} from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface TableEndpoints { + fetchTableDefinition: (tableId: string) => Promise + fetchTableData: (tableId: string) => Promise + searchTable: ( + sourceId: string, + opts: SearchRowRequest + ) => Promise + importTableData: ( + tableId: string, + rows: Row[], + identifierFields?: string[] + ) => Promise + csvToJson: (csvString: string) => Promise + getTables: () => Promise + getTable: (tableId: string) => Promise + saveTable: (table: SaveTableRequest) => Promise + deleteTable: (id: string, rev: string) => Promise<{ message: string }> + validateNewTableImport: ( + rows: Row[], + schema: TableSchema + ) => Promise + validateExistingTableImport: ( + rows: Row[], + tableId?: string + ) => Promise + migrateColumn: ( + tableId: string, + oldColumn: string, + newColumn: string + ) => Promise +} + +export const buildTableEndpoints = (API: BaseAPIClient): TableEndpoints => ({ + /** + * Fetches a table definition. + * Since definitions cannot change at runtime, the result is cached. + * @param tableId the ID of the table to fetch + */ + fetchTableDefinition: async tableId => { + return await API.get({ + url: `/api/tables/${tableId}`, + cache: true, + }) + }, + + /** + * Fetches all rows from a table. + * @param sourceId the ID of the table to fetch + */ + fetchTableData: async sourceId => { + return await API.get({ url: `/api/${sourceId}/rows` }) + }, + + /** + * Searches a table using Lucene. + * @param sourceId the ID of the table to search + * @param opts the search opts + */ + searchTable: async (sourceId, opts) => { + return await API.post({ + url: `/api/${sourceId}/search`, + body: opts, + }) + }, + + /** + * Imports data into an existing table + * @param tableId the table ID to import to + * @param rows the data import object + * @param identifierFields column names to be used as keys for overwriting existing rows + */ + importTableData: async (tableId, rows, identifierFields) => { + return await API.post({ + url: `/api/tables/${tableId}/import`, + body: { + rows, + identifierFields, + }, + }) + }, + + /** + * Converts a CSV string to JSON + * @param csvString the CSV string + */ + csvToJson: async csvString => { + return await API.post({ + url: "/api/convert/csvToJson", + body: { + csvString, + }, + }) + }, + + /** + * Gets a list of tables. + */ + getTables: async () => { + return await API.get({ + url: "/api/tables", + }) + }, + + /** + * Get a single table based on table ID. + * Dupe of fetchTableDefinition but not cached? + */ + getTable: async tableId => { + return await API.get({ + url: `/api/tables/${tableId}`, + }) + }, + + /** + * Saves a table. + * @param table the table to save + */ + saveTable: async table => { + return await API.post({ + url: "/api/tables", + body: table, + }) + }, + + /** + * Deletes a table. + * @param id the ID of the table to delete + * @param rev the rev of the table to delete + */ + deleteTable: async (id, rev) => { + return await API.delete({ + url: `/api/tables/${id}/${rev}`, + }) + }, + + validateNewTableImport: async (rows, schema) => { + return await API.post< + ValidateNewTableImportRequest, + ValidateTableImportResponse + >({ + url: "/api/tables/validateNewTableImport", + body: { + rows, + schema, + }, + }) + }, + validateExistingTableImport: async (rows, tableId) => { + return await API.post< + ValidateTableImportRequest, + ValidateTableImportResponse + >({ + url: "/api/tables/validateExistingTableImport", + body: { + rows, + tableId, + }, + }) + }, + migrateColumn: async (tableId, oldColumn, newColumn) => { + return await API.post({ + url: `/api/tables/${tableId}/migrate`, + body: { + oldColumn, + newColumn, + }, + }) + }, +}) diff --git a/packages/frontend-core/src/api/types.ts b/packages/frontend-core/src/api/types.ts index 75acab3a66..424ee8817d 100644 --- a/packages/frontend-core/src/api/types.ts +++ b/packages/frontend-core/src/api/types.ts @@ -27,6 +27,7 @@ import { RowActionEndpoints } from "./rowActions" import { RowEndpoints } from "./rows" import { ScreenEndpoints } from "./screens" import { SelfEndpoints } from "./self" +import { TableEndpoints } from "./tables" export enum HTTPMethod { POST = "POST", @@ -124,4 +125,5 @@ export type APIClient = BaseAPIClient & RouteEndpoints & RowEndpoints & ScreenEndpoints & - SelfEndpoints & { rowActions: RowActionEndpoints; [key: string]: any } + SelfEndpoints & + TableEndpoints & { rowActions: RowActionEndpoints; [key: string]: any } diff --git a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte index 8ecec03e0e..96443311c2 100644 --- a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte +++ b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte @@ -29,11 +29,11 @@ const migrateUserColumn = async () => { try { - await API.migrateColumn({ - tableId: $definition._id, - oldColumn: column.schema.name, - newColumn: newColumnName, - }) + await API.migrateColumn( + $definition._id, + column.schema.name, + newColumnName + ) notifications.success("Column migrated") } catch (e) { notifications.error(`Failed to migrate: ${e.message}`) diff --git a/packages/frontend-core/src/components/grid/stores/datasources/table.js b/packages/frontend-core/src/components/grid/stores/datasources/table.js index 847c4f00b7..bb7bf0835e 100644 --- a/packages/frontend-core/src/components/grid/stores/datasources/table.js +++ b/packages/frontend-core/src/components/grid/stores/datasources/table.js @@ -27,8 +27,7 @@ export const createActions = context => { } const getRow = async id => { - const res = await API.searchTable({ - tableId: get(datasource).tableId, + const res = await API.searchTable(get(datasource).tableId, { limit: 1, query: { equal: { diff --git a/packages/frontend-core/src/fetch/TableFetch.js b/packages/frontend-core/src/fetch/TableFetch.js index ed17c20c79..777d16aa45 100644 --- a/packages/frontend-core/src/fetch/TableFetch.js +++ b/packages/frontend-core/src/fetch/TableFetch.js @@ -19,8 +19,7 @@ export default class TableFetch extends DataFetch { // Search table try { - const res = await this.API.searchTable({ - tableId, + const res = await this.API.searchTable(tableId, { query, limit, sort: sortColumn, From edbcc1f7641a84f646c6b1a026456dbdd02b38e7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 3 Dec 2024 15:16:14 +0000 Subject: [PATCH 049/103] Type template endpoints --- .../src/api/{templates.js => templates.ts} | 21 +++++++++++++++++-- packages/frontend-core/src/api/types.ts | 4 +++- 2 files changed, 22 insertions(+), 3 deletions(-) rename packages/frontend-core/src/api/{templates.js => templates.ts} (52%) diff --git a/packages/frontend-core/src/api/templates.js b/packages/frontend-core/src/api/templates.ts similarity index 52% rename from packages/frontend-core/src/api/templates.js rename to packages/frontend-core/src/api/templates.ts index 660a85d745..dfdc8d26ec 100644 --- a/packages/frontend-core/src/api/templates.js +++ b/packages/frontend-core/src/api/templates.ts @@ -1,4 +1,18 @@ -export const buildTemplateEndpoints = API => ({ +import { Template } from "@budibase/types" +import { BaseAPIClient } from "./types" + +export interface TemplateEndpoints { + getEmailTemplates: () => Promise + + // Missing request or response types + getEmailTemplateDefinitions: () => Promise + saveEmailTemplate: (templaet: any) => Promise + getAppTemplates: () => Promise +} + +export const buildTemplateEndpoints = ( + API: BaseAPIClient +): TemplateEndpoints => ({ /** * Gets the list of email template definitions. */ @@ -10,7 +24,10 @@ export const buildTemplateEndpoints = API => ({ * Gets the list of email templates. */ getEmailTemplates: async () => { - return await API.get({ url: "/api/global/template/email" }) + const res = await API.get