The grpc.federation.service
option is always required to enable gRPC Federation. Please specify as follows.
service MyService {
option (grpc.federation.service) = {};
}
field | type | required or optional |
---|---|---|
env |
Env | optional |
The definition of environment variables used by the service.
The environment variables defined here can be accessed through the Get<service-name>Env()
function in the custom resolver. They can also be used by accessing the grpc.federation.env
variable in CEL expressions.
When using grpc.federation.env
to access environment variables, the message using the option must exist in the same package as the service that defines the env
option.
Keep in mind that multiple services within the same package can use the env
option. However, if a message is employed by multiple services with differing env
options, that message can only access the set of environment variables that are common to those services.
For instance, suppose ServiceA has two environment variables, ENV_VAR1
and ENV_VAR2
, while ServiceB has just one, ENV_VAR1
. If both services reference Message1
, users can only access ENV_VAR1
from Message1
, as ServiceB does not define ENV_VAR2
in its environment options.
message
and var
cannot be used simultaneously.
field | type | required or optional |
---|---|---|
message |
string | optional |
var |
repeated EnvVar | optional |
If there is a message that represents a collection of environment variables, you can specify that message.
If the following environment variables exist, they can be interpreted and handled as an Env
message.
FOO=hello
BAR=1
service MyService {
option (grpc.federation.service) = {
env { message: "Env" }
};
}
message Env {
string foo = 1;
int64 bar = 2;
}
The definition of the environment variable.
field | type | required or optional |
---|---|---|
name |
string | required |
type |
EnvType | required |
option |
EnvVarOption | optional |
The name of the environment variable. It is case insensitive.
This represents the types of environment variables. Available types include primitive types, their repeated types, and map types.
field | type | required or optional |
---|---|---|
kind |
EnvKind | optional |
repeated |
EnvType | optional |
map |
EnvMapType | optional |
It is used to represent the following primitive types.
string
:STRING
bool
:BOOL
int64
:INT64
uint64
:UINT64
double
:DOUBLE
google.protobuf.Duration
:DURATION
The following example represents the equivalent of the int64
type.
service MyService {
option (grpc.federation.service) = {
env {
var {
name: "i64"
type { kind: INT64 }
}
}
};
}
It is used to represent the repeated
type.
The following example represents the equivalent of the repeated string
type.
service MyService {
option (grpc.federation.service) = {
env {
var {
name: "repeated_value"
type {
repeated {
kind: STRING
}
}
}
}
};
}
It is used to represent a map type, specifying EnvType
values for both key and value.
field | type | required or optional |
---|---|---|
key |
EnvType | required |
value |
EnvType | required |
The following example represents the equivalent of the map<string, int64>
type.
service MyService {
option (grpc.federation.service) = {
env {
var {
name: "map"
type {
map {
key { kind: STRING }
value { kind: INT64 }
}
}
}
}
};
}
These are options for modifying the behavior when reading environment variables.
field | type | required or optional |
---|---|---|
alternate |
string | optional |
default |
string | optional |
required |
bool | optional |
ignored |
bool | optional |
If you want to refer to an environment variable by a different name than specified by var.name
, use this option.
If you have a value you want to specify in case the environment variable does not exist, use this option.
Make the existence of the environment variable mandatory.
No action is taken even if the environment variable exists.
field | type | required or optional |
---|---|---|
timeout |
string | optional |
response |
string | optional |
the time to timeout. If the specified time period elapses, DEADLINE_EXCEEDED status is returned.
If you want to handle this error, you need to implement a custom error handler in Go.
The format is the same as Go's time.Duration format. See https://pkg.go.dev/time#ParseDuration.
service MyService {
option (grpc.federation.service) = {};
rpc Get(GetRequest) returns (GetResponse) {
option (grpc.federation.method).timeout = "1m";
};
}
response
specify the name of the message you want to use to create the response value.
If you specify a reserved type like google.protobuf.Empty
as the response, you cannot define gRPC Federation options.
In such cases, you can specify a separate message to create the response value.
The specified response message must contain fields with the same names and types as all the fields in the original response.
service MyService {
option (grpc.federation.service) = {};
rpc Update(UpdateRequest) returns (google.protobuf.Empty) {
option (grpc.federation.method).response = "UpdateResponse";
};
}
message UpdateRequest {
string id = 1;
}
message UpdateResponse {
option (grpc.federation.message) = {
def {
call {
method: "pkg.PostService/UpdatePost"
request { field: "id" by: "$.id" }
}
}
};
}
field | type | required or optional |
---|---|---|
alias |
string | optional |
alias
mapping between enums defined in other packages and enums defined on the federation service side.
The alias
is the FQDN ( <package-name>.<enum-name>
) to the enum.
If this definition exists, type conversion is automatically performed before the enum value assignment operation.
If a enum with this option has a value that is not present in the enum specified by alias, and the alias option is not specified for that value, an error is occurred.
package mypkg;
enum FooEnum {
option (grpc.federation.enum).alias = "foopkg.FooEnum";
ENUM_VALUE_1 = 0;
ENUM_VALUE_2 = 1;
}
field | type | required or optional |
---|---|---|
default |
bool | optional |
alias |
repeated string | optional |
Specifies the default value of the enum. All values other than those specified in alias will be default values.
- myservice.proto
package mypkg;
import "foo.proto";
enum FooEnum {
option (grpc.federation.enum).alias = "foopkg.FooEnum";
ENUM_VALUE_UNKNOWN = 0 [(grpc.federation.enum_value).default = true];
ENUM_VALUE_1 = 1 [(grpc.federation.enum_value).alias = "FOO_VALUE_1"];
ENUM_VALUE_2 = 2 [(grpc.federation.enum_value).alias = "FOO_VALUE_2"];
}
- foo.proto
package foopkg;
enum FooEnum {
FOO_VALUE_1 = 0;
FOO_VALUE_2 = 1;
FOO_VALUE_3 = 2;
}
alias
can be used when alias is specified in grpc.federation.enum
option,
and specifies the value name to be referenced among the enums specified in alias of enum option.
multiple value names can be specified for alias
.
- myservice.proto
package mypkg;
import "foo.proto";
enum FooEnum {
option (grpc.federation.enum).alias = "foopkg.FooEnum";
ENUM_VALUE_1 = 0 [(grpc.federation.enum_value).alias = "FOO_VALUE_1"];
ENUM_VALUE_2 = 1 [(grpc.federation.enum_value).alias = "FOO_VALUE_2"];
}
- foo.proto
package foopkg;
enum FooEnum {
FOO_VALUE_1 = 0;
FOO_VALUE_2 = 1;
}
field | type | required or optional |
---|---|---|
def |
repeated VariableDefinition | optional |
custom_resolver |
bool | optional |
alias |
string | optional |
def
can define variables.
The defined variables can be referenced in subsequent CEL field.
The name
is a variable name and autobind
can be used when the variable is message
type.
It can be used for automatic field binding.
If if
is defined, then it is evaluated only if the condition is matched.
If does not evaluated, a default value of the type obtained when evaluating is assigned.
field | type | required or optional |
---|---|---|
name |
string | optional |
autobind |
bool | optional |
if |
CEL | optional |
The value to be assigned to a variable can be created with the following features.
field | type | required or optional |
---|---|---|
by |
CEL | optional |
call |
CallExpr | optional |
message |
MessageExpr | optional |
map |
MapExpr | optional |
validation |
ValidationExpr | optional |
If the defined value is a message type and the field of that message type exists in the message with the same name and type,
the field binding is automatically performed.
If multiple autobinds are used at the same message,
you must explicitly use the grpc.federation.field
option to do the binding yourself, since duplicate field names cannot be correctly determined as one.
- myservice.proto
package mypkg;
import "foo.proto";
message MyMessage {
option (grpc.federation.message) = {
def {
call { method: "foopkg.FooService/GetFoo" }
// The value returned by `call` is `foopkg.GetFooReply` message.
// In this case, if a field with the same name and type as the field in the `GetFooReply` message exists in `MyMessage`,
// field binding is automatically performed.
autobind: true
}
};
string field1 = 1; // autobound from `foopkg.GetFooReply.field1`
bool field2 = 2; // autobound from `foopkg.GetFooReply.field2`
}
- foo.proto
package foopkg;
service FooService {
rpc GetFoo(GetFooRequest) returns (GetFooReply) {};
}
message GetFooReply {
string field1 = 1;
bool field2 = 2;
}
message MyMessage {
option (grpc.federation.message) = {
// `x` is assigned `10`
def {
name: "x"
if: "true"
by: "10"
}
// `y` is assigned `0`
def {
name: "y"
if: "false"
by: "10"
}
};
}
Binds the result of evaluating the CEL defined in by
to the variable defined in name.
message MyMessage {
option (grpc.federation.message) = {
def {
// variable `v` is assigned the value `6`.
name: "v"
by: "1 + 2 + 3"
}
};
}
call
is used to call the gRPC method and assign the result to a variable.
message MyMessage {
option (grpc.federation.message) = {
def {
// The type of "res" variable is `foopkg.FooService/GetFoo` method's response type.
name: "res"
call {
method: "foopkg.FooService/GetFoo"
request { field: "field1", by: "'abcd'" }
}
}
// The type of "foo" variable is foo's field type of GetFoo's response type.
// If the variable type is a message type, `autobind` feature can be used.
def { name: "foo" by: "res.foo" autobind: true }
};
}
field | type | required or optional |
---|---|---|
method |
string | required |
request |
repeated MethodRequest | optional |
timeout |
string | optional |
retry |
RetryPolicy | optional |
error |
repeated GRPCError | optional |
Specify the FQDN for the gRPC method. format is <package-name>.<service-name>/<method-name>
.
- myservice.proto
package mypkg;
import "foo.proto";
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
}
}
};
}
- foo.proto
package foopkg;
service FooService {
rpc GetFoo(GetFooRequest) returns (GetFooReply) {};
}
Specify the request parameters for the gRPC method.
The field
corresponds to the field name in the request message, and the by
specifies which value is associated with the field.
- myservice.proto
package mypkg;
import "foo.proto";
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
request: [
{ field: "field1", by: "'hello'" }
]
}
}
};
}
- foo.proto
package foopkg;
service FooService {
rpc GetFoo(GetFooRequest) returns (GetFooReply) {};
}
message GetFooRequest {
string field1 = 1;
}
field | type | required or optional |
---|---|---|
field |
string | required |
by |
CEL | optional |
The field name of the request message.
by
used to refer to a variable or message argument defined in a grpc.federation.message
option by CEL.
You need to use $.
to refer to the message argument.
The time to timeout. If the specified time period elapses, DEADLINE_EXCEEDED status is returned.
If you want to handle this error, you need to implement a custom error handler in Go.
The format is the same as Go's time.Duration format. See https://pkg.go.dev/time#ParseDuration.
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
timeout: "1m"
}
}
};
}
Specify the retry policy if the method call fails.
field | type | required or optional |
---|---|---|
if |
CEL | optional |
constant |
RetryPolicyConstant | optional |
exponential |
RetryPolicyExponential | optional |
if
specifies condition in CEL. If the condition is true
, run the retry process according to the policy.
If this field is omitted, it is always treated as true
and run the retry process.
The return value must always be of type boolean
.
Note
Within the if
, the error
variable can be used as a gRPC error variable when evaluating CEL.
A detailed description of this variable is here
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
retry {
if: "error.code != google.rpc.Code.UNIMPLEMENTED"
constant {
interval: "10s"
}
}
}
}
};
}
Retry according to the "constant" policy.
field | type | required or optional |
---|---|---|
inerval |
string | optional |
max_retries |
uint64 | optional |
Interval value. Default value is 1s
.
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
retry {
constant {
interval: "10s"
}
}
}
}
};
}
Max retry count. Default value is 5
. If 0
is specified, it never stops.
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
retry {
constant {
max_retries: 3
}
}
}
}
};
}
field | type | required or optional |
---|---|---|
initial_interval |
string | optional |
randomization_factor |
double | optional |
multiplier |
double | optional |
max_interval |
string | optional |
max_retries |
uint64 | optional |
Initial interval value. Default value is 500ms
.
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
retry {
exponential {
initial_interval: "1s"
}
}
}
}
};
}
Randomization factor value. Default value is 0.5
.
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
retry {
exponential {
randomization_factor: 1.0
}
}
}
}
};
}
Multiplier. Default value is 1.5
.
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
retry {
exponential {
multiplier: 1.0
}
}
}
}
};
}
Max interval value. Default value is 60s
.
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
retry {
exponential {
max_interval: "30s"
}
}
}
}
};
}
Max retry count. Default value is 5
. If 0
is specified, it never stops.
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
retry {
exponential {
max_retries: 3
}
}
}
}
};
}
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
// If an error is returned when the gRPC method is called, the error block is evaluated.
// If there are multiple error blocks, they are processed in order from the top.
// The first error matching the condition is returned.
// If none of the conditions match, the original error is returned.
error {
// The variables can be defined for use in error blocks
def { name: "a" by: "1" }
if: "a == 1" // if true, returns the error.
code: FAILED_PRECONDITION // gRPC error code. this field is required.
message: "'this is error message'" // gRPC error message. this field is required.
// If you want to add the more information to the gRPC error, you can specify it in the details block.
details {
// The variables can be defined for use in details block.
def { name: "b" by: "2" }
if: "b == 2" // if true, add to the error details.
// You can specify the value defined in `google/rpc/error_details.proto`.
// FYI: https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
precondition_failure {
violations {
type: "'type value'"
subject: "'subject value'"
description: "'desc value'"
}
}
// If you want to add your own message, you can add it using the message name and arguments as follows.
message {
name: "CustomMessage"
args { name: "msg" by: "id" }
}
}
}
// This error block is evaluated if the above condition is false.
error {
// If `ignore: true` is specified, the error is ignored.
ignore: true
}
}
}
};
}
field | type | required or optional |
---|---|---|
def |
repeated VariableDefinition | optional |
if |
CEL | optional |
code |
google.rpc.Code | required |
message |
CEL | required |
details |
repeated GRPCErrorDetail | optional |
ignore |
bool | optional |
ignore_and_response |
CEL | optional |
Note
Within the error block, the error
variable can be used as a special variable when evaluating CEL.
A detailed description of this variable is here
if
specifies condition in CEL. If the condition is true
, it returns defined error information.
If this field is omitted, it is always treated as true
and returns defined error information.
The return value must always be of type boolean
.
code
is a gRPC status code.
message
is a gRPC status message.
If omitted, the message will be auto-generated from the configurations.
ignore
ignore the error if the condition in the if
field is true
and ignore
field is set to true
.
When an error is ignored, the returned response is always null
value.
If you want to return a response that is not null
, please use ignore_and_response
feature.
Therefore, ignore
and ignore_and_response
cannot be specified same.
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
error {
// dscribe the condition for ignoring error.
if: "error.code == google.rpc.Code.UNAVAILABLE"
ignore: true
}
}
}
};
}
ignore_and_response
ignore the error if the condition in the if
field is true
and it returns response specified in CEL.
The evaluation value of CEL must always be the same as the response message type.
ignore
and ignore_and_response
cannot be specified same.
message MyMessage {
option (grpc.federation.message) = {
def {
call {
method: "foopkg.FooService/GetFoo"
error {
// dscribe the condition for ignoring error.
if: "error.code == google.rpc.Code.UNAVAILABLE"
// specify the response to return when errors are ignored.
// The return value must always be a response message.
ignore_and_response: "foopkg.Foo{x: 1}"
}
}
}
};
}
message
used to refer to other message.
The parameters necessary to obtain a message must be passed as message arguments by args
.
field | type | required or optional |
---|---|---|
name |
string | required |
args |
repeated Argument | optional |
Specify the message name with FQDN. format is <package-name>.<message-name>
.
<package-name>
can be omitted when referring to messages in the same package.
package mypkg;
message MyMessage {
option (grpc.federation.message) = {
// get the `Foo` message and assign it to the `foo` variable.
def {
name: "foo"
message { name: "Foo" }
}
};
}
message Foo {
option (grpc.federation.message).custom_resolver = true;
string x = 1;
}
Specify the parameters needed to retrieve the message. This is called the message argument.
package mypkg;
message MyMessage {
option (grpc.federation.message) = {
def {
name: "foo"
message {
name: "Foo"
// pass arguments to retrieve Foo message.
args [
{ name: "x" by: "'xxxx'" },
{ name: "y" by: "true" }
]
}
}
};
}
message Foo {
// $.x is `xxxx` value
string x = 1 [(grpc.federation.field).by = "$.x"];
// $.y is `true` value
bool y = 2 [(grpc.federation.field).by = "$.y"];
}
field | type | required or optional |
---|---|---|
name |
string | required |
by |
CEL | optional |
inline |
CEL | optional |
Name of the message argument.
Use this name to refer to the message argument.
For example, if foo
is specified as the name, it is referenced by $.foo
in dependent message.
by
used to refer to a variable or message argument defined in a grpc.federation.message
option by CEL.
You need to use $.
to refer to the message argument.
inline
like by
, it refers to the specified value and expands all fields beyond it.
For this reason, the referenced value must always be of message type.
field | type | required or optional |
---|---|---|
iterator |
Iterator | required |
by |
CEL | optional |
message |
MessageExpr | optional |
Create the iterator variable.
src
must be a repeated typename
defines the name of the iterator variable
message MyMessage {
option (grpc.federation.message) = {
def {
name: "foo_list"
message { name: "FooList" }
}
def {
// `list_x` value is a list of Foo.x. The type is repeated string.
name: "list_x"
map {
iterator {
// iterator variable name is `iter`.
name: "iter"
// refer to FooList message's `foos` field.
src: "foo_list.foos"
}
// refer to `iter` variable and returns x field value.
by: "iter.x"
}
}
};
}
message FooList {
option (grpc.federation.message).custom_resolver = true;
repeated Foo foos = 1;
}
message Foo {
string x = 1;
}
field | type | required or optional |
---|---|---|
name |
string | required |
src |
CEL | required |
The name of the iterator variable.
The variable of repeated type that form the basis of iterator.
Create map elements using CEL
by referencing variables created with iterator
section.
Create map elements using message
value by referencing variables created with iterator
section.
A validation rule against variables defined within the current scope.
field | type | required or optional |
---|---|---|
name |
string | optional |
error |
ValidationError | required |
A unique name for the validation. If set, the validation error type will be Error. If omitted, the validation error type will be ValidationError.
A validation rule and validation error to be returned.
field | type | required or optional |
---|---|---|
code |
google.rpc.Code | required |
message |
string | optional |
if |
CEL | optional |
details |
repeated ValidationErrorDetail | optional |
A gRPC status code to be returned in case of validation error. For the available Enum codes, check googleapis/google/rpc /code.proto.
A gRPC status message in case of validation error. If omitted, the message will be auto-generated from the configurations.
A validation rule in CEL. If the condition is true, the validation returns the error.
The return value must always be of type boolean. Either if
or details
must be specified.
message MyMessage {
option (grpc.federation.message) = {
def {
validation {
name: "myMessageValidation",
error {
code: FAILED_PRECONDITION
message: "MyMessage validation failed",
if: "$.id == 'wrong'"
}
}
}
};
...
}
details
is a list of validation rules and error details. If the validation fails, the corresponding error details are set.
Either if
or details
must be specified. The other error detail types will be supported soon.
field | type | required or optional |
---|---|---|
if |
CEL | required |
by |
repeated CEL | optional |
message |
repeated MessageExpr | optional |
precondition_failure |
repeated google.rpc.PreconditionFailure | optional |
bad_request |
repeated google.rpc.BadRequest | optional |
localized_message |
repeated google.rpc.LocalizedMessage | optional |
message MyMessage {
option (grpc.federation.message) = {
def {
validation {
name: "myMessageValidation",
error {
code: FAILED_PRECONDITION
message: "MyMessage validation failed",
details {
if: "$.id == 'wrong'",
by: "pkg.Message{field: value}"
message {
name: "ErrorMessage",
args {
name: "message",
by: "'some message'"
}
}
precondition_failure {
violations {
type: "'some-type'"
subject: "'some-subject'"
description: "'some-desc'"
}
}
bad_request {
field_violations {
field: "'some-field'"
description: "'some-desc'"
}
}
localized_message {
locale: "en-US"
message: "'some message'"
}
}
}
}
}
};
...
}
if
specifies validation rule in CEL. If the condition is true, the validation returns an error with the specified details.
by
specify a message in CEL to express the details of the error.
message
represents arbitrary proto messages to describe the error detail.
precondition_failure
describes what preconditions have failed. See google.rpc.PreconditionFailure for the details.
bad_request
describes violations in a client request. See google.rpc.BadRequest for the details.
localized_message
provides a localized error message that is safe to return to the user. See google.rpc.BadRequest for the details.
There is a limit to what can be expressed in proto, so if you want to execute a process that cannot be expressed in proto, you will need to implement it yourself in the Go language.
If custom_resolver is true, the resolver for this message is implemented by Go.
If there are any values retrieved by def
, they are passed as arguments for custom resolver.
Each field of the message returned by the custom resolver is automatically bound.
If you want to change the binding process for a particular field, set custom_resolver=true
option for that field.
message Foo {
option (grpc.federation.message).custom_resolver = true;
}
or
message Foo {
User user = 1 [(grpc.federation.field).custom_resolver = true];
}
alias
mapping between messages defined in other packages and messages defined on the federation service side.
The alias
is the FQDN ( <package-name>.<message-name>
) to the message.
If this definition exists, type conversion is automatically performed before the field assignment operation.
If a message with this option has a field that is not present in the message specified by alias, and the alias option is not specified for that field, an error is occurred.
field | type | required or optional |
---|---|---|
by |
string | optional |
oneof |
FieldOneof | optional |
custom_resolver |
bool | optional |
alias |
string | optional |
by
used to refer to a variable or message argument defined in a grpc.federation.message
option by CEL.
You need to use $.
to refer to the message argument.
oneof
used to directly assign a value to a field defined by oneof
.
field | type | required or optional |
---|---|---|
if |
CEL | optional |
default |
bool | optional |
def |
repeated VariableDefinition | optional |
by |
CEL | optional |
if
is the condition to be assigned to oneof field.
The return value must be of bool type.
default
used to assign a value when none of the other fields match any of the specified expressions.
Only one value can be defined per oneof.
def
defines the list of variables to which the oneof field refers.
by
used to refer to a variable or message argument defined in a grpc.federation.message
and grpc.federation.field.oneof
by CEL.
You need to use $.
to refer to the message argument.
If custom_resolver is true, the field binding process is to be implemented in Go.
If there are any values retrieved by grpc.federation.message
option, they are passed as arguments for custom resolver.
alias
can be used when alias
is specified in grpc.federation.message
option,
and specifies the field name to be referenced among the messages specified in alias
of message option.
If the specified field has the same type or can be converted automatically, its value is assigned.
Message argument is an argument passed when depends on other messages via the def.message
feature. This parameter propagates the information necessary to resolve message dependencies.
Normally, only values explicitly specified in args
can be referenced as message arguments.
However, exceptionally, a field in the gRPC method's request message can be referenced as a message argument to the method's response.
For example, consider the GetPost
method of the FederationService
: the GetPostReply
message implicitly allows the fields of the GetPostRequest
message to be used as message arguments.
In summary, it looks like this.
- For the response message of rpc, the request message fields are the message arguments.
- For other messages, the parameters explicitly passed in
def.message.args
option are the message arguments. - You can access to the message argument with
$.
prefix.