mirror of https://github.com/grpc/grpc-node.git
Merge pull request #2711 from n0v1/feat/method-options
grpc-loader: Expose method options
This commit is contained in:
commit
567449875d
|
@ -57,6 +57,7 @@
|
||||||
"@types/node": "^10.17.26",
|
"@types/node": "^10.17.26",
|
||||||
"@types/yargs": "^17.0.24",
|
"@types/yargs": "^17.0.24",
|
||||||
"clang-format": "^1.2.2",
|
"clang-format": "^1.2.2",
|
||||||
|
"google-proto-files": "^3.0.2",
|
||||||
"gts": "^3.1.0",
|
"gts": "^3.1.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
|
|
|
@ -115,6 +115,34 @@ export interface EnumTypeDefinition extends ProtobufTypeDefinition {
|
||||||
format: 'Protocol Buffer 3 EnumDescriptorProto';
|
format: 'Protocol Buffer 3 EnumDescriptorProto';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum IdempotencyLevel {
|
||||||
|
IDEMPOTENCY_UNKNOWN = 'IDEMPOTENCY_UNKNOWN',
|
||||||
|
NO_SIDE_EFFECTS = 'NO_SIDE_EFFECTS',
|
||||||
|
IDEMPOTENT = 'IDEMPOTENT'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NamePart {
|
||||||
|
name_part: string;
|
||||||
|
is_extension: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UninterpretedOption {
|
||||||
|
name?: NamePart[];
|
||||||
|
identifier_value?: string;
|
||||||
|
positive_int_value?: number;
|
||||||
|
negative_int_value?: number;
|
||||||
|
double_value?: number;
|
||||||
|
string_value?: string;
|
||||||
|
aggregate_value?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MethodOptions {
|
||||||
|
deprecated: boolean;
|
||||||
|
idempotency_level: IdempotencyLevel;
|
||||||
|
uninterpreted_option: UninterpretedOption[];
|
||||||
|
[k: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MethodDefinition<RequestType, ResponseType, OutputRequestType=RequestType, OutputResponseType=ResponseType> {
|
export interface MethodDefinition<RequestType, ResponseType, OutputRequestType=RequestType, OutputResponseType=ResponseType> {
|
||||||
path: string;
|
path: string;
|
||||||
requestStream: boolean;
|
requestStream: boolean;
|
||||||
|
@ -126,6 +154,7 @@ export interface MethodDefinition<RequestType, ResponseType, OutputRequestType=R
|
||||||
originalName?: string;
|
originalName?: string;
|
||||||
requestType: MessageTypeDefinition;
|
requestType: MessageTypeDefinition;
|
||||||
responseType: MessageTypeDefinition;
|
responseType: MessageTypeDefinition;
|
||||||
|
options: MethodOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServiceDefinition {
|
export interface ServiceDefinition {
|
||||||
|
@ -220,6 +249,27 @@ function createSerializer(cls: Protobuf.Type): Serialize<object> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapMethodOptions(options: Partial<MethodOptions>[] | undefined): MethodOptions {
|
||||||
|
return (options || []).reduce((obj: MethodOptions, item: Partial<MethodOptions>) => {
|
||||||
|
for (const [key, value] of Object.entries(item)) {
|
||||||
|
switch (key) {
|
||||||
|
case 'uninterpreted_option' :
|
||||||
|
obj.uninterpreted_option.push(item.uninterpreted_option as UninterpretedOption);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
obj[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deprecated: false,
|
||||||
|
idempotency_level: IdempotencyLevel.IDEMPOTENCY_UNKNOWN,
|
||||||
|
uninterpreted_option: [],
|
||||||
|
}
|
||||||
|
) as MethodOptions;
|
||||||
|
}
|
||||||
|
|
||||||
function createMethodDefinition(
|
function createMethodDefinition(
|
||||||
method: Protobuf.Method,
|
method: Protobuf.Method,
|
||||||
serviceName: string,
|
serviceName: string,
|
||||||
|
@ -242,6 +292,7 @@ function createMethodDefinition(
|
||||||
originalName: camelCase(method.name),
|
originalName: camelCase(method.name),
|
||||||
requestType: createMessageDefinition(requestType, fileDescriptors),
|
requestType: createMessageDefinition(requestType, fileDescriptors),
|
||||||
responseType: createMessageDefinition(responseType, fileDescriptors),
|
responseType: createMessageDefinition(responseType, fileDescriptors),
|
||||||
|
options: mapMethodOptions(method.parsedOptions),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { rpcFileDescriptorSet } from '../test_protos/rpc.desc';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
|
|
||||||
import * as proto_loader from '../src/index';
|
import * as proto_loader from '../src/index';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
// Relative path from build output directory to test_protos directory
|
// Relative path from build output directory to test_protos directory
|
||||||
const TEST_PROTO_DIR = `${__dirname}/../../test_protos/`;
|
const TEST_PROTO_DIR = `${__dirname}/../../test_protos/`;
|
||||||
|
@ -128,4 +129,53 @@ describe('Descriptor types', () => {
|
||||||
// This will throw if the file descriptor object cannot be parsed
|
// This will throw if the file descriptor object cannot be parsed
|
||||||
proto_loader.loadFileDescriptorSetFromObject(rpcFileDescriptorSet);
|
proto_loader.loadFileDescriptorSetFromObject(rpcFileDescriptorSet);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Can parse method options into object correctly', () => {
|
||||||
|
const includeDirs = [
|
||||||
|
dirname(require.resolve('google-proto-files/package.json'))
|
||||||
|
];
|
||||||
|
const packageDefinition = proto_loader.loadSync(`${TEST_PROTO_DIR}/method_options.proto`, { includeDirs });
|
||||||
|
assert('Hello' in packageDefinition);
|
||||||
|
const service = packageDefinition.Hello as proto_loader.ServiceDefinition
|
||||||
|
assert.deepStrictEqual(service.Hello.options, {
|
||||||
|
deprecated: true,
|
||||||
|
idempotency_level: 'NO_SIDE_EFFECTS',
|
||||||
|
uninterpreted_option: [{
|
||||||
|
name: {
|
||||||
|
name_part: 'foo',
|
||||||
|
is_extension: false,
|
||||||
|
},
|
||||||
|
identifier_value: 'bar',
|
||||||
|
positive_int_value: 9007199254740991,
|
||||||
|
negative_int_value: -9007199254740991,
|
||||||
|
double_value: 1.2345,
|
||||||
|
string_value: 'foobar',
|
||||||
|
aggregate_value: 'foobar'
|
||||||
|
}],
|
||||||
|
'(google.api.http)': {
|
||||||
|
post: '/hello',
|
||||||
|
body: '*',
|
||||||
|
response_body: '*',
|
||||||
|
additional_bindings: {}
|
||||||
|
},
|
||||||
|
'(google.api.method_signature)': 'bar'
|
||||||
|
})
|
||||||
|
assert.deepStrictEqual(service.HelloWithoutOptions.options, {
|
||||||
|
deprecated: false,
|
||||||
|
idempotency_level: 'IDEMPOTENCY_UNKNOWN',
|
||||||
|
uninterpreted_option: []
|
||||||
|
})
|
||||||
|
assert.deepStrictEqual(service.HelloWithSomeOptions.options, {
|
||||||
|
deprecated: true,
|
||||||
|
idempotency_level: 'IDEMPOTENCY_UNKNOWN',
|
||||||
|
uninterpreted_option: [],
|
||||||
|
'(google.api.http)': {
|
||||||
|
get: '/hello',
|
||||||
|
additional_bindings: {
|
||||||
|
body: '*',
|
||||||
|
get: '/hello-world'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
import "google/api/client.proto";
|
||||||
|
import "google/api/httpbody.proto";
|
||||||
|
|
||||||
|
message Empty {}
|
||||||
|
|
||||||
|
message MethodSignature {
|
||||||
|
repeated string method_signature = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service Hello {
|
||||||
|
rpc Hello (Empty) returns (Empty) {
|
||||||
|
option deprecated = true;
|
||||||
|
option idempotency_level = NO_SIDE_EFFECTS;
|
||||||
|
option uninterpreted_option = {
|
||||||
|
name: {
|
||||||
|
name_part: 'foo'
|
||||||
|
is_extension: false
|
||||||
|
}
|
||||||
|
identifier_value: 'bar'
|
||||||
|
positive_int_value: 9007199254740991
|
||||||
|
negative_int_value: -9007199254740991
|
||||||
|
double_value: 1.2345
|
||||||
|
string_value: 'foobar'
|
||||||
|
aggregate_value: 'foobar'
|
||||||
|
};
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/hello"
|
||||||
|
body: "*"
|
||||||
|
response_body: "*"
|
||||||
|
additional_bindings: {}
|
||||||
|
};
|
||||||
|
option (google.api.method_signature) = 'bar';
|
||||||
|
}
|
||||||
|
rpc HelloWithoutOptions (Empty) returns (Empty) {}
|
||||||
|
rpc HelloWithSomeOptions (Empty) returns (Empty) {
|
||||||
|
option deprecated = true;
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/hello"
|
||||||
|
additional_bindings: {
|
||||||
|
get: "/hello-world"
|
||||||
|
body: "*"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue