mirror of
https://github.com/ToolJet/ToolJet
synced 2026-04-22 22:17:55 +00:00
* refactor: Abstract common oauth component for openapi * chore: comment out redis-server startup in preview script * fix: update PostgreSQL version from 16 to 13 in Dockerfile * fix: update PostgreSQL directory paths from version 16 to 13 in Dockerfile * fix: edit button on encrypted fields * fix: conditionally render dropdowns * fix: Change helpertext for redirect uri in oauthwrapper * fix: query response to display json * fix: copywriting & optional chaining * fix: handle client credentials correctly --------- Co-authored-by: adishM98 Bot <adish.madhu@gmail.com>
150 lines
4.5 KiB
TypeScript
150 lines
4.5 KiB
TypeScript
import {
|
|
QueryResult,
|
|
User,
|
|
App,
|
|
OAuthUnauthorizedClientError,
|
|
QueryError,
|
|
QueryService,
|
|
getRefreshedToken,
|
|
validateAndSetRequestOptionsBasedOnAuthType,
|
|
getAuthUrl,
|
|
} from '@tooljet-plugins/common';
|
|
import { SourceOptions, QueryOptions, RestAPIResult } from './types';
|
|
import got, { HTTPError, OptionsOfTextResponseBody } from 'got';
|
|
import urrl from 'url';
|
|
|
|
export default class Openapi implements QueryService {
|
|
private resolvePathParams(params: any, path: string) {
|
|
let newString = path;
|
|
Object.entries(params).map(([key, value]) => {
|
|
newString = newString.replace(`{${key}}`, value as any);
|
|
});
|
|
return newString;
|
|
}
|
|
|
|
private sanitizeObject(params: any) {
|
|
Object.keys(params).forEach((key) => (params[key] === '' ? delete params[key] : {}));
|
|
return params;
|
|
}
|
|
|
|
private parseValue = (value) => {
|
|
if (typeof value !== 'string') return value;
|
|
try {
|
|
return JSON.parse(value);
|
|
} catch (e) {
|
|
return value;
|
|
}
|
|
};
|
|
|
|
private parseRequest = (obj) => {
|
|
if (!obj) return obj;
|
|
return Object.keys(obj).reduce((acc, key) => {
|
|
acc[key] = this.parseValue(obj[key]);
|
|
return acc;
|
|
}, {});
|
|
};
|
|
|
|
async run(
|
|
sourceOptions: SourceOptions,
|
|
queryOptions: QueryOptions,
|
|
dataSourceId: string,
|
|
dataSourceUpdatedAt: string,
|
|
context?: { user?: User; app?: App }
|
|
): Promise<RestAPIResult> {
|
|
const { host, path, operation, params } = queryOptions;
|
|
const { request, query, header, path: pathParams } = params;
|
|
const resolvedHost = sourceOptions.host || host;
|
|
const url = new URL(resolvedHost + this.resolvePathParams(pathParams, path));
|
|
const parsedRequest = request ? this.parseRequest(request) : undefined;
|
|
const json =
|
|
operation !== 'get' && parsedRequest && Object.keys(parsedRequest).length > 0
|
|
? this.sanitizeObject(parsedRequest)
|
|
: undefined;
|
|
|
|
const _requestOptions: OptionsOfTextResponseBody = {
|
|
method: operation,
|
|
headers: header,
|
|
searchParams: {
|
|
...query,
|
|
},
|
|
};
|
|
|
|
if (json && Object.keys(json).length > 0) {
|
|
_requestOptions.json = json;
|
|
}
|
|
|
|
const authValidatedRequestOptions: QueryResult = await validateAndSetRequestOptionsBasedOnAuthType(
|
|
sourceOptions,
|
|
context,
|
|
_requestOptions,
|
|
{ url }
|
|
);
|
|
const { status, data } = authValidatedRequestOptions;
|
|
if (status === 'needs_oauth') return authValidatedRequestOptions;
|
|
|
|
const requestOptions = data as OptionsOfTextResponseBody;
|
|
|
|
let result = {};
|
|
let requestObject = {};
|
|
let responseObject = {};
|
|
let responseHeaders = {};
|
|
|
|
try {
|
|
const response = await got(url, requestOptions);
|
|
const contentType = response.headers['content-type'];
|
|
|
|
result = contentType && contentType.includes('application/json') ? JSON.parse(response.body) : response.body;
|
|
|
|
requestObject = {
|
|
requestUrl: response.request.requestUrl,
|
|
method: response.request.options.method,
|
|
headers: response.request.options.headers,
|
|
params: urrl.parse(response.request.requestUrl.toString(), true).query,
|
|
};
|
|
|
|
responseObject = {
|
|
body: response.body,
|
|
statusCode: response.statusCode,
|
|
};
|
|
|
|
responseHeaders = response.headers;
|
|
} catch (error) {
|
|
console.log(error);
|
|
|
|
if (error instanceof HTTPError) {
|
|
result = {
|
|
requestObject: {
|
|
requestUrl: error.request.requestUrl,
|
|
requestHeaders: error.request.options.headers,
|
|
requestParams: urrl.parse(error.request.requestUrl.toString(), true).query,
|
|
},
|
|
responseObject: {
|
|
statusCode: error.response.statusCode,
|
|
responseBody: error.response.body,
|
|
},
|
|
responseHeaders: error.response.headers,
|
|
};
|
|
}
|
|
if (sourceOptions['auth_type'] === 'oauth2' && error?.response?.statusCode == 401) {
|
|
throw new OAuthUnauthorizedClientError('Unauthorized status from API server', error.message, result);
|
|
}
|
|
throw new QueryError('Query could not be completed', error.message, result);
|
|
}
|
|
|
|
return {
|
|
status: 'ok',
|
|
data: result,
|
|
request: requestObject,
|
|
response: responseObject,
|
|
responseHeaders,
|
|
};
|
|
}
|
|
|
|
authUrl(sourceOptions: SourceOptions): string {
|
|
return getAuthUrl(sourceOptions);
|
|
}
|
|
|
|
async refreshToken(sourceOptions: any, error: any, userId: string, isAppPublic: boolean) {
|
|
return getRefreshedToken(sourceOptions, error, userId, isAppPublic);
|
|
}
|
|
}
|