Skip to content

Commit 822966d

Browse files
authored
Nexus callbacks, tasks, and incoming service registry (#352)
1 parent 34e5d97 commit 822966d

File tree

12 files changed

+438
-1
lines changed

12 files changed

+438
-1
lines changed

api-linter.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,20 @@
1414
- "**/operatorservice/v1/request_response.proto"
1515
disabled_rules:
1616
- "core::0122::name-suffix"
17+
- "core::0131::request-name-behavior"
18+
- "core::0131::request-name-reference"
1719
- "core::0131::request-name-required"
1820
- "core::0131::request-unknown-fields"
19-
- "core::0132::request-parent-required"
2021
- "core::0132::request-unknown-fields"
22+
- "core::0132::request-parent-required"
2123
- "core::0132::response-unknown-fields"
24+
- "core::0133::request-parent-required"
25+
- "core::0133::request-resource-behavior"
26+
- "core::0133::request-resource-field"
2227
- "core::0134::request-unknown-fields"
28+
- "core::0135::request-resource-name"
29+
- "core::0135::request-name-behavior"
30+
- "core::0135::request-name-reference"
2331
- "core::0158::request-page-size-field"
2432
- "core::0158::request-page-token-field"
2533
- "core::0158::response-next-page-token-field"

google/protobuf/struct.proto

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Protocol Buffers - Google's data interchange format
2+
// Copyright 2008 Google Inc. All rights reserved.
3+
// https://developers.google.com/protocol-buffers/
4+
//
5+
// Redistribution and use in source and binary forms, with or without
6+
// modification, are permitted provided that the following conditions are
7+
// met:
8+
//
9+
// * Redistributions of source code must retain the above copyright
10+
// notice, this list of conditions and the following disclaimer.
11+
// * Redistributions in binary form must reproduce the above
12+
// copyright notice, this list of conditions and the following disclaimer
13+
// in the documentation and/or other materials provided with the
14+
// distribution.
15+
// * Neither the name of Google Inc. nor the names of its
16+
// contributors may be used to endorse or promote products derived from
17+
// this software without specific prior written permission.
18+
//
19+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20+
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21+
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22+
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23+
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25+
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26+
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27+
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28+
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
31+
syntax = "proto3";
32+
33+
package google.protobuf;
34+
35+
option cc_enable_arenas = true;
36+
option go_package = "google.golang.org/protobuf/types/known/structpb";
37+
option java_package = "com.google.protobuf";
38+
option java_outer_classname = "StructProto";
39+
option java_multiple_files = true;
40+
option objc_class_prefix = "GPB";
41+
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
42+
43+
// `Struct` represents a structured data value, consisting of fields
44+
// which map to dynamically typed values. In some languages, `Struct`
45+
// might be supported by a native representation. For example, in
46+
// scripting languages like JS a struct is represented as an
47+
// object. The details of that representation are described together
48+
// with the proto support for the language.
49+
//
50+
// The JSON representation for `Struct` is JSON object.
51+
message Struct {
52+
// Unordered map of dynamically typed values.
53+
map<string, Value> fields = 1;
54+
}
55+
56+
// `Value` represents a dynamically typed value which can be either
57+
// null, a number, a string, a boolean, a recursive struct value, or a
58+
// list of values. A producer of value is expected to set one of these
59+
// variants. Absence of any variant indicates an error.
60+
//
61+
// The JSON representation for `Value` is JSON value.
62+
message Value {
63+
// The kind of value.
64+
oneof kind {
65+
// Represents a null value.
66+
NullValue null_value = 1;
67+
// Represents a double value.
68+
double number_value = 2;
69+
// Represents a string value.
70+
string string_value = 3;
71+
// Represents a boolean value.
72+
bool bool_value = 4;
73+
// Represents a structured value.
74+
Struct struct_value = 5;
75+
// Represents a repeated `Value`.
76+
ListValue list_value = 6;
77+
}
78+
}
79+
80+
// `NullValue` is a singleton enumeration to represent the null value for the
81+
// `Value` type union.
82+
//
83+
// The JSON representation for `NullValue` is JSON `null`.
84+
enum NullValue {
85+
// Null value.
86+
NULL_VALUE = 0;
87+
}
88+
89+
// `ListValue` is a wrapper around a repeated field of values.
90+
//
91+
// The JSON representation for `ListValue` is JSON array.
92+
message ListValue {
93+
// Repeated field of dynamically typed values.
94+
repeated Value values = 1;
95+
}

temporal/api/common/v1/message.proto

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,18 @@ message ResetOptions {
178178
// possibly others in the future.)
179179
bool current_run_only = 11;
180180
}
181+
182+
// Callback to attach to various events in the system, e.g. workflow run completion.
183+
message Callback {
184+
message Nexus {
185+
// Callback URL.
186+
// (-- api-linter: core::0140::uri=disabled
187+
// aip.dev/not-precedent: Not following this guideline. --)
188+
string url = 1;
189+
}
190+
191+
reserved 1; // For a generic callback mechanism to be added later.
192+
oneof variant {
193+
Nexus nexus = 2;
194+
}
195+
}

temporal/api/enums/v1/common.proto

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,19 @@ enum Severity {
5454
SEVERITY_MEDIUM = 2;
5555
SEVERITY_LOW = 3;
5656
}
57+
58+
// State of the callback.
59+
enum CallbackState {
60+
// Default value, unspecified state.
61+
CALLBACK_STATE_UNSPECIFIED = 0;
62+
// Callback is standing by, waiting to be triggered.
63+
CALLBACK_STATE_STANDBY = 1;
64+
// Callback is in the queue waiting to be executed or is currently executing.
65+
CALLBACK_STATE_SCHEDULED = 2;
66+
// Callback has failed with a retryable error and is backing off before the next attempt.
67+
CALLBACK_STATE_BACKING_OFF = 3;
68+
// Callback has failed.
69+
CALLBACK_STATE_FAILED = 4;
70+
// Callback has succeeded.
71+
CALLBACK_STATE_SUCCEEDED = 5;
72+
}

temporal/api/enums/v1/task_queue.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ enum TaskQueueType {
5656
TASK_QUEUE_TYPE_WORKFLOW = 1;
5757
// Activity type of task queue.
5858
TASK_QUEUE_TYPE_ACTIVITY = 2;
59+
// Task queue type for dispatching Nexus requests.
60+
TASK_QUEUE_TYPE_NEXUS = 3;
5961
}
6062

6163
// Specifies which category of tasks may reach a worker on a versioned task queue.

temporal/api/history/v1/message.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ message WorkflowExecutionStartedEventAttributes {
104104
// If this workflow intends to use anything other than the current overall default version for
105105
// the queue, then we include it here.
106106
temporal.api.common.v1.WorkerVersionStamp source_version_stamp = 29;
107+
108+
// Completion callbacks attached when this workflow was started.
109+
repeated temporal.api.common.v1.Callback completion_callbacks = 30;
107110
}
108111

109112
message WorkflowExecutionCompletedEventAttributes {
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// The MIT License
2+
//
3+
// Copyright (c) 2023 Temporal Technologies Inc. All rights reserved.
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
syntax = "proto3";
22+
23+
package temporal.api.nexus.v1;
24+
25+
option go_package = "go.temporal.io/api/nexus/v1;nexus";
26+
option java_package = "io.temporal.api.nexus.v1";
27+
option java_multiple_files = true;
28+
option java_outer_classname = "MessageProto";
29+
option ruby_package = "Temporalio::Api::Nexus::V1";
30+
option csharp_namespace = "Temporalio.Api.Nexus.V1";
31+
32+
import "google/protobuf/any.proto";
33+
import "google/protobuf/struct.proto";
34+
import "temporal/api/common/v1/message.proto";
35+
36+
// A general purpose failure message.
37+
// See: https://github.com/nexus-rpc/api/blob/main/SPEC.md#failure
38+
message Failure {
39+
string message = 1;
40+
map<string, string> metadata = 2;
41+
google.protobuf.Value details = 3;
42+
}
43+
44+
message HandlerError {
45+
// See https://github.com/nexus-rpc/api/blob/main/SPEC.md#predefined-handler-errors.
46+
string error_type = 1;
47+
Failure failure = 2;
48+
}
49+
50+
message UnsuccessfulOperationError {
51+
// See https://github.com/nexus-rpc/api/blob/main/SPEC.md#operationinfo.
52+
string operation_state = 1;
53+
Failure failure = 2;
54+
}
55+
56+
// A request to start an operation.
57+
message StartOperationRequest {
58+
// Type of operation to start.
59+
string operation = 1;
60+
// A request ID that can be used as an idempotentency key.
61+
string request_id = 2;
62+
// Callback URL to call upon completion if the started operation is async.
63+
string callback = 3;
64+
// Full request body from the incoming HTTP request.
65+
temporal.api.common.v1.Payload payload = 4;
66+
}
67+
68+
// A request to cancel an operation.
69+
message CancelOperationRequest {
70+
// Type of operation to cancel.
71+
string operation = 1;
72+
// Operation ID as originally generated by a Handler.
73+
string operation_id = 2;
74+
}
75+
76+
// A Nexus request.
77+
message Request {
78+
// Headers extracted from the original request in the Temporal frontend.
79+
// When using Nexus over HTTP, this includes the request's HTTP headers ignoring multiple values.
80+
map<string, string> header = 1;
81+
oneof variant {
82+
StartOperationRequest start_operation = 2;
83+
CancelOperationRequest cancel_operation = 3;
84+
}
85+
}
86+
87+
// Response variant for StartOperationRequest.
88+
message StartOperationResponse {
89+
// An operation completed successfully.
90+
message Sync {
91+
temporal.api.common.v1.Payload payload = 1;
92+
}
93+
94+
// The operation will complete asynchronously.
95+
// The returned ID can be used to reference this operation.
96+
message Async {
97+
string operation_id = 1;
98+
}
99+
100+
oneof variant {
101+
Sync sync_success = 1;
102+
Async async_success = 2;
103+
// The operation completed unsuccessfully (failed or canceled).
104+
UnsuccessfulOperationError operation_error = 3;
105+
}
106+
}
107+
108+
// Response variant for CancelOperationRequest.
109+
message CancelOperationResponse {
110+
}
111+
112+
// A response indicating that the handler has successfully processed a request.
113+
message Response {
114+
// Variant must correlate to the corresponding Request's variant.
115+
oneof variant {
116+
StartOperationResponse start_operation = 1;
117+
CancelOperationResponse cancel_operation = 2;
118+
}
119+
}
120+
121+
// A binding from a service name to namespace, task queue, and metadata for dispatching incoming Nexus requests.
122+
message IncomingService {
123+
// Data version for this service. Must match current version on update or set to 0 to create a new service.
124+
int64 version = 1;
125+
// Service name, unique for this cluster.
126+
// The service name is used to address this service.
127+
// By default, when using Nexus over HTTP, the service name is matched against the base URL path.
128+
// E.g. the URL /my-service would match a service named "my-service".
129+
// The name can contain any characters and is escaped when matched against a URL.
130+
string name = 2;
131+
// Namespace to route requests to.
132+
string namespace = 3;
133+
// Task queue to route requests to.
134+
string task_queue = 4;
135+
// Generic service metadata that is available to the server's authorizer.
136+
map<string, google.protobuf.Any> metadata = 5;
137+
}
138+

temporal/api/operatorservice/v1/request_response.proto

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ option ruby_package = "Temporalio::Api::OperatorService::V1";
3232
option csharp_namespace = "Temporalio.Api.OperatorService.V1";
3333

3434
import "temporal/api/enums/v1/common.proto";
35+
import "temporal/api/nexus/v1/message.proto";
3536
import "google/protobuf/duration.proto";
3637

3738
// (-- Search Attribute --)
@@ -128,3 +129,43 @@ message ClusterMetadata {
128129
// A flag to indicate if a connection is active.
129130
bool is_connection_enabled = 6;
130131
}
132+
133+
message GetNexusIncomingServiceRequest {
134+
// Name of service to retrieve.
135+
string name = 1;
136+
}
137+
138+
message GetNexusIncomingServiceResponse {
139+
temporal.api.nexus.v1.IncomingService service = 1;
140+
}
141+
142+
message CreateOrUpdateNexusIncomingServiceRequest {
143+
temporal.api.nexus.v1.IncomingService service = 1;
144+
}
145+
146+
message CreateOrUpdateNexusIncomingServiceResponse {
147+
// Data post acceptance. Can be used to issue additional updates to this record.
148+
temporal.api.nexus.v1.IncomingService service = 1;
149+
}
150+
151+
message DeleteNexusIncomingServiceRequest {
152+
// Name of service to delete.
153+
string name = 1;
154+
}
155+
156+
message DeleteNexusIncomingServiceResponse {
157+
}
158+
159+
message ListNexusIncomingServicesRequest {
160+
int32 page_size = 1;
161+
// To get the next page, pass in `ListNexusIncomingServicesResponse.next_page_token` from the previous page's
162+
// response, the token will be empty if there's no other page.
163+
// Note: the last page may be empty if the total number of services registered is a multiple of the page size.
164+
bytes next_page_token = 2;
165+
}
166+
167+
message ListNexusIncomingServicesResponse {
168+
// Token for getting the next page.
169+
bytes next_page_token = 1;
170+
repeated temporal.api.nexus.v1.IncomingService services = 2;
171+
}

temporal/api/operatorservice/v1/service.proto

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,24 @@ service OperatorService {
8181
// ListClusters returns information about Temporal clusters.
8282
rpc ListClusters(ListClustersRequest) returns (ListClustersResponse) {
8383
}
84+
85+
// Get a registered incoming Nexus service by name. The returned version can be used for optimistic updates.
86+
rpc GetNexusIncomingService(GetNexusIncomingServiceRequest) returns (GetNexusIncomingServiceResponse) {
87+
}
88+
89+
// Optimistically create or update a Nexus service based on provided version.
90+
// To update an existing service, get the current service record via the `GetNexusIncomingService` API, modify it
91+
// and submit to this API.
92+
// Set version to 0 to create a new service.
93+
// Returns the updated service with the updated version, which can be used for subsequent updates.
94+
rpc CreateOrUpdateNexusIncomingService(CreateOrUpdateNexusIncomingServiceRequest) returns (CreateOrUpdateNexusIncomingServiceResponse) {
95+
}
96+
97+
// Delete an incoming Nexus service by name.
98+
rpc DeleteNexusIncomingService(DeleteNexusIncomingServiceRequest) returns (DeleteNexusIncomingServiceResponse) {
99+
}
100+
101+
// List all nexus incoming service names. Use next_page_token in the response for pagination.
102+
rpc ListNexusIncomingServices(ListNexusIncomingServicesRequest) returns (ListNexusIncomingServicesResponse) {
103+
}
84104
}

0 commit comments

Comments
 (0)