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 }