mirror of https://github.com/grpc/grpc-node.git
grpc-js: Fix method config name handling in service configs
This commit is contained in:
parent
1df90db060
commit
69257a7893
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@grpc/grpc-js",
|
"name": "@grpc/grpc-js",
|
||||||
"version": "1.9.0",
|
"version": "1.9.1",
|
||||||
"description": "gRPC Library for Node - pure JS implementation",
|
"description": "gRPC Library for Node - pure JS implementation",
|
||||||
"homepage": "https://grpc.io/",
|
"homepage": "https://grpc.io/",
|
||||||
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",
|
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",
|
||||||
|
|
|
@ -21,7 +21,11 @@ import {
|
||||||
LoadBalancingConfig,
|
LoadBalancingConfig,
|
||||||
getFirstUsableConfig,
|
getFirstUsableConfig,
|
||||||
} from './load-balancer';
|
} from './load-balancer';
|
||||||
import { ServiceConfig, validateServiceConfig } from './service-config';
|
import {
|
||||||
|
MethodConfig,
|
||||||
|
ServiceConfig,
|
||||||
|
validateServiceConfig,
|
||||||
|
} from './service-config';
|
||||||
import { ConnectivityState } from './connectivity-state';
|
import { ConnectivityState } from './connectivity-state';
|
||||||
import { ConfigSelector, createResolver, Resolver } from './resolver';
|
import { ConfigSelector, createResolver, Resolver } from './resolver';
|
||||||
import { ServiceError } from './call';
|
import { ServiceError } from './call';
|
||||||
|
@ -43,6 +47,59 @@ function trace(text: string): void {
|
||||||
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
|
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NameMatchLevel = 'EMPTY' | 'SERVICE' | 'SERVICE_AND_METHOD';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name match levels in order from most to least specific. This is the order in
|
||||||
|
* which searches will be performed.
|
||||||
|
*/
|
||||||
|
const NAME_MATCH_LEVEL_ORDER: NameMatchLevel[] = [
|
||||||
|
'SERVICE_AND_METHOD',
|
||||||
|
'SERVICE',
|
||||||
|
'EMPTY',
|
||||||
|
];
|
||||||
|
|
||||||
|
function hasMatchingName(
|
||||||
|
service: string,
|
||||||
|
method: string,
|
||||||
|
methodConfig: MethodConfig,
|
||||||
|
matchLevel: NameMatchLevel
|
||||||
|
): boolean {
|
||||||
|
for (const name of methodConfig.name) {
|
||||||
|
switch (matchLevel) {
|
||||||
|
case 'EMPTY':
|
||||||
|
if (!name.service && !name.method) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'SERVICE':
|
||||||
|
if (name.service === service && !name.method) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'SERVICE_AND_METHOD':
|
||||||
|
if (name.service === service && name.method === method) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findMatchingConfig(
|
||||||
|
service: string,
|
||||||
|
method: string,
|
||||||
|
methodConfigs: MethodConfig[],
|
||||||
|
matchLevel: NameMatchLevel
|
||||||
|
): MethodConfig | null {
|
||||||
|
for (const config of methodConfigs) {
|
||||||
|
if (hasMatchingName(service, method, config, matchLevel)) {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function getDefaultConfigSelector(
|
function getDefaultConfigSelector(
|
||||||
serviceConfig: ServiceConfig | null
|
serviceConfig: ServiceConfig | null
|
||||||
): ConfigSelector {
|
): ConfigSelector {
|
||||||
|
@ -54,14 +111,22 @@ function getDefaultConfigSelector(
|
||||||
const service = splitName[0] ?? '';
|
const service = splitName[0] ?? '';
|
||||||
const method = splitName[1] ?? '';
|
const method = splitName[1] ?? '';
|
||||||
if (serviceConfig && serviceConfig.methodConfig) {
|
if (serviceConfig && serviceConfig.methodConfig) {
|
||||||
for (const methodConfig of serviceConfig.methodConfig) {
|
/* Check for the following in order, and return the first method
|
||||||
for (const name of methodConfig.name) {
|
* config that matches:
|
||||||
if (
|
* 1. A name that exactly matches the service and method
|
||||||
name.service === service &&
|
* 2. A name with no method set that matches the service
|
||||||
(name.method === undefined || name.method === method)
|
* 3. An empty name
|
||||||
) {
|
*/
|
||||||
|
for (const matchLevel of NAME_MATCH_LEVEL_ORDER) {
|
||||||
|
const matchingConfig = findMatchingConfig(
|
||||||
|
service,
|
||||||
|
method,
|
||||||
|
serviceConfig.methodConfig,
|
||||||
|
matchLevel
|
||||||
|
);
|
||||||
|
if (matchingConfig) {
|
||||||
return {
|
return {
|
||||||
methodConfig: methodConfig,
|
methodConfig: matchingConfig,
|
||||||
pickInformation: {},
|
pickInformation: {},
|
||||||
status: Status.OK,
|
status: Status.OK,
|
||||||
dynamicFilterFactories: [],
|
dynamicFilterFactories: [],
|
||||||
|
@ -69,7 +134,6 @@ function getDefaultConfigSelector(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
methodConfig: { name: [] },
|
methodConfig: { name: [] },
|
||||||
pickInformation: {},
|
pickInformation: {},
|
||||||
|
|
|
@ -35,7 +35,7 @@ import {
|
||||||
} from './load-balancer';
|
} from './load-balancer';
|
||||||
|
|
||||||
export interface MethodConfigName {
|
export interface MethodConfigName {
|
||||||
service: string;
|
service?: string;
|
||||||
method?: string;
|
method?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,20 +95,36 @@ const DURATION_REGEX = /^\d+(\.\d{1,9})?s$/;
|
||||||
const CLIENT_LANGUAGE_STRING = 'node';
|
const CLIENT_LANGUAGE_STRING = 'node';
|
||||||
|
|
||||||
function validateName(obj: any): MethodConfigName {
|
function validateName(obj: any): MethodConfigName {
|
||||||
if (!('service' in obj) || typeof obj.service !== 'string') {
|
// In this context, and unset field and '' are considered the same
|
||||||
throw new Error('Invalid method config name: invalid service');
|
if ('service' in obj && obj.service !== '') {
|
||||||
|
if (typeof obj.service !== 'string') {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid method config name: invalid service: expected type string, got ${typeof obj.service}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const result: MethodConfigName = {
|
if ('method' in obj && obj.method !== '') {
|
||||||
|
if (typeof obj.method !== 'string') {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid method config name: invalid method: expected type string, got ${typeof obj.service}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
service: obj.service,
|
||||||
|
method: obj.method,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
service: obj.service,
|
service: obj.service,
|
||||||
};
|
};
|
||||||
if ('method' in obj) {
|
}
|
||||||
if (typeof obj.method === 'string') {
|
|
||||||
result.method = obj.method;
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid method config name: invalid method');
|
if ('method' in obj && obj.method !== undefined) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid method config name: method set with empty or unset service`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateRetryPolicy(obj: any): RetryPolicy {
|
function validateRetryPolicy(obj: any): RetryPolicy {
|
||||||
|
|
Loading…
Reference in New Issue