// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; import "google/api/annotations.proto"; import "google/api/routing.proto"; import "google/api/client.proto"; import "google/protobuf/empty.proto"; package google.explicit.dynamic.routing.header; option java_package = "com.google.explicit.dynamic.routing.header"; option java_multiple_files = true; // This service is meant for testing all scenarios related to // explicit dynamic routing headers feature, including but not limited to all examples in routing.proto // All test cases in this proto assumes that the configured routing annotation is well formatted and passes all validation service ExplicitDynamicRoutingHeaderTesting { option (google.api.default_host) = "localhost:7469"; //Example 1 // Extracting a field from the request to put into the routing header // unchanged, with the key equal to the field name. rpc Example1Test(Request) returns (google.protobuf.Empty) { option (google.api.routing) = { //take the `app_profile_id` routing_parameters { field: "app_profile_id" } }; } // Example 2 // Extracting a field from the request to put into the routing header // unchanged, with the key different from the field name. rpc Example2Test(Request) returns (google.protobuf.Empty) { option (google.api.routing) = { // Take the `app_profile_id`, but name it `routing_id` in the header. routing_parameters { field: "app_profile_id" path_template: "{routing_id=**}" } }; } // Example 3 // // Extracting a field from the request to put into the routing // header, while matching a path template syntax on the field's value. rpc Example3Test(Request) returns (google.protobuf.Empty) { option (google.api.routing) = { routing_parameters { field: "table_name" path_template: "{table_name=projects/*/instances/*/**}" } }; } // Example 3c // //Multiple alternative conflictingly named path templates are // specified. The one that matches is used to construct the header. rpc Example3CTest(Request) returns (google.protobuf.Empty) { option (google.api.routing) = { routing_parameters { field: "table_name" path_template: "{table_name=regions/*/zones/*/**}" } routing_parameters { field: "table_name" path_template: "{table_name=projects/*/instances/*/**}" } }; } // Example 4 // // Extracting a single routing header key-value pair by matching a // template syntax on (a part of) a single request field. rpc Example4Test(Request) returns (google.protobuf.Empty) { option (google.api.routing) = { routing_parameters { field: "table_name" path_template: "{routing_id=projects/*}/**" } }; } // Example 5 // // Extracting a single routing header key-value pair by matching // several conflictingly named path templates on (parts of) a single request // field. The last template to match "wins" the conflict. rpc Example5Test(Request) returns (google.protobuf.Empty) { option (google.api.routing) = { // If the `table_name` does not have instances information, // take just the project id for routing. // Otherwise take project + instance. routing_parameters { field: "table_name" path_template: "{routing_id=projects/*}/**" } routing_parameters { field: "table_name" path_template: "{routing_id=projects/*/instances/*}/**" } }; } // Example 6 // // Extracting multiple routing header key-value pairs by matching // several non-conflicting path templates on (parts of) a single request field. rpc Example6Test(Request) returns (google.protobuf.Empty) { option (google.api.routing) = { // The routing code needs two keys instead of one composite // but works only for the tables with the "project-instance" name // syntax. routing_parameters { field: "table_name" path_template: "{project_id=projects/*}/instances/*/**" } routing_parameters { field: "table_name" path_template: "projects/*/{instance_id=instances/*}/**" } }; } // Example 7 // // Extracting multiple routing header key-value pairs by matching // several path templates on multiple request fields. rpc Example7Test(Request) returns (google.protobuf.Empty) { option (google.api.routing) = { // The routing needs both `project_id` and `routing_id` // (from the `app_profile_id` field) for routing. routing_parameters { field: "table_name" path_template: "{project_id=projects/*}/**" } routing_parameters { field: "app_profile_id" path_template: "{routing_id=**}" } }; } // Example 8 // // Extracting a single routing header key-value pair by matching // several conflictingly named path templates on several request fields. The // last template to match "wins" the conflict. rpc Example8Test(Request) returns (google.protobuf.Empty) { option (google.api.routing) = { // The `routing_id` can be a project id or a region id depending on // the table name format, but only if the `app_profile_id` is not set. // If `app_profile_id` is set it should be used instead. routing_parameters { field: "table_name" path_template: "{routing_id=projects/*}/**" } routing_parameters { field: "table_name" path_template: "{routing_id=regions/*}/**" } routing_parameters { field: "app_profile_id" path_template: "{routing_id=**}" } }; } // Example 9 // // Bringing it all together. rpc Example9Test(Request) returns (google.protobuf.Empty) { option (google.api.routing) = { // For routing both `table_location` and a `routing_id` are needed. // // table_location can be either an instance id or a region+zone id. // // For `routing_id`, take the value of `app_profile_id` // - If it's in the format `profiles/`, send // just the `` part. // - If it's any other literal, send it as is. // If the `app_profile_id` is empty, and the `table_name` starts with // the project_id, send that instead. routing_parameters { field: "table_name" path_template: "projects/*/{table_location=instances/*}/tables/*" } routing_parameters { field: "table_name" path_template: "{table_location=regions/*/zones/*}/tables/*" } routing_parameters { field: "table_name" path_template: "{routing_id=projects/*}/**" } routing_parameters { field: "app_profile_id" path_template: "{routing_id=**}" } routing_parameters { field: "app_profile_id" path_template: "profiles/{routing_id=*}" } }; } //We should ignore http annotation if both http and routing header annotations are configured, rpc BackwardsCompatible1Test(Request) returns (google.protobuf.Empty) { option (google.api.http) = { get: "/v1beta1/{table_name=tests/*}" }; option (google.api.routing) = { routing_parameters { field: "table_name" path_template: "{routing_id=projects/*}/**" } }; } //We should ignore http annotation even routing header annotation has no routing parameters rpc BackwardsCompatible2Test(Request) returns (google.protobuf.Empty) { option (google.api.http) = { get: "/v1beta1/{table_name=tests/*}" }; option (google.api.routing) = { }; } //Http annotation should still work for implicit routing headers as before if routing annotation is not configured. rpc BackwardsCompatible3Test(Request) returns (google.protobuf.Empty) { option (google.api.http) = { get: "/v1beta1/{table_name=tests/*}" }; } rpc NestedFieldTest(RequestWithNestedField) returns (google.protobuf.Empty) { option (google.api.routing) = { routing_parameters { field: "nested_field.another_nested_field.name" path_template: "{routing_id=projects/*}/**" } }; } } // Example message: // // { // table_name: projects/proj_foo/instances/instance_bar/table/table_baz, // app_profile_id: profiles/prof_qux // } message Request { // The name of the Table // Values can be of the following formats: // - `projects//tables/` // - `projects//instances//tables/
` // - `region//zones//tables/
` string table_name = 1; // This value specifies routing for replication. // It can be in the following formats: // - `profiles/` // - a legacy `profile_id` that can be any string string app_profile_id = 2; } message RequestWithNestedField { NestedField nested_field = 1; } message NestedField { AnotherNestedField another_nested_field = 1; } message AnotherNestedField { string name = 1; }