syntax = "proto3";

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/any.proto";

package obelisk;

service ExecutionRepository {
    rpc GenerateExecutionId(GenerateExecutionIdRequest) returns (GenerateExecutionIdResponse);
    rpc Submit(SubmitRequest) returns (SubmitResponse);
    rpc GetStatus(GetStatusRequest) returns (stream GetStatusResponse);
    rpc ListExecutions(ListExecutionsRequest) returns (ListExecutionsResponse);
    // Get the execution log
    rpc ListExecutionEvents(ListExecutionEventsRequest) returns (ListExecutionEventsResponse);
    // List child execution responses
    rpc ListResponses(ListResponsesRequest) returns (ListResponsesResponse);
    rpc ListExecutionEventsAndResponses(ListExecutionEventsAndResponsesRequest) returns (ListExecutionEventsAndResponsesResponse);
    rpc GetBacktrace(GetBacktraceRequest) returns (GetBacktraceResponse);
    rpc GetBacktraceSource(GetBacktraceSourceRequest) returns (GetBacktraceSourceResponse);
    rpc Stub(StubRequest) returns (StubResponse);
    // Activity cancellation is best effort.
    rpc Cancel(CancelRequest) returns (CancelResponse);
    rpc ListLogs(ListLogsRequest) returns (ListLogsResponse);

    rpc ReplayExecution(ReplayExecutionRequest) returns (ReplayExecutionResponse);
    rpc UpgradeExecutionComponent(UpgradeExecutionComponentRequest) returns (UpgradeExecutionComponentResponse);

    rpc PauseExecution(PauseExecutionRequest) returns (PauseExecutionResponse);
    rpc UnpauseExecution(UnpauseExecutionRequest) returns (UnpauseExecutionResponse);
}

service FunctionRepository {
    rpc ListComponents(ListComponentsRequest) returns (ListComponentsResponse);
    rpc GetWit(GetWitRequest) returns (GetWitResponse);
}

service DeploymentRepository {
    rpc ListDeployments(ListDeploymentsRequest) returns (ListDeploymentsResponse);
    rpc GetCurrentDeploymentId(GetCurrentDeploymentIdRequest) returns (GetCurrentDeploymentIdResponse);
    rpc SwitchDeployment(SwitchDeploymentRequest) returns (SwitchDeploymentResponse);
    rpc SubmitDeployment(SubmitDeploymentRequest) returns (SubmitDeploymentResponse);
    rpc GetDeployment(GetDeploymentRequest) returns (GetDeploymentResponse);
}

message GenerateExecutionIdRequest {
}

message GenerateExecutionIdResponse {
    ExecutionId execution_id = 1;
}

message ListExecutionsRequest {
    message Cursor {
        oneof cursor {
            ExecutionId execution_id = 1;
            google.protobuf.Timestamp created_at = 2;
        }
    }
    message NewerThan {
        uint32 length = 1;
        Cursor cursor = 2;
        bool including_cursor = 3;
    }
    message OlderThan {
        uint32 length = 1;
        Cursor cursor = 2;
        bool including_cursor = 3;
    }

    optional string function_name_prefix = 1;

    // Do not return child executions
    bool top_level_only = 2;
    bool hide_finished = 5;
    optional string execution_id_prefix = 6;

    oneof pagination { // latest by `created_at` if not set
        NewerThan newer_than = 3;
        OlderThan older_than = 4;
    };

    optional ContentDigest component_digest = 7;
    optional DeploymentId deployment_id = 8;
}

message ExecutionSummary {
    ExecutionId execution_id = 1;
    FunctionName function_name = 2;
    ExecutionStatus current_status = 3;
    google.protobuf.Timestamp created_at = 4;
    google.protobuf.Timestamp first_scheduled_at = 5;
    ContentDigest component_digest = 6;
    DeploymentId deployment_id = 7;
    ComponentType component_type = 8;
}

message ListExecutionsResponse {
    repeated ExecutionSummary executions = 3;
}

message ListComponentsRequest {
    optional FunctionName function_name = 1;
    optional ContentDigest component_digest = 2;
    bool extensions = 3;
}

message ListComponentsResponse {
    repeated Component components = 1;
}

message Component {
    ComponentId component_id = 1;
    repeated FunctionDetail exports = 6; // Functions are sorted by their interface name
    repeated FunctionDetail imports = 7; // Functions are sorted by their interface name
}

enum ComponentType {
    COMPONENT_TYPE_UNSPECIFIED = 0;
    COMPONENT_TYPE_WORKFLOW = 1;
    COMPONENT_TYPE_ACTIVITY = 2;
    COMPONENT_TYPE_WEBHOOK_ENDPOINT = 3;
    COMPONENT_TYPE_ACTIVITY_STUB = 4;
}

message FunctionDetail {
    FunctionName function_name = 1;
    repeated FunctionParameter params = 2;
    optional WitType return_type = 3;
    optional FunctionExtension extension = 4;
    bool submittable = 5;
}

enum FunctionExtension {
    FUNCTION_EXTENSION_UNSPECIFIED = 0;
    FUNCTION_EXTENSION_SUBMIT = 1;
    FUNCTION_EXTENSION_AWAIT_NEXT = 2;
    FUNCTION_EXTENSION_SCHEDULE = 3;
    FUNCTION_EXTENSION_STUB = 4;
    FUNCTION_EXTENSION_GET = 5;
}

message FunctionParameter {
    WitType type = 1;
    string name = 2;
}

message WitType {
    // WIT string as outputed by WIT printer. May contain external types
    string wit_type = 1;

    // Internal information about the type serialized as JSON
    // deprecated
    string type_wrapper = 2;

    // WIT type without external references, e.g. `variant { first, second(string) }`
    string wit_type_inline = 3;
}

message ExecutionId {
    string id = 1;
}

message RunId {
    string id = 1;
}

message JoinSetId {
    enum JoinSetKind {
        JOIN_SET_KIND_UNSPECIFIED = 0;
        JOIN_SET_KIND_ONE_OFF = 1;
        JOIN_SET_KIND_NAMED = 2;
        JOIN_SET_KIND_GENERATED = 3;
    }

    JoinSetKind kind = 2;
    string name = 3;
}

message DelayId {
    string id = 1;
}

// sha256 digest of the component
message ContentDigest {
    string digest = 1; // hex encoded with `sha256:` prefix.
}

message ComponentId {
    ComponentType component_type = 1;
    string name = 2;
    ContentDigest digest = 3;
}

message DeploymentId {
    string id = 1;
}

message FunctionName {
    // `namespace:pkg_name/ifc_name` or `namespace:pkg_name/ifc_name@version`
    string interface_name = 1;
    string function_name = 2;
}

message SubmitRequest {
    ExecutionId execution_id = 1;
    FunctionName function_name = 2;
    google.protobuf.Any params = 3;
}

message SubmitResponse {
    enum Outcome {
        OUTCOME_UNSPECIFIED = 0;
        OUTCOME_CREATED = 1;
        OUTCOME_EXISTS_WITH_SAME_PARAMETERS = 2;
    }

    Outcome outcome = 1;
}

message StubRequest {
    ExecutionId execution_id = 1;
    google.protobuf.Any return_value = 4;
}

message StubResponse {
}

message GetStatusRequest {
    ExecutionId execution_id = 1;
    bool follow = 2;
    bool send_finished_status = 3;
}

// Corresponds to concise Pending State
message ExecutionStatus {
    message Locked {
        RunId run_id = 2;
        google.protobuf.Timestamp lock_expires_at = 3;
    }
    message PendingAt {
        google.protobuf.Timestamp scheduled_at = 1;
    }
    message BlockedByJoinSet {
        JoinSetId join_set_id = 1;
        google.protobuf.Timestamp lock_expires_at = 2;
        bool closing = 3;
    }
    message Finished {
        reserved 1;
        reserved 2;
        reserved 3;
        google.protobuf.Timestamp finished_at = 4;
        ResultKind result_kind = 5;
    }
    message Paused {
    }

    oneof status {
        Locked locked = 1;
        PendingAt pending_at = 2;
        BlockedByJoinSet blocked_by_join_set = 3;
        Finished finished = 4;
        Paused paused = 6;
    }
    ContentDigest component_digest = 5;
}

message ResultKind {
    message Ok {
    }
    message Error {
    }
    oneof value {
        Ok ok = 1;
        Error error = 2;
        ExecutionFailureKind execution_failure_kind = 3;
    }
}

message FinishedStatus {
    google.protobuf.Timestamp created_at = 1;
    google.protobuf.Timestamp scheduled_at = 2;
    google.protobuf.Timestamp finished_at = 3;
    SupportedFunctionResult value = 4;
}

enum ExecutionFailureKind {
    EXECUTION_FAILURE_KIND_UNSPECIFIED = 0;
    // Applicable to activities only, because workflows will be retried forever
    EXECUTION_FAILURE_KIND_TIMED_OUT = 1;
    // Applicable to workflows
    EXECUTION_FAILURE_KIND_NONDETERMINISM_DETECTED = 2;
    // Applicable to WASM components
    EXECUTION_FAILURE_KIND_OUT_OF_FUEL = 3;
    // Applicable to activities
    EXECUTION_FAILURE_KIND_CANCELLED = 4;
    EXECUTION_FAILURE_KIND_UNCATEGORIZED = 5;
}

message SupportedFunctionResult {
    message OkPayload {
        google.protobuf.Any return_value = 1;
    }
    message ErrorPayload {
        google.protobuf.Any return_value = 1;
    }

    // FinishedExecutionError
    message ExecutionFailure {
        ExecutionFailureKind kind = 1;
        optional string reason = 2;
        optional string detail = 3;
    }
    oneof value {
        OkPayload ok = 1;
        ErrorPayload error = 2;
        ExecutionFailure execution_failure = 4;
    }
}

message GetStatusResponse {
    oneof message {
        // First message in the stream
        ExecutionSummary summary = 1;
        // Remaining messages
        ExecutionStatus current_status = 2;
        // Finished status, sent only if `send_finished_status` is set.
        FinishedStatus finished_status = 3;
    }
}

message ExecutionEvent {

    message Created {
        FunctionName function_name = 1;
        google.protobuf.Any params = 2;
        google.protobuf.Timestamp scheduled_at = 3;
        ComponentId component_id = 4;
        optional ExecutionId scheduled_by = 5;
        DeploymentId deployment_id = 6;
    }

    message Locked {
        ComponentId component_id = 1;
        DeploymentId deployment_id = 2;
        google.protobuf.Timestamp lock_expires_at = 3;
        string run_id = 4;
    }

    message Unlocked {
        google.protobuf.Timestamp backoff_expires_at = 1;
        string reason = 2;
    }

    message TemporarilyFailed {
        string reason = 1;
        optional string detail = 2;
        google.protobuf.Timestamp backoff_expires_at = 3;
        repeated HttpClientTrace http_client_traces = 4;
    }

    message TemporarilyTimedOut {
        google.protobuf.Timestamp backoff_expires_at = 1;
        repeated HttpClientTrace http_client_traces = 2;
    }

    message Finished {
        SupportedFunctionResult value = 1;
        repeated HttpClientTrace http_client_traces = 2;
    }

    message Paused {
    }

    message Unpaused {
    }

    message HistoryEvent {

        message Persist {
            message PersistKind {
                message RandomString {
                }
                message RandomU64 {
                }
                message ExecutionId {
                }
                oneof variant {
                    RandomString random_string = 1;
                    RandomU64 random_u64 = 2;
                    ExecutionId execution_id = 3;
                }
            }
            google.protobuf.Any data = 1;
            PersistKind kind = 2;
        }

        message JoinSetCreated {
            JoinSetId join_set_id = 1;
        }

        message JoinSetRequest {

            message DelayRequest {
                DelayId delay_id = 1;
                google.protobuf.Timestamp expires_at = 2;
            }

            message ChildExecutionRequest {
                ExecutionId child_execution_id = 1;
                bool success = 2;  // false if function not found or params parsing error
            }

            JoinSetId join_set_id = 1;
            oneof join_set_request {
                DelayRequest delay_request = 2;
                ChildExecutionRequest child_execution_request = 3;
            }
        }

        message JoinNext {
            JoinSetId join_set_id = 1;
            google.protobuf.Timestamp run_expires_at = 2;
            bool closing = 3;
        }

        message JoinNextTooMany {
            JoinSetId join_set_id = 1;
            optional FunctionName function = 2;
        }

        message JoinNextTry {
            JoinSetId join_set_id = 1;

            enum Outcome {
                FOUND = 0;
                PENDING = 1;
                ALL_PROCESSED = 2;
            }
            Outcome outcome = 3;
        }

        message Schedule {
            message ScheduledAt {
                message Now {}

                message At {
                    google.protobuf.Timestamp at = 1;
                }

                message In {
                    google.protobuf.Duration in = 1;
                }

                oneof variant {
                    Now now = 1;
                    At at = 2;
                    In in = 3;
                }
            }
            ExecutionId execution_id = 1;
            ScheduledAt scheduled_at = 2;
            bool success = 3;  // false if function not found or params parsing error
        }

        message Stub {
            ExecutionId execution_id = 1;
            bool success = 2;
        }

        oneof event {
            Persist persist = 1;
            JoinSetCreated join_set_created = 2;
            JoinSetRequest join_set_request = 3;
            JoinNext join_next = 4;
            JoinNextTooMany join_next_too_many = 7;
            JoinNextTry join_next_try = 8;
            Schedule schedule = 5;
            Stub stub = 6;
        }
    }

    google.protobuf.Timestamp created_at = 1;
    uint32 version = 2;

    oneof event {
        Created created = 3;
        Locked locked = 4;
        Unlocked unlocked = 5;
        TemporarilyFailed temporarily_failed = 6;
        TemporarilyTimedOut temporarily_timed_out = 7;
        Finished finished = 8;
        HistoryEvent history_variant = 9;
        Paused paused = 11;
        Unpaused unpaused = 12;
    }

    optional uint32 backtrace_id = 10;
}

message HttpClientTrace {
    google.protobuf.Timestamp sent_at = 1;
    string uri = 2;
    string method = 3;
    optional google.protobuf.Timestamp finished_at = 4;
    // present iif finished_at is sent.
    oneof result {
        uint32 status = 5;
        string error = 6;
    }
}

message ListExecutionEventsRequest {
    ExecutionId execution_id = 1;
    uint32 version_from = 2; // 0 to request the first page
    uint32 length = 3;
    bool include_backtrace_id = 4; // return version_min_including if a matching backtrace is found
}
message ListExecutionEventsResponse {
    repeated ExecutionEvent events = 2;
    uint32 max_version = 3;
}

message JoinSetResponseEvent {
    message DelayFinished {
        DelayId delay_id = 1;
        bool success = 2; // Set to `false` if delay was cancelled.
    }

    message ChildExecutionFinished {
        ExecutionId child_execution_id = 1;
        SupportedFunctionResult value = 2;
    }

    google.protobuf.Timestamp created_at = 1;
    JoinSetId join_set_id = 2;
    oneof response {
        DelayFinished delay_finished = 3;
        ChildExecutionFinished child_execution_finished = 4;
    }
}

message ResponseWithCursor {
    JoinSetResponseEvent event = 1;
    uint32 cursor = 2;
}

message ListResponsesRequest {
    ExecutionId execution_id = 1;
    uint32 cursor_from = 2; // 0 to request the first page
    uint32 length = 3;
    bool including_cursor = 4; // true to request the first page
}
message ListResponsesResponse {
    repeated ResponseWithCursor responses = 1;
    uint32 max_cursor = 2;
}

message ListExecutionEventsAndResponsesRequest {
    ExecutionId execution_id = 1;
    uint32 version_from = 2; // 0 to request the first page of execution events
    uint32 events_length = 3;
    uint32 responses_cursor_from = 4; // 0 to request the first page of responses
    uint32 responses_length = 5;
    bool responses_including_cursor = 6; // true to request the first page of responses
    bool include_backtrace_id = 7; // return version_min_including if a matching backtrace is found
}
message ListExecutionEventsAndResponsesResponse {
    repeated ExecutionEvent events = 1;
    repeated ResponseWithCursor responses = 2;
    ExecutionStatus current_status = 3;
    uint32 max_version = 4;
    uint32 max_cursor = 5;
}

message GetWitRequest {
    ContentDigest component_digest = 1;
}

message GetWitResponse {
    optional string content = 1;
}

message GetBacktraceRequest {
    message First {}
    message Last {}
    message Specific {
        uint32 version = 1;
    }

    ExecutionId execution_id = 1;
    oneof filter {
        First first = 2;
        Last last = 3;
        Specific specific = 4;
    }
}

message GetBacktraceResponse {
    ComponentId component_id = 1;
    WasmBacktrace wasm_backtrace = 2;
}

message WasmBacktrace {
    repeated FrameInfo frames = 1;
    uint32 version_min_including = 2;
    uint32 version_max_excluding = 3;
}

message FrameInfo {
  string module = 1;
  string func_name = 2;
  repeated FrameSymbol symbols = 3;
}

message FrameSymbol {
  optional string func_name = 1;
  optional string file = 2;
  optional uint32 line = 3;
  optional uint32 col = 4;
}

message GetBacktraceSourceRequest {
    ComponentId component_id = 1;
    string file = 2; // As appears in FrameSymbol
}

message GetBacktraceSourceResponse {
    string content = 1;
}

message CancelRequest {
    message CancelRequestActivity {
        ExecutionId execution_id = 1;
    }
    message CancelRequestDelay {
        DelayId delay_id = 1;
    }
    oneof request {
        CancelRequestActivity activity = 1;
        CancelRequestDelay delay = 2;
    }
}

message CancelResponse {
    enum CancelOutcome {
        CANCEL_OUTCOME_UNSPECIFIED = 0;
        // Cancel successfully requested, activity termination is best effort.
        // The activity may still be running.
        CANCEL_OUTCOME_CANCELLED = 1;
        // Execution or delay was already marked as finished, and not cancelled.
        CANCEL_OUTCOME_ALREADY_FINISHED = 2;
    }
    CancelOutcome outcome = 1;
}

message ReplayExecutionRequest {
  ExecutionId execution_id = 1;
}
message ReplayExecutionResponse {
}


message UpgradeExecutionComponentRequest {
  ExecutionId execution_id = 1;
  ContentDigest expected_component_digest = 2; // old
  ContentDigest new_component_digest = 3;      // new
  bool skip_determinism_check = 4;
}
message UpgradeExecutionComponentResponse {
}

// Logging

enum LogLevel {
    LOG_LEVEL_UNSPECIFIED = 0;
    LOG_LEVEL_TRACE = 1;
    LOG_LEVEL_DEBUG = 2;
    LOG_LEVEL_INFO = 3;
    LOG_LEVEL_WARN = 4;
    LOG_LEVEL_ERROR = 5;
}

enum LogStreamType {
    LOG_STREAM_TYPE_UNSPECIFIED = 0;
    LOG_STREAM_TYPE_STDOUT = 1;
    LOG_STREAM_TYPE_STDERR = 2;
}

message ListLogsRequest {
    ExecutionId execution_id = 1;
    int32 page_size = 2;
    string page_token = 3;
    bool show_logs = 4;
    bool show_streams = 5;
    repeated LogLevel levels = 6; // Only applied if show_logs = true, empty means return all levels.
    repeated LogStreamType stream_types = 7; // Only applied if show_streams = true, empty means return all stream types.
}

message ListLogsResponse {

    message LogEntry {
        google.protobuf.Timestamp created_at = 1;

        message LogVariant {
            LogLevel level = 1;
            string message = 2;
        }

        message StreamVariant {
            bytes payload = 1;
            LogStreamType stream_type = 2;
        }

        oneof entry {
            LogVariant log = 2;
            StreamVariant stream = 3;
        }

        RunId run_id = 4;
    }

    repeated LogEntry logs = 1;
    string next_page_token = 2;
    optional string prev_page_token = 3;
}

message ListDeploymentsRequest {
    message NewerThan {
        uint32 length = 1;
        optional DeploymentId cursor = 2;
        bool including_cursor = 3;
    }
    message OlderThan {
        uint32 length = 1;
        optional DeploymentId cursor = 2;
        bool including_cursor = 3;
    }

    oneof pagination { // latest if not set
        NewerThan newer_than = 1;
        OlderThan older_than = 2;
    };
    bool include_config_json = 3;
}

enum DeploymentStatus {
    DEPLOYMENT_STATUS_UNSPECIFIED = 0;
    DEPLOYMENT_STATUS_INACTIVE = 1;
    DEPLOYMENT_STATUS_ENQUEUED = 2;
    DEPLOYMENT_STATUS_ACTIVE = 3;
}

message Deployment {
    DeploymentId deployment_id = 1;
    DeploymentStatus status = 2;
    google.protobuf.Timestamp created_at = 3;
    optional google.protobuf.Timestamp last_active_at = 4;
    optional string config_json = 5;
}

message DeploymentSummary {
    Deployment deployment = 1;
    uint32 locked = 2;
    uint32 pending = 3;
    uint32 scheduled = 4;
    uint32 blocked = 5;
    uint32 finished = 6;
}

message ListDeploymentsResponse {
    repeated DeploymentSummary deployments = 1;
}

message GetCurrentDeploymentIdRequest {
}
message GetCurrentDeploymentIdResponse {
    DeploymentId deployment_id = 1;
}

message SwitchDeploymentRequest {
    DeploymentId deployment_id = 1;
    // Run `obelisk server verify` on the deployment config before switching.
    bool verify = 2;
    // Perform a hot redeployment (reload running components without restart).
    bool hot_redeploy = 3;
}
message SwitchDeploymentResponse {
    enum Outcome {
        SWITCH_OUTCOME_UNSPECIFIED = 0;
        // The server performed a hot redeploy; the new deployment is now live.
        SWITCH_OUTCOME_SWITCHED = 1;
        // The database was updated; the server must be restarted to apply the new deployment.
        SWITCH_OUTCOME_RESTART_REQUIRED = 2;
    }
    Outcome outcome = 1;
}

message SubmitDeploymentRequest {
    string config_json = 1;
    optional string created_by = 2;
    // Verify all environment variables before persisting the deployment.
    bool verify = 3;
}
message SubmitDeploymentResponse {
    DeploymentId deployment_id = 1;
}

message GetDeploymentRequest {
    DeploymentId deployment_id = 1;
}
message GetDeploymentResponse {
    Deployment deployment = 1;
}


message PauseExecutionRequest {
    ExecutionId execution_id = 1;
}
message PauseExecutionResponse {
}


message UnpauseExecutionRequest {
    ExecutionId execution_id = 1;
}
message UnpauseExecutionResponse {
}
