@gravity-ui/gateway 4.1.0 → 4.1.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{build → dist/commonjs}/components/grpc.d.ts +4 -4
- package/{build → dist/commonjs}/components/grpc.js +85 -75
- package/{build → dist/commonjs}/components/mixed.d.ts +4 -4
- package/{build → dist/commonjs}/components/mixed.js +11 -12
- package/{build → dist/commonjs}/components/rest.d.ts +5 -5
- package/{build → dist/commonjs}/components/rest.js +32 -32
- package/{build → dist/commonjs}/constants.d.ts +2 -2
- package/{build → dist/commonjs}/constants.js +25 -18
- package/{build → dist/commonjs}/index.d.ts +8 -7
- package/{build → dist/commonjs}/index.js +32 -44
- package/{build → dist/commonjs}/models/common.d.ts +11 -6
- package/{build → dist/commonjs}/models/context.d.ts +0 -1
- package/dist/commonjs/package.json +3 -0
- package/dist/commonjs/utils/axios.d.ts +3 -0
- package/{build → dist/commonjs}/utils/axios.js +3 -4
- package/{build → dist/commonjs}/utils/common.d.ts +3 -3
- package/{build → dist/commonjs}/utils/common.js +7 -8
- package/{build → dist/commonjs}/utils/create-context-api.d.ts +2 -2
- package/{build → dist/commonjs}/utils/create-context-api.js +5 -6
- package/{build → dist/commonjs}/utils/grpc-reflection.js +21 -12
- package/{build → dist/commonjs}/utils/grpc.js +7 -8
- package/dist/commonjs/utils/overrideEndpoints/index.d.ts +2 -0
- package/dist/commonjs/utils/overrideEndpoints/index.js +4 -0
- package/{build → dist/commonjs}/utils/overrideEndpoints/overrideEndpoints.d.ts +1 -1
- package/{build → dist/commonjs}/utils/overrideEndpoints/overrideEndpoints.js +1 -2
- package/{build → dist/commonjs}/utils/parse-error.d.ts +4 -4
- package/{build → dist/commonjs}/utils/parse-error.js +18 -18
- package/{build → dist/commonjs}/utils/proto-path-resolver.js +23 -17
- package/{build → dist/commonjs}/utils/redact-sensitive-headers.d.ts +1 -2
- package/{build → dist/commonjs}/utils/redact-sensitive-headers.js +1 -2
- package/dist/commonjs/utils/source-dir.d.ts +1 -0
- package/dist/commonjs/utils/source-dir.js +41 -0
- package/{build → dist/commonjs}/utils/typed-api.d.ts +1 -1
- package/{build → dist/commonjs}/utils/typed-api.js +1 -2
- package/{build → dist/commonjs}/utils/validate.js +6 -10
- package/dist/esm/components/grpc.d.ts +24 -0
- package/dist/esm/components/grpc.js +641 -0
- package/dist/esm/components/mixed.d.ts +11 -0
- package/dist/esm/components/mixed.js +62 -0
- package/dist/esm/components/rest.d.ts +8 -0
- package/dist/esm/components/rest.js +349 -0
- package/dist/esm/constants.d.ts +53 -0
- package/dist/esm/constants.js +79 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.js +264 -0
- package/dist/esm/models/common.d.ts +272 -0
- package/dist/esm/models/common.js +5 -0
- package/dist/esm/models/context.d.ts +22 -0
- package/dist/esm/models/context.js +1 -0
- package/dist/esm/models/error.d.ts +12 -0
- package/dist/esm/models/error.js +1 -0
- package/dist/esm/package.json +3 -0
- package/{build → dist/esm}/utils/axios.d.ts +1 -1
- package/dist/esm/utils/axios.js +23 -0
- package/dist/esm/utils/common.d.ts +15 -0
- package/dist/esm/utils/common.js +38 -0
- package/dist/esm/utils/create-context-api.d.ts +4 -0
- package/dist/esm/utils/create-context-api.js +38 -0
- package/dist/esm/utils/grpc-reflection.d.ts +28 -0
- package/dist/esm/utils/grpc-reflection.js +68 -0
- package/dist/esm/utils/grpc.d.ts +5 -0
- package/dist/esm/utils/grpc.js +39 -0
- package/dist/esm/utils/overrideEndpoints/index.d.ts +2 -0
- package/dist/esm/utils/overrideEndpoints/index.js +2 -0
- package/dist/esm/utils/overrideEndpoints/overrideEndpoints.d.ts +17 -0
- package/dist/esm/utils/overrideEndpoints/overrideEndpoints.js +96 -0
- package/dist/esm/utils/parse-error.d.ts +30 -0
- package/dist/esm/utils/parse-error.js +210 -0
- package/dist/esm/utils/proto-path-resolver.d.ts +2 -0
- package/dist/esm/utils/proto-path-resolver.js +23 -0
- package/dist/esm/utils/redact-sensitive-headers.d.ts +3 -0
- package/dist/esm/utils/redact-sensitive-headers.js +12 -0
- package/dist/esm/utils/source-dir.d.ts +1 -0
- package/dist/esm/utils/source-dir.js +4 -0
- package/dist/esm/utils/typed-api.d.ts +2 -0
- package/dist/esm/utils/typed-api.js +3 -0
- package/dist/esm/utils/validate.d.ts +4 -0
- package/dist/esm/utils/validate.js +47 -0
- package/package.json +41 -16
- package/build/utils/overrideEndpoints/index.d.ts +0 -2
- package/build/utils/overrideEndpoints/index.js +0 -4
- /package/bin/{patch.js → patch.cjs} +0 -0
- /package/{build → dist/commonjs}/models/common.js +0 -0
- /package/{build → dist/commonjs}/models/context.js +0 -0
- /package/{build → dist/commonjs}/models/error.d.ts +0 -0
- /package/{build → dist/commonjs}/models/error.js +0 -0
- /package/{build → dist/commonjs}/utils/grpc-reflection.d.ts +0 -0
- /package/{build → dist/commonjs}/utils/grpc.d.ts +0 -0
- /package/{build → dist/commonjs}/utils/proto-path-resolver.d.ts +0 -0
- /package/{build → dist/commonjs}/utils/validate.d.ts +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
export function isExtendedActionEndpoint(endpoint) {
|
|
3
|
+
return (endpoint === null || endpoint === void 0 ? void 0 : endpoint.path) !== undefined;
|
|
4
|
+
}
|
|
5
|
+
export function isExtendedGrpcActionEndpoint(endpoint) {
|
|
6
|
+
return ((endpoint === null || endpoint === void 0 ? void 0 : endpoint.grpcOptions) !== undefined ||
|
|
7
|
+
(endpoint === null || endpoint === void 0 ? void 0 : endpoint.insecure) !== undefined ||
|
|
8
|
+
(endpoint === null || endpoint === void 0 ? void 0 : endpoint.secureWithoutRootCert) !== undefined);
|
|
9
|
+
}
|
|
10
|
+
export function isExtendedRestActionEndpoint(endpoint) {
|
|
11
|
+
return (endpoint === null || endpoint === void 0 ? void 0 : endpoint.axiosConfig) !== undefined;
|
|
12
|
+
}
|
|
13
|
+
export function getKeys(obj) {
|
|
14
|
+
return Object.keys(obj);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* This function should only use to sanitize debugHeaders that are creating in our code
|
|
18
|
+
*/
|
|
19
|
+
export function sanitizeDebugHeaders(debugHeaders) {
|
|
20
|
+
return _.omit(debugHeaders, ['x-api-request-body']);
|
|
21
|
+
}
|
|
22
|
+
export function getHeadersFromMetadata(metadata, prefix = '') {
|
|
23
|
+
return Object.entries(metadata).reduce((headers, [key, values]) => {
|
|
24
|
+
headers[`${prefix}${key}`] = values.filter((value) => typeof value === 'string').join(' ');
|
|
25
|
+
return headers;
|
|
26
|
+
}, {});
|
|
27
|
+
}
|
|
28
|
+
export function handleError(ErrorConstructor, error, ctx, message, extra) {
|
|
29
|
+
if (error instanceof Error) {
|
|
30
|
+
ctx.logError(message, ErrorConstructor.wrap(error), extra);
|
|
31
|
+
}
|
|
32
|
+
else if (typeof error === 'string') {
|
|
33
|
+
ctx.logError(message, { error }, extra);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
ctx.logError(message, error, extra);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ApiActionConfig, ApiByScope, ContextApiWithRoot, GatewayRequest, GatewayResponse, SchemasByScope } from '../models/common.js';
|
|
2
|
+
import { GatewayContext } from '../models/context.js';
|
|
3
|
+
export type RequestContext<Context extends GatewayContext> = Omit<ApiActionConfig<Context, never>, 'args'>;
|
|
4
|
+
export declare function generateContextApi<TFullSchema extends SchemasByScope, Context extends GatewayContext, Req extends GatewayRequest<Context>, Res extends GatewayResponse>(baseApi: ApiByScope<TFullSchema, Context, Req, Res>, requestContext: RequestContext<Context>): ContextApiWithRoot<TFullSchema>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import { getKeys } from './common.js';
|
|
3
|
+
function createContextApiForScope(api, requestContext) {
|
|
4
|
+
return _.reduce(api, (acc, service, serviceName) => {
|
|
5
|
+
acc[serviceName] = _.reduce(service, (accService, action, actionName) => {
|
|
6
|
+
accService[actionName] = (args) => action(Object.assign(Object.assign({}, requestContext), { args })).then((response) => response.responseData ||
|
|
7
|
+
response
|
|
8
|
+
.stream);
|
|
9
|
+
return accService;
|
|
10
|
+
}, {});
|
|
11
|
+
return acc;
|
|
12
|
+
}, {});
|
|
13
|
+
}
|
|
14
|
+
export function generateContextApi(baseApi, requestContext) {
|
|
15
|
+
var _a;
|
|
16
|
+
const contextApi = _.reduce(baseApi, (contextApi, scope, scopeName) => {
|
|
17
|
+
contextApi[scopeName] = createContextApiForScope(scope, requestContext);
|
|
18
|
+
return contextApi;
|
|
19
|
+
}, {});
|
|
20
|
+
const api = contextApi;
|
|
21
|
+
const rootScope = contextApi.root;
|
|
22
|
+
if (rootScope) {
|
|
23
|
+
for (const rootService of getKeys(rootScope)) {
|
|
24
|
+
const curScope = (_a = api[rootService]) !== null && _a !== void 0 ? _a : {};
|
|
25
|
+
for (const rootAction of getKeys(rootScope[rootService])) {
|
|
26
|
+
const rootServiceFunc = rootScope[rootService][rootAction];
|
|
27
|
+
if (curScope[rootAction]) {
|
|
28
|
+
for (const curScopeAction of getKeys(curScope[rootAction])) {
|
|
29
|
+
rootServiceFunc[curScopeAction] = curScope[rootAction][curScopeAction];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
curScope[rootAction] = rootServiceFunc;
|
|
33
|
+
}
|
|
34
|
+
api[rootService] = curScope;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return api;
|
|
38
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ChannelCredentials } from '@grpc/grpc-js';
|
|
2
|
+
type DescriptorExtensionProto = string[] | {
|
|
3
|
+
includeProtoRoots: string[];
|
|
4
|
+
filenames: string[];
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* @param actionEndpoint
|
|
8
|
+
* @param protoKey
|
|
9
|
+
* @param credentials
|
|
10
|
+
* @param grpcOptions
|
|
11
|
+
* @param descriptorExtensionProto
|
|
12
|
+
* @returns Promise<protobufjs.Root>.
|
|
13
|
+
* use toDescriptor for use with protoLoader.
|
|
14
|
+
* use toJSON for get a JSON descriptor.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getCachedReflectionRoot(actionEndpoint: string, protoKey: string, credentials: ChannelCredentials, grpcOptions?: object, descriptorExtensionProto?: DescriptorExtensionProto): Promise<any>;
|
|
17
|
+
/**
|
|
18
|
+
* @param actionEndpoint
|
|
19
|
+
* @param protoKey
|
|
20
|
+
* @param credentials
|
|
21
|
+
* @param grpcOptions
|
|
22
|
+
* @param addToCache
|
|
23
|
+
* @returns Promise<protobufjs.Root>.
|
|
24
|
+
* use toDescriptor for use with protoLoader.
|
|
25
|
+
* use toJSON for get a JSON descriptor.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getReflectionRoot(actionEndpoint: string, protoKey: string, credentials: ChannelCredentials, grpcOptions?: object, addToCache?: boolean): Promise<any>;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import * as protobufjs from 'protobufjs';
|
|
3
|
+
import { patchProtoPathResolver } from './proto-path-resolver.js';
|
|
4
|
+
const reflectionClientsMap = {};
|
|
5
|
+
function getCachedClient(actionEndpoint, credentials, grpcOptions, descriptorExtensionProto) {
|
|
6
|
+
const cacheKey = [actionEndpoint, JSON.stringify([grpcOptions, descriptorExtensionProto])];
|
|
7
|
+
let clientWithCache = _.get(reflectionClientsMap, cacheKey);
|
|
8
|
+
if (!clientWithCache) {
|
|
9
|
+
const grpcReflection = require('grpc-reflection-js');
|
|
10
|
+
let descriptorRoot;
|
|
11
|
+
if (descriptorExtensionProto) {
|
|
12
|
+
descriptorRoot = protobufjs.Root.fromJSON(require('protobufjs/ext/descriptor'));
|
|
13
|
+
if (Array.isArray(descriptorExtensionProto)) {
|
|
14
|
+
descriptorRoot.loadSync(descriptorExtensionProto);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
patchProtoPathResolver(descriptorRoot, descriptorExtensionProto.includeProtoRoots);
|
|
18
|
+
descriptorRoot.loadSync(descriptorExtensionProto.filenames);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const client = new grpcReflection.Client(actionEndpoint, credentials, grpcOptions, undefined, descriptorRoot);
|
|
22
|
+
clientWithCache = { client, reflectionRootPromiseMap: {} };
|
|
23
|
+
_.set(reflectionClientsMap, cacheKey, clientWithCache);
|
|
24
|
+
}
|
|
25
|
+
return clientWithCache;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @param actionEndpoint
|
|
29
|
+
* @param protoKey
|
|
30
|
+
* @param credentials
|
|
31
|
+
* @param grpcOptions
|
|
32
|
+
* @param descriptorExtensionProto
|
|
33
|
+
* @returns Promise<protobufjs.Root>.
|
|
34
|
+
* use toDescriptor for use with protoLoader.
|
|
35
|
+
* use toJSON for get a JSON descriptor.
|
|
36
|
+
*/
|
|
37
|
+
export async function getCachedReflectionRoot(actionEndpoint, protoKey, credentials, grpcOptions, descriptorExtensionProto) {
|
|
38
|
+
const { client, reflectionRootPromiseMap } = getCachedClient(actionEndpoint, credentials, grpcOptions, descriptorExtensionProto);
|
|
39
|
+
const cacheKey = [actionEndpoint, protoKey];
|
|
40
|
+
let cachedRootPromise = _.get(reflectionRootPromiseMap, cacheKey);
|
|
41
|
+
if (!cachedRootPromise) {
|
|
42
|
+
cachedRootPromise = client.fileContainingSymbol(protoKey);
|
|
43
|
+
_.set(reflectionRootPromiseMap, cacheKey, cachedRootPromise);
|
|
44
|
+
cachedRootPromise.catch(() => {
|
|
45
|
+
_.set(reflectionRootPromiseMap, cacheKey, undefined);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const loadedRoot = await cachedRootPromise;
|
|
49
|
+
return loadedRoot;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* @param actionEndpoint
|
|
53
|
+
* @param protoKey
|
|
54
|
+
* @param credentials
|
|
55
|
+
* @param grpcOptions
|
|
56
|
+
* @param addToCache
|
|
57
|
+
* @returns Promise<protobufjs.Root>.
|
|
58
|
+
* use toDescriptor for use with protoLoader.
|
|
59
|
+
* use toJSON for get a JSON descriptor.
|
|
60
|
+
*/
|
|
61
|
+
export async function getReflectionRoot(actionEndpoint, protoKey, credentials, grpcOptions, addToCache) {
|
|
62
|
+
const { client, reflectionRootPromiseMap } = getCachedClient(actionEndpoint, credentials, grpcOptions);
|
|
63
|
+
const loadedRoot = await client.fileContainingSymbol(protoKey);
|
|
64
|
+
if (addToCache) {
|
|
65
|
+
_.set(reflectionRootPromiseMap, [actionEndpoint, protoKey], Promise.resolve(loadedRoot));
|
|
66
|
+
}
|
|
67
|
+
return loadedRoot;
|
|
68
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as grpc from '@grpc/grpc-js';
|
|
2
|
+
import * as protobufjs from 'protobufjs';
|
|
3
|
+
export declare function decodeAnyMessageRecursively(root: protobufjs.Root, message?: unknown, decodeAnyMessageProtoLoaderOptions?: protobufjs.IConversionOptions): unknown;
|
|
4
|
+
export declare function isRetryableError(error?: grpc.ServiceError): boolean;
|
|
5
|
+
export declare function isRecreateServiceError(error?: grpc.ServiceError): boolean;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import { DEFAULT_PROTO_LOADER_OPTIONS, RECREATE_SERVICE_CODES, RETRYABLE_STATUS_CODES, } from '../constants.js';
|
|
3
|
+
function isEncodedMessage(message) {
|
|
4
|
+
return Boolean(message.type_url && message.value);
|
|
5
|
+
}
|
|
6
|
+
export function decodeAnyMessageRecursively(root, message, decodeAnyMessageProtoLoaderOptions) {
|
|
7
|
+
if (!message || typeof message !== 'object') {
|
|
8
|
+
return message;
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(message)) {
|
|
11
|
+
return message.map((innerMessage) => decodeAnyMessageRecursively(root, innerMessage, decodeAnyMessageProtoLoaderOptions));
|
|
12
|
+
}
|
|
13
|
+
if (typeof message === 'object' && !isEncodedMessage(message)) {
|
|
14
|
+
return Object.entries(message).reduce((res, [key, value]) => {
|
|
15
|
+
res[key] = decodeAnyMessageRecursively(root, value, decodeAnyMessageProtoLoaderOptions);
|
|
16
|
+
return res;
|
|
17
|
+
}, {});
|
|
18
|
+
}
|
|
19
|
+
const lastSlashIndex = message.type_url.lastIndexOf('/');
|
|
20
|
+
if (lastSlashIndex < 0) {
|
|
21
|
+
return message;
|
|
22
|
+
}
|
|
23
|
+
const typeName = message.type_url.substring(lastSlashIndex + 1);
|
|
24
|
+
const type = root.lookupType(typeName);
|
|
25
|
+
const decodedMessage = type.toObject(type.decode(message.value), Object.assign(Object.assign({}, DEFAULT_PROTO_LOADER_OPTIONS), decodeAnyMessageProtoLoaderOptions));
|
|
26
|
+
return decodeAnyMessageRecursively(root, decodedMessage, decodeAnyMessageProtoLoaderOptions);
|
|
27
|
+
}
|
|
28
|
+
export function isRetryableError(error) {
|
|
29
|
+
if (!error) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return RETRYABLE_STATUS_CODES.includes(error.code);
|
|
33
|
+
}
|
|
34
|
+
export function isRecreateServiceError(error) {
|
|
35
|
+
if (!error) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return RECREATE_SERVICE_CODES.includes(error.code);
|
|
39
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ActionEndpoint, SchemasByScope } from '../../index.js';
|
|
2
|
+
export interface OverrideParams {
|
|
3
|
+
[serviceKeyScope: string]: {
|
|
4
|
+
[endpointName: string]: ActionEndpoint;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Overrides endpoints in the schema according to overrideParams
|
|
9
|
+
* @param originalSchema Schema for the gateway
|
|
10
|
+
* @param overrideParams Definitions of endpoints for services in the format {[<scope>.<service key>]: {[<endpointName>]: ActionEndpoint}}
|
|
11
|
+
* If <scope> is not specified, root is assumed.
|
|
12
|
+
* Example: '{serviceName: {endpoint: "https://example.com"}}'
|
|
13
|
+
* @param installation
|
|
14
|
+
* @param env
|
|
15
|
+
* @return new overridden schema
|
|
16
|
+
*/
|
|
17
|
+
export declare function overrideEndpoints<T extends SchemasByScope>(originalSchema: T, overrideParams: OverrideParams, installation: string | undefined, env: string | undefined): T;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
function parseOverrideEndpointsParams(params) {
|
|
3
|
+
const parsedParams = [];
|
|
4
|
+
for (const [serviceKeyScope, serviceKeyValues] of Object.entries(params)) {
|
|
5
|
+
for (const [endpointName, config] of Object.entries(serviceKeyValues)) {
|
|
6
|
+
let scope = 'root';
|
|
7
|
+
let serviceKey = serviceKeyScope;
|
|
8
|
+
const pointInd = serviceKey.indexOf('.');
|
|
9
|
+
if (pointInd >= 0) {
|
|
10
|
+
scope = serviceKey.slice(0, pointInd);
|
|
11
|
+
serviceKey = serviceKey.slice(pointInd + 1);
|
|
12
|
+
}
|
|
13
|
+
parsedParams.push({
|
|
14
|
+
scope,
|
|
15
|
+
serviceKey,
|
|
16
|
+
endpointName,
|
|
17
|
+
config,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return parsedParams;
|
|
22
|
+
}
|
|
23
|
+
function getKeys(obj) {
|
|
24
|
+
return Object.keys(obj);
|
|
25
|
+
}
|
|
26
|
+
function deepCopy(obj) {
|
|
27
|
+
const res = Object.assign({}, obj);
|
|
28
|
+
getKeys(res).forEach((key) => {
|
|
29
|
+
if (typeof res[key] === 'object' && !Array.isArray(res[key]) && res[key] !== null) {
|
|
30
|
+
res[key] = deepCopy(res[key]);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return res;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Overrides endpoints in the schema according to overrideParams
|
|
37
|
+
* @param originalSchema Schema for the gateway
|
|
38
|
+
* @param overrideParams Definitions of endpoints for services in the format {[<scope>.<service key>]: {[<endpointName>]: ActionEndpoint}}
|
|
39
|
+
* If <scope> is not specified, root is assumed.
|
|
40
|
+
* Example: '{serviceName: {endpoint: "https://example.com"}}'
|
|
41
|
+
* @param installation
|
|
42
|
+
* @param env
|
|
43
|
+
* @return new overridden schema
|
|
44
|
+
*/
|
|
45
|
+
export function overrideEndpoints(originalSchema, overrideParams, installation, env) {
|
|
46
|
+
if (!installation) {
|
|
47
|
+
console.warn('overrideEndpoints: installation empty');
|
|
48
|
+
return originalSchema;
|
|
49
|
+
}
|
|
50
|
+
if (!env) {
|
|
51
|
+
console.warn('overrideEndpoints: env empty');
|
|
52
|
+
return originalSchema;
|
|
53
|
+
}
|
|
54
|
+
const schema = deepCopy(originalSchema);
|
|
55
|
+
const parsedParams = parseOverrideEndpointsParams(overrideParams);
|
|
56
|
+
parsedParams.forEach((overrideConfig) => {
|
|
57
|
+
var _a;
|
|
58
|
+
const curService = (_a = schema[overrideConfig.scope]) === null || _a === void 0 ? void 0 : _a[overrideConfig.serviceKey];
|
|
59
|
+
if (!curService || !curService.actions || !curService.endpoints) {
|
|
60
|
+
console.warn('overrideEndpoints: incorrect service ' +
|
|
61
|
+
overrideConfig.scope +
|
|
62
|
+
'.' +
|
|
63
|
+
overrideConfig.serviceKey);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const endpoints = curService.endpoints;
|
|
67
|
+
const envEndpoints = _.get(endpoints, [installation, env]);
|
|
68
|
+
if (!envEndpoints) {
|
|
69
|
+
console.warn('overrideEndpoints: ' + [installation, env] + ' not exists in envEndpoints');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (overrideConfig.endpointName) {
|
|
73
|
+
if (!envEndpoints[overrideConfig.endpointName]) {
|
|
74
|
+
console.warn('overrideEndpoints: incorrect endpointName ' + overrideConfig.endpointName);
|
|
75
|
+
}
|
|
76
|
+
overrideEndpoint(envEndpoints, overrideConfig.endpointName, overrideConfig);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return schema;
|
|
80
|
+
}
|
|
81
|
+
function isExtendedEndpointConfig(config) {
|
|
82
|
+
return typeof config === 'object';
|
|
83
|
+
}
|
|
84
|
+
function overrideEndpoint(envEndpoints, endpointName, overrideConfig) {
|
|
85
|
+
const config = envEndpoints[endpointName];
|
|
86
|
+
if (isExtendedEndpointConfig(config)) {
|
|
87
|
+
if (typeof overrideConfig.config === 'string') {
|
|
88
|
+
config.path = overrideConfig.config;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
Object.assign(config, overrideConfig.config);
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
envEndpoints[endpointName] = overrideConfig.config;
|
|
96
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as grpc from '@grpc/grpc-js';
|
|
2
|
+
import * as protobufjs from 'protobufjs';
|
|
3
|
+
import { Lang } from '../constants.js';
|
|
4
|
+
import { GatewayError } from '../models/common.js';
|
|
5
|
+
import { AppErrorConstructor } from '../models/error.js';
|
|
6
|
+
export declare function parseMixedError(e: Error & {
|
|
7
|
+
code?: string;
|
|
8
|
+
}): {
|
|
9
|
+
status: number;
|
|
10
|
+
message: string;
|
|
11
|
+
code: string;
|
|
12
|
+
debug: {
|
|
13
|
+
originalError: Error & {
|
|
14
|
+
code?: string;
|
|
15
|
+
};
|
|
16
|
+
stack: string | undefined;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export declare function parseRestError(error: any, lang?: string): GatewayError;
|
|
20
|
+
export declare function parseGrpcError(error: grpc.ServiceError, packageRoot: protobufjs.Root, lang?: Lang, decodeAnyMessageProtoLoaderOptions?: protobufjs.IConversionOptions): GatewayError;
|
|
21
|
+
export declare class GrpcError extends Error {
|
|
22
|
+
private parsedError;
|
|
23
|
+
private rawError;
|
|
24
|
+
constructor(message: string, parsedError: GatewayError, rawError?: grpc.ServiceError);
|
|
25
|
+
getGatewayError(): GatewayError;
|
|
26
|
+
getRawError(): grpc.ServiceError | undefined;
|
|
27
|
+
getAppError(ErrorConstructor: AppErrorConstructor): Error;
|
|
28
|
+
}
|
|
29
|
+
export declare function isGrpcError(error: Error | GrpcError): error is GrpcError;
|
|
30
|
+
export declare function grpcErrorFactory(error: Error): GrpcError;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import { Lang } from '../constants.js';
|
|
3
|
+
import { decodeAnyMessageRecursively } from './grpc.js';
|
|
4
|
+
const DEFAULT_GATEWAY_CODE = 'GATEWAY_REQUEST_ERROR';
|
|
5
|
+
const DEFAULT_GATEWAY_MESSAGE = 'Gateway request error';
|
|
6
|
+
export function parseMixedError(e) {
|
|
7
|
+
return {
|
|
8
|
+
status: 500,
|
|
9
|
+
message: String(e.message || DEFAULT_GATEWAY_MESSAGE),
|
|
10
|
+
code: String(e.code || DEFAULT_GATEWAY_CODE),
|
|
11
|
+
debug: {
|
|
12
|
+
originalError: e,
|
|
13
|
+
stack: e.stack,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function getErrorTitle(error) {
|
|
18
|
+
const prop = _.propertyOf(error);
|
|
19
|
+
const details = prop('details[0]');
|
|
20
|
+
if (details) {
|
|
21
|
+
return prop('details[0].type') || prop('details[0].code') || prop('code');
|
|
22
|
+
}
|
|
23
|
+
return prop('type') || prop('code');
|
|
24
|
+
}
|
|
25
|
+
function getErrorMessage(error) {
|
|
26
|
+
const prop = _.propertyOf(error);
|
|
27
|
+
const message = prop('message');
|
|
28
|
+
const detail = prop('detail');
|
|
29
|
+
const detailsMessage = prop('details[0].message');
|
|
30
|
+
if (message) {
|
|
31
|
+
return message;
|
|
32
|
+
}
|
|
33
|
+
if (detailsMessage) {
|
|
34
|
+
return detailsMessage;
|
|
35
|
+
}
|
|
36
|
+
if (detail) {
|
|
37
|
+
return prop('detail.Description') || prop('detail.Body') || detail;
|
|
38
|
+
}
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
function getStatusByGrpcCode(code) {
|
|
42
|
+
switch (code) {
|
|
43
|
+
case 0:
|
|
44
|
+
return 200;
|
|
45
|
+
case 1:
|
|
46
|
+
return 499;
|
|
47
|
+
case 2:
|
|
48
|
+
return 500;
|
|
49
|
+
case 3:
|
|
50
|
+
return 400;
|
|
51
|
+
case 4:
|
|
52
|
+
return 504;
|
|
53
|
+
case 5:
|
|
54
|
+
return 404;
|
|
55
|
+
case 6:
|
|
56
|
+
return 409;
|
|
57
|
+
case 7:
|
|
58
|
+
return 403;
|
|
59
|
+
case 8:
|
|
60
|
+
return 429;
|
|
61
|
+
case 9:
|
|
62
|
+
return 400;
|
|
63
|
+
case 10:
|
|
64
|
+
return 409;
|
|
65
|
+
case 11:
|
|
66
|
+
return 400;
|
|
67
|
+
case 12:
|
|
68
|
+
return 501;
|
|
69
|
+
case 13:
|
|
70
|
+
return 500;
|
|
71
|
+
case 14:
|
|
72
|
+
return 503;
|
|
73
|
+
case 15:
|
|
74
|
+
return 500;
|
|
75
|
+
case 16:
|
|
76
|
+
return 401;
|
|
77
|
+
default:
|
|
78
|
+
return 500;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function parseRestError(error, lang) {
|
|
82
|
+
const prop = _.propertyOf(error);
|
|
83
|
+
let status = 500;
|
|
84
|
+
let title;
|
|
85
|
+
let description;
|
|
86
|
+
let details;
|
|
87
|
+
let type;
|
|
88
|
+
let code;
|
|
89
|
+
let debug;
|
|
90
|
+
if (prop('response')) {
|
|
91
|
+
const responseData = prop('response.data');
|
|
92
|
+
const statusTitle = `${prop('response.status')} ${prop('response.statusText')}`;
|
|
93
|
+
const errorTitle = getErrorTitle(responseData);
|
|
94
|
+
status = prop('response.status');
|
|
95
|
+
code = prop('response.data.code');
|
|
96
|
+
type = prop('response.data.type');
|
|
97
|
+
details = prop('response.data.details');
|
|
98
|
+
debug = prop('response.data.debug');
|
|
99
|
+
title = errorTitle ? errorTitle : statusTitle;
|
|
100
|
+
description = getErrorMessage(responseData);
|
|
101
|
+
if (status === 403) {
|
|
102
|
+
title = lang === Lang.Ru ? 'Доступ запрещен' : 'Access denied';
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
code = prop('code');
|
|
107
|
+
title = lang === Lang.Ru ? 'Ошибка' : 'Error';
|
|
108
|
+
if (code === 'ECONNABORTED' || code === 'ETIMEDOUT') {
|
|
109
|
+
status = 504;
|
|
110
|
+
description = lang === Lang.Ru ? 'Превышено время ожидания ответа' : 'Timeout exceeded';
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
status = 500;
|
|
114
|
+
description =
|
|
115
|
+
lang === Lang.Ru
|
|
116
|
+
? 'Произошла непредвиденная ошибка. Попробуйте обновить страницу через некоторое время.'
|
|
117
|
+
: 'An unexpected error has occurred. Try to refresh the page in a few moments.';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
status,
|
|
122
|
+
message: String(description || DEFAULT_GATEWAY_MESSAGE),
|
|
123
|
+
code: String(code || DEFAULT_GATEWAY_CODE),
|
|
124
|
+
details: Object.assign({ type,
|
|
125
|
+
title,
|
|
126
|
+
description }, (typeof details === 'object' && !Array.isArray(details) ? details : { details })),
|
|
127
|
+
debug,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function decodeGrpcStatusMessage(metadata, packageRoot, decodeAnyMessageProtoLoaderOptions) {
|
|
131
|
+
const statusMessageBin = metadata.get('grpc-status-details-bin')[0];
|
|
132
|
+
if (!statusMessageBin) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
return decodeAnyMessageRecursively(packageRoot, {
|
|
136
|
+
type_url: 'type.googleapis.com/google.rpc.Status',
|
|
137
|
+
value: statusMessageBin,
|
|
138
|
+
}, decodeAnyMessageProtoLoaderOptions);
|
|
139
|
+
}
|
|
140
|
+
export function parseGrpcError(error, packageRoot, lang = Lang.Ru, decodeAnyMessageProtoLoaderOptions) {
|
|
141
|
+
let title = lang === Lang.Ru ? 'Ошибка' : 'Error';
|
|
142
|
+
if (error.code === 7) {
|
|
143
|
+
// Always redefine title for Access denied errors
|
|
144
|
+
title = lang === Lang.Ru ? 'Доступ запрещен' : 'Access denied';
|
|
145
|
+
}
|
|
146
|
+
let code = error.code;
|
|
147
|
+
let description = error.details;
|
|
148
|
+
let details;
|
|
149
|
+
if (error.metadata) {
|
|
150
|
+
try {
|
|
151
|
+
const statusMessage = decodeGrpcStatusMessage(error.metadata, packageRoot, decodeAnyMessageProtoLoaderOptions);
|
|
152
|
+
if (statusMessage) {
|
|
153
|
+
code = statusMessage.code;
|
|
154
|
+
description = statusMessage.message;
|
|
155
|
+
details = statusMessage.details;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (e) { }
|
|
159
|
+
}
|
|
160
|
+
// Always redefine description for Timeout exceeded errors
|
|
161
|
+
if (error.code === 4) {
|
|
162
|
+
description = lang === Lang.Ru ? 'Превышено время ожидания ответа' : 'Timeout exceeded';
|
|
163
|
+
}
|
|
164
|
+
// Use default description if description is undefined, but not for Access denied errors
|
|
165
|
+
if (!description) {
|
|
166
|
+
description =
|
|
167
|
+
lang === Lang.Ru
|
|
168
|
+
? 'Произошла непредвиденная ошибка. Попробуйте обновить страницу через некоторое время.'
|
|
169
|
+
: 'An unexpected error has occurred. Try to refresh the page in a few moments.';
|
|
170
|
+
}
|
|
171
|
+
const status = getStatusByGrpcCode(error.code);
|
|
172
|
+
return {
|
|
173
|
+
status,
|
|
174
|
+
message: String(description || DEFAULT_GATEWAY_MESSAGE),
|
|
175
|
+
code: DEFAULT_GATEWAY_CODE,
|
|
176
|
+
details: Object.assign({ title,
|
|
177
|
+
description, grpcCode: code }, (typeof details === 'object' && !Array.isArray(details) ? details : { details })),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
export class GrpcError extends Error {
|
|
181
|
+
constructor(message, parsedError, rawError) {
|
|
182
|
+
super(message);
|
|
183
|
+
this.parsedError = parsedError;
|
|
184
|
+
this.rawError = rawError;
|
|
185
|
+
}
|
|
186
|
+
getGatewayError() {
|
|
187
|
+
return this.parsedError;
|
|
188
|
+
}
|
|
189
|
+
getRawError() {
|
|
190
|
+
return this.rawError;
|
|
191
|
+
}
|
|
192
|
+
getAppError(ErrorConstructor) {
|
|
193
|
+
return new ErrorConstructor(this.parsedError.message, {
|
|
194
|
+
code: this.parsedError.code,
|
|
195
|
+
details: this.parsedError.details,
|
|
196
|
+
debug: this.parsedError.debug,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export function isGrpcError(error) {
|
|
201
|
+
return error.getGatewayError !== undefined;
|
|
202
|
+
}
|
|
203
|
+
export function grpcErrorFactory(error) {
|
|
204
|
+
return new GrpcError(error.message, {
|
|
205
|
+
status: 500,
|
|
206
|
+
code: '',
|
|
207
|
+
message: error.message,
|
|
208
|
+
details: {},
|
|
209
|
+
});
|
|
210
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as protobufjs from 'protobufjs';
|
|
4
|
+
export function patchProtoPathResolver(root, includeDirs) {
|
|
5
|
+
const originalResolvePath = root.resolvePath;
|
|
6
|
+
root.resolvePath = function (origin, target) {
|
|
7
|
+
if (target in protobufjs.common || path.isAbsolute(target)) {
|
|
8
|
+
return target;
|
|
9
|
+
}
|
|
10
|
+
for (let i = 0; i < includeDirs.length; i++) {
|
|
11
|
+
const directory = includeDirs[i];
|
|
12
|
+
const fullPath = path.join(directory, target);
|
|
13
|
+
try {
|
|
14
|
+
fs.accessSync(fullPath, fs.constants.R_OK);
|
|
15
|
+
return fullPath;
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return originalResolvePath(origin, target);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function redactSensitiveHeaders(ctx, headers) {
|
|
2
|
+
var _a, _b;
|
|
3
|
+
if ((_a = ctx.utils) === null || _a === void 0 ? void 0 : _a.redactSensitiveHeaders) {
|
|
4
|
+
return ctx.utils.redactSensitiveHeaders(headers);
|
|
5
|
+
}
|
|
6
|
+
else if ((_b = ctx.utils) === null || _b === void 0 ? void 0 : _b.redactSensitiveKeys) {
|
|
7
|
+
return ctx.utils.redactSensitiveKeys(headers);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
return headers;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sourceDir: string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function validateArgs<TParams>(args: TParams, schema: object): string | false;
|
|
2
|
+
export declare function encodePathParams<TParams extends {}>(params: TParams): Record<string, any>;
|
|
3
|
+
export declare function getPathParam(value: string): string;
|
|
4
|
+
export declare function getPathArgsProxy<TParams extends {}>(args: TParams, encodePathArgs?: boolean): TParams;
|