Add optional stronger typing of requests and responses to ensure conformity of flattened function params
This commit is contained in:
parent
6d50f70258
commit
417040f2d5
|
@ -1,92 +0,0 @@
|
|||
export const buildDatasourceEndpoints = API => ({
|
||||
/**
|
||||
* Gets a list of datasources.
|
||||
*/
|
||||
getDatasources: async () => {
|
||||
return await API.get({
|
||||
url: "/api/datasources",
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompts the server to build the schema for a datasource.
|
||||
* @param datasourceId the datasource ID to build the schema for
|
||||
* @param tablesFilter list of specific table names to be build the schema
|
||||
*/
|
||||
buildDatasourceSchema: async ({ datasourceId, tablesFilter }) => {
|
||||
return await API.post({
|
||||
url: `/api/datasources/${datasourceId}/schema`,
|
||||
body: {
|
||||
tablesFilter,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a datasource
|
||||
* @param datasource the datasource to create
|
||||
* @param fetchSchema whether to fetch the schema or not
|
||||
* @param tablesFilter a list of tables to actually fetch rather than simply
|
||||
* all that are accessible.
|
||||
*/
|
||||
createDatasource: async ({ datasource, fetchSchema, tablesFilter }) => {
|
||||
return await API.post({
|
||||
url: "/api/datasources",
|
||||
body: {
|
||||
datasource,
|
||||
fetchSchema,
|
||||
tablesFilter,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates a datasource
|
||||
* @param datasource the datasource to update
|
||||
*/
|
||||
updateDatasource: async datasource => {
|
||||
return await API.put({
|
||||
url: `/api/datasources/${datasource._id}`,
|
||||
body: datasource,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes a datasource.
|
||||
* @param datasourceId the ID of the ddtasource to delete
|
||||
* @param datasourceRev the rev of the datasource to delete
|
||||
*/
|
||||
deleteDatasource: async ({ datasourceId, datasourceRev }) => {
|
||||
return await API.delete({
|
||||
url: `/api/datasources/${datasourceId}/${datasourceRev}`,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Validate a datasource configuration
|
||||
* @param datasource the datasource configuration to validate
|
||||
*/
|
||||
validateDatasource: async datasource => {
|
||||
return await API.post({
|
||||
url: `/api/datasources/verify`,
|
||||
body: { datasource },
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch table names available within the datasource, for filtering out undesired tables
|
||||
* @param datasource the datasource configuration to use for fetching tables
|
||||
*/
|
||||
fetchInfoForDatasource: async datasource => {
|
||||
return await API.post({
|
||||
url: `/api/datasources/info`,
|
||||
body: { datasource },
|
||||
})
|
||||
},
|
||||
|
||||
fetchExternalSchema: async datasourceId => {
|
||||
return await API.get({
|
||||
url: `/api/datasources/${datasourceId}/schema/external`,
|
||||
})
|
||||
},
|
||||
})
|
|
@ -0,0 +1,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<Datasource[]>
|
||||
buildDatasourceSchema: (
|
||||
datasourceId: string,
|
||||
tablesFilter?: BuildSchemaFromSourceRequest["tablesFilter"]
|
||||
) => Promise<BuildSchemaFromSourceResponse>
|
||||
createDatasource: (
|
||||
request: CreateDatasourceRequest
|
||||
) => Promise<CreateDatasourceResponse>
|
||||
updateDatasource: (
|
||||
datasource: Datasource
|
||||
) => Promise<UpdateDatasourceResponse>
|
||||
deleteDatasource: (id: string, rev: string) => Promise<void>
|
||||
validateDatasource: (
|
||||
datasource: Datasource
|
||||
) => Promise<VerifyDatasourceResponse>
|
||||
fetchInfoForDatasource: (
|
||||
datasource: Datasource
|
||||
) => Promise<FetchDatasourceInfoResponse>
|
||||
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<UpdateDatasourceRequest, UpdateDatasourceResponse>({
|
||||
url: `/api/datasources/${datasource._id}`,
|
||||
body: datasource,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes a datasource.
|
||||
*/
|
||||
deleteDatasource: async (id: string, rev: string) => {
|
||||
return await API.delete({
|
||||
url: `/api/datasources/${id}/${rev}`,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Validate a datasource configuration
|
||||
*/
|
||||
validateDatasource: async (datasource: Datasource) => {
|
||||
return await API.post<VerifyDatasourceRequest, VerifyDatasourceResponse>({
|
||||
url: `/api/datasources/verify`,
|
||||
body: { datasource },
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch table names available within the datasource, for filtering out undesired tables
|
||||
*/
|
||||
fetchInfoForDatasource: async (datasource: Datasource) => {
|
||||
return await API.post<
|
||||
FetchDatasourceInfoRequest,
|
||||
FetchDatasourceInfoResponse
|
||||
>({
|
||||
url: `/api/datasources/info`,
|
||||
body: { datasource },
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches the external schema of a datasource
|
||||
*/
|
||||
fetchExternalSchema: async (datasourceId: string) => {
|
||||
return await API.get({
|
||||
url: `/api/datasources/${datasourceId}/schema/external`,
|
||||
})
|
||||
},
|
||||
})
|
|
@ -103,7 +103,9 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
|
|||
}
|
||||
|
||||
// Performs an API call to the server.
|
||||
const makeApiCall = async <T>(callConfig: APICallConfig): Promise<T> => {
|
||||
const makeApiCall = async <RequestT, ResponseT>(
|
||||
callConfig: APICallConfig<RequestT, ResponseT>
|
||||
): Promise<ResponseT> => {
|
||||
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<T>(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 <T>(
|
||||
callConfig: APICallConfig
|
||||
): Promise<T> => {
|
||||
const makeCachedApiCall = async <RequestT = void, ResponseT = void>(
|
||||
callConfig: APICallConfig<RequestT, ResponseT>
|
||||
): Promise<ResponseT> => {
|
||||
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 <T>(params: APICallParams): Promise<T> => {
|
||||
async <RequestT = void, ResponseT = void>(
|
||||
params: APICallParams<RequestT, ResponseT>
|
||||
): Promise<ResponseT> => {
|
||||
try {
|
||||
let callConfig: APICallConfig = {
|
||||
let callConfig: APICallConfig<RequestT, ResponseT> = {
|
||||
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<T>(callConfig)
|
||||
return await handler(callConfig)
|
||||
} catch (error) {
|
||||
if (config?.onError) {
|
||||
config.onError(error)
|
||||
|
|
|
@ -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<RequestT = null, ResponseT = void> = {
|
||||
method: HTTPMethod
|
||||
url: string
|
||||
body: RequestT
|
||||
json: boolean
|
||||
external: boolean
|
||||
suppressErrors: boolean
|
||||
cache: boolean
|
||||
body?: any
|
||||
parseResponse?: <T>(response: Response) => Promise<T> | T
|
||||
parseResponse?: (response: Response) => Promise<ResponseT> | ResponseT
|
||||
}
|
||||
|
||||
export type APICallParams = Pick<APICallConfig, "url"> & Partial<APICallConfig>
|
||||
export type APICallParams<
|
||||
RequestT = null,
|
||||
ResponseT = void
|
||||
> = RequestT extends null
|
||||
? Pick<APICallConfig<RequestT, ResponseT>, "url"> &
|
||||
Partial<APICallConfig<RequestT, ResponseT>>
|
||||
: Pick<APICallConfig<RequestT, ResponseT>, "url" | "body"> &
|
||||
Partial<APICallConfig<RequestT, ResponseT>>
|
||||
|
||||
export type BaseAPIClient = {
|
||||
post: <T>(params: APICallParams) => Promise<T>
|
||||
get: <T>(params: APICallParams) => Promise<T>
|
||||
put: <T>(params: APICallParams) => Promise<T>
|
||||
delete: <T>(params: APICallParams) => Promise<T>
|
||||
patch: <T>(params: APICallParams) => Promise<T>
|
||||
post: <RequestT = null, ResponseT = void>(
|
||||
params: APICallParams<RequestT, ResponseT>
|
||||
) => Promise<ResponseT>
|
||||
get: <ResponseT = void>(
|
||||
params: APICallParams<undefined | null, ResponseT>
|
||||
) => Promise<ResponseT>
|
||||
put: <RequestT = null, ResponseT = void>(
|
||||
params: APICallParams<RequestT, ResponseT>
|
||||
) => Promise<ResponseT>
|
||||
delete: <RequestT = null, ResponseT = void>(
|
||||
params: APICallParams<RequestT, ResponseT>
|
||||
) => Promise<ResponseT>
|
||||
patch: <RequestT = null, ResponseT = void>(
|
||||
params: APICallParams<RequestT, ResponseT>
|
||||
) => Promise<ResponseT>
|
||||
error: (message: string) => void
|
||||
invalidateCache: () => void
|
||||
getAppID: () => string
|
||||
|
@ -58,4 +76,5 @@ export type APIClient = BaseAPIClient &
|
|||
AuthEndpoints &
|
||||
AutomationEndpoints &
|
||||
BackupEndpoints &
|
||||
ConfigEndpoints & { [key: string]: any }
|
||||
ConfigEndpoints &
|
||||
DatasourceEndpoints & { [key: string]: any }
|
||||
|
|
Loading…
Reference in New Issue