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/duration.proto";
import "google/protobuf/timestamp.proto";
import "temporal/api/common/v1/message.proto";
import "temporal/api/enums/v1/common.proto";
import "temporal/api/enums/v1/nexus.proto";
import "temporal/api/failure/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;
    string stack_trace = 4;
    map<string, string> metadata = 2;
    // UTF-8 encoded JSON serializable details.
    bytes details = 3;
    Failure cause = 5;
}

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 {
    message Capabilities {
        // If set, handlers may use temporal.api.failure.v1.Failure instances to return failures to the server.
        // This also allows handler and operation errors to have their own messages and stack traces.
        bool temporal_failure_responses = 1;
    }

    // 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;

    Capabilities capabilities = 100;

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

    // The endpoint this request was addressed to before forwarding to the worker.
    // Supported from server version 1.30.0.
    string endpoint = 10;
}

// 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).
        // Deprecated. Use the failure variant instead.
        UnsuccessfulOperationError operation_error = 3 [deprecated = true];
        // The operation completed unsuccessfully (failed or canceled).
        // Failure object must contain an ApplicationFailureInfo or CanceledFailureInfo object.
        temporal.api.failure.v1.Failure failure = 4;
    }
}

// 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;
    }
}

// NexusOperationExecutionCancellationInfo contains the state of a Nexus operation cancellation.
message NexusOperationExecutionCancellationInfo {
    // The time when cancellation was requested.
    google.protobuf.Timestamp requested_time = 1;

    temporal.api.enums.v1.NexusOperationCancellationState state = 2;

    // The number of attempts made to deliver the cancel operation request.
    // This number represents a minimum bound since the attempt is incremented after the request completes.
    int32 attempt = 3;

    // The time when the last attempt completed.
    google.protobuf.Timestamp last_attempt_complete_time = 4;
    // The last attempt's failure, if any.
    temporal.api.failure.v1.Failure last_attempt_failure = 5;
    // The time when the next attempt is scheduled.
    google.protobuf.Timestamp next_attempt_schedule_time = 6;

    // If the state is BLOCKED, blocked reason provides additional information.
    string blocked_reason = 7;

    // A reason that may be specified in the CancelNexusOperationRequest.
    string reason = 8;
}

// Full current state of a standalone Nexus operation, as of the time of the request.
message NexusOperationExecutionInfo {
    // Unique identifier of this Nexus operation within its namespace along with run ID (below).
    string operation_id = 1;
    string run_id = 2;

    // Endpoint name, resolved to a URL via the cluster's endpoint registry.
    string endpoint = 3;
    // Service name.
    string service = 4;
    // Operation name.
    string operation = 5;

    // A general status for this operation, indicates whether it is currently running or in one of the terminal statuses.
    // Updated once when the operation is originally scheduled, and again when it reaches a terminal status.
    temporal.api.enums.v1.NexusOperationExecutionStatus status = 6;
    // More detailed breakdown of NEXUS_OPERATION_EXECUTION_STATUS_RUNNING.
    temporal.api.enums.v1.PendingNexusOperationState state = 7;

    // Schedule-to-close timeout for this operation.
    // (-- api-linter: core::0140::prepositions=disabled
    //     aip.dev/not-precedent: "to" is used to indicate interval. --)
    google.protobuf.Duration schedule_to_close_timeout = 8;

    // Schedule-to-start timeout for this operation.
    // (-- api-linter: core::0140::prepositions=disabled
    //     aip.dev/not-precedent: "to" is used to indicate interval. --)
    google.protobuf.Duration schedule_to_start_timeout = 9;

    // Start-to-close timeout for this operation.
    // (-- api-linter: core::0140::prepositions=disabled
    //     aip.dev/not-precedent: "to" is used to indicate interval. --)
    google.protobuf.Duration start_to_close_timeout = 10;

    // The number of attempts made to deliver the start operation request.
    // This number is approximate, it is incremented when a task is added to the history queue.
    // In practice, there could be more attempts if a task is executed but fails to commit, or less attempts if a task
    // was never executed.
    int32 attempt = 11;

    // Time the operation was originally scheduled via a StartNexusOperation request.
    google.protobuf.Timestamp schedule_time = 12;
    // Scheduled time + schedule to close timeout.
    google.protobuf.Timestamp expiration_time = 13;
    // Time when the operation transitioned to a closed state.
    google.protobuf.Timestamp close_time = 14;

    // The time when the last attempt completed.
    google.protobuf.Timestamp last_attempt_complete_time = 15;
    // The last attempt's failure, if any.
    temporal.api.failure.v1.Failure last_attempt_failure = 16;
    // The time when the next attempt is scheduled.
    google.protobuf.Timestamp next_attempt_schedule_time = 17;

    // Elapsed time from schedule_time to now for running operations or to close_time for closed
    // operations, including all attempts and backoff between attempts.
    google.protobuf.Duration execution_duration = 18;

    NexusOperationExecutionCancellationInfo cancellation_info = 19;

    // If the state is BLOCKED, blocked reason provides additional information.
    string blocked_reason = 20;

    // Server-generated request ID used as an idempotency token when submitting start requests to
    // the handler. Distinct from the request_id in StartNexusOperationRequest, which is the
    // caller-side idempotency key for the StartNexusOperation RPC itself.
    string request_id = 21;

    // Operation token. Only set for asynchronous operations after a successful StartOperation call.
    string operation_token = 22;

    // Incremented each time the operation's state is mutated in persistence.
    int64 state_transition_count = 23;

    temporal.api.common.v1.SearchAttributes search_attributes = 24;

    // Header for context propagation and tracing purposes.
    map<string, string> nexus_header = 25;

    // Metadata for use by user interfaces to display the fixed as-of-start summary and details of the operation.
    temporal.api.sdk.v1.UserMetadata user_metadata = 26;

    // Links attached by the handler of this operation on start or completion.
    repeated temporal.api.common.v1.Link links = 27;

    // The identity of the client who started this operation.
    string identity = 28;
}

// Limited Nexus operation information returned in the list response.
// When adding fields here, ensure that it is also present in NexusOperationExecutionInfo (note that it may already be present in
// NexusOperationExecutionInfo but not at the top-level).
message NexusOperationExecutionListInfo {
    // A unique identifier of this operation within its namespace along with run ID (below).
    string operation_id = 1;
    // The run ID of the standalone Nexus operation.
    string run_id = 2;

    // Endpoint name.
    string endpoint = 3;
    // Service name.
    string service = 4;
    // Operation name.
    string operation = 5;

    // Time the operation was originally scheduled via a StartNexusOperation request.
    google.protobuf.Timestamp schedule_time = 6;
    // If the operation is in a terminal status, this field represents the time the operation transitioned to that status.
    google.protobuf.Timestamp close_time = 7;
    // The status is updated once, when the operation is originally scheduled, and again when the operation reaches a terminal status.
    temporal.api.enums.v1.NexusOperationExecutionStatus status = 8;

    // Search attributes from the start request.
    temporal.api.common.v1.SearchAttributes search_attributes = 9;

    // Updated on terminal status.
    int64 state_transition_count = 10;
    // The difference between close time and scheduled time.
    // This field is only populated if the operation is closed.
    google.protobuf.Duration execution_duration = 11;
}
