// The MIT License
//
// Copyright (c) 2023 Temporal Technologies Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

syntax = "proto3";

package temporal.api.nexus.v1;

option go_package = "go.temporal.io/api/nexus/v1;nexus";
option java_package = "io.temporal.api.nexus.v1";
option java_multiple_files = true;
option java_outer_classname = "MessageProto";
option ruby_package = "Temporalio::Api::Nexus::V1";
option csharp_namespace = "Temporalio.Api.Nexus.V1";

import "google/protobuf/timestamp.proto";
import "temporal/api/common/v1/message.proto";
import "temporal/api/sdk/v1/user_metadata.proto";

// A general purpose failure message.
// See: https://github.com/nexus-rpc/api/blob/main/SPEC.md#failure
message Failure {
    string message = 1;
    map<string, string> metadata = 2;
    bytes details = 3;
}

message HandlerError {
    // See https://github.com/nexus-rpc/api/blob/main/SPEC.md#predefined-handler-errors.
    string error_type = 1;
    Failure failure = 2;
}

message UnsuccessfulOperationError {
    // See https://github.com/nexus-rpc/api/blob/main/SPEC.md#operationinfo.
    string operation_state = 1;
    Failure failure = 2;
}

// A request to start an operation.
message StartOperationRequest {
    // Name of service to start the operation in.
    string service = 1;
    // Type of operation to start.
    string operation = 2;
    // A request ID that can be used as an idempotentency key.
    string request_id = 3;
    // Callback URL to call upon completion if the started operation is async.
    string callback = 4;
    // Full request body from the incoming HTTP request.
    temporal.api.common.v1.Payload payload = 5;
    // Header that is expected to be attached to the callback request when the operation completes.
    map<string, string> callback_header = 6;
}

// A request to cancel an operation.
message CancelOperationRequest {
    // Service name.
    string service = 1;
    // Type of operation to cancel.
    string operation = 2;
    // Operation ID as originally generated by a Handler.
    string operation_id = 3;
}

// A Nexus request.
message Request {
    // Headers extracted from the original request in the Temporal frontend.
    // When using Nexus over HTTP, this includes the request's HTTP headers ignoring multiple values.
    map<string, string> header = 1;

    // The timestamp when the request was scheduled in the frontend.
    // (-- api-linter: core::0142::time-field-names=disabled
    //     aip.dev/not-precedent: Not following linter rules. --)
    google.protobuf.Timestamp scheduled_time = 2;

    oneof variant {
        StartOperationRequest start_operation = 3;
        CancelOperationRequest cancel_operation = 4;
    }
}

// Response variant for StartOperationRequest.
message StartOperationResponse {
    // An operation completed successfully.
    message Sync {
        temporal.api.common.v1.Payload payload = 1;
    }

    // The operation will complete asynchronously.
    // The returned ID can be used to reference this operation.
    message Async {
        string operation_id = 1;
    }

    oneof variant {
        Sync sync_success = 1;
        Async async_success = 2;
        // The operation completed unsuccessfully (failed or canceled).
        UnsuccessfulOperationError operation_error = 3;
    }
}

// Response variant for CancelOperationRequest.
message CancelOperationResponse {
}

// A response indicating that the handler has successfully processed a request.
message Response {
    // Variant must correlate to the corresponding Request's variant.
    oneof variant {
        StartOperationResponse start_operation = 1;
        CancelOperationResponse cancel_operation = 2;
    }
}

// A cluster-global binding from an endpoint ID to a target for dispatching incoming Nexus requests.
message Endpoint {
    // Data version for this endpoint, incremented for every update issued via the UpdateNexusEndpoint API.
    int64 version = 1;
    // Unique server-generated endpoint ID.
    string id = 2;
    // Spec for the endpoint.
    EndpointSpec spec = 3;

    // The date and time when the endpoint was created.
    // (-- api-linter: core::0142::time-field-names=disabled
    //     aip.dev/not-precedent: Not following linter rules. --)
    google.protobuf.Timestamp created_time = 4;

    // The date and time when the endpoint was last modified.
    // Will not be set if the endpoint has never been modified.
    // (-- api-linter: core::0142::time-field-names=disabled
    //     aip.dev/not-precedent: Not following linter rules. --)
    google.protobuf.Timestamp last_modified_time = 5;

    // Server exposed URL prefix for invocation of operations on this endpoint.
    // This doesn't include the protocol, hostname or port as the server does not know how it should be accessed
    // publicly. The URL is stable in the face of endpoint renames.
    string url_prefix = 6;
}

// Contains mutable fields for an Endpoint.
message EndpointSpec {
    // Endpoint name, unique for this cluster. Must match `[a-zA-Z_][a-zA-Z0-9_]*`.
    // Renaming an endpoint breaks all workflow callers that reference this endpoint, causing operations to fail.
    string name = 1;

    temporal.api.sdk.v1.UserMetadata metadata = 2;

    // Target to route requests to.
    EndpointTarget target = 3;
}

// Target to route requests to.
message EndpointTarget {
    // Target a worker polling on a Nexus task queue in a specific namespace.
    message Worker {
        // Namespace to route requests to.
        string namespace = 1;
        // Nexus task queue to route requests to.
        string task_queue = 2;
    }
  
    // Target an external server by URL.
    // At a later point, this will support providing credentials, in the meantime, an http.RoundTripper can be injected
    // into the server to modify the request.
    message External {
        // URL to call.
        string url = 1;
    }

    oneof variant {
        Worker worker = 1;
        External external = 2;
    }
}
