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/enums/v1/nexus.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;
    // UTF-8 encoded JSON serializable details.
    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;
    // Retry behavior, defaults to the retry behavior of the error type as defined in the spec.
    temporal.api.enums.v1.NexusHandlerErrorRetryBehavior retry_behavior = 3;
}

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

message Link {
    // See https://github.com/nexus-rpc/api/blob/main/SPEC.md#links.
    string url = 1;
    string type = 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;
    // Links contain caller information and can be attached to the operations started by the handler.
    repeated Link links = 7;
}

// 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.
    //
    // Deprecated. Renamed to operation_token.
    string operation_id = 3 [deprecated = true];

    // Operation token as originally generated by a Handler.
    string operation_token = 4;
}

// 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;
        repeated Link links = 2;
    }

    // The operation will complete asynchronously.
    // The returned ID can be used to reference this operation.
    message Async {
        // Deprecated. Renamed to operation_token.
        string operation_id = 1 [deprecated = true];
        repeated Link links = 2;
        string operation_token = 3;
    }

    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;

    // Markdown description serialized as a single JSON string.
    // If the Payload is encrypted, the UI and CLI may decrypt with the configured codec server endpoint.
    // By default, the server enforces a limit of 20,000 bytes for this entire payload.
    temporal.api.common.v1.Payload description = 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;
    }
}
