syntax = "proto3";

package enginepb;

import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/empty.proto";

import "engine/proto/resources.proto";

option go_package = "github.com/pingcap/tiflow/engine/enginepb";

service Discovery {
    rpc RegisterExecutor(RegisterExecutorRequest) returns(Executor) {
        option (google.api.http) = {
            post: "/api/v1/executors/register"
            body: "executor"
        };
    }

    // ListExecutors lists all executors.
    // Executors will use this API to discover other executors.
    // Currently, we assume that there aren't too many executors.
    // If the number of executors becomes very large in the future,
    // we can consider a mechanism to watch the changes of the executors.
    rpc ListExecutors(ListExecutorsRequest) returns(ListExecutorsResponse) {
        option (google.api.http) = {
            get: "/api/v1/executors"
        };
    }

    rpc ListMasters(ListMastersRequest) returns(ListMastersResponse) {
        option (google.api.http) = {
            get: "/api/v1/masters"
        };
    }

    rpc Heartbeat(HeartbeatRequest) returns(HeartbeatResponse) {}


    // QueryMetaStore queries metastore manager and returns the information of a matching metastore.
    rpc QueryMetaStore(QueryMetaStoreRequest) returns(QueryMetaStoreResponse) {}
    
    // QueryStorageConfig queries and returns external storage config.
    rpc QueryStorageConfig(QueryStorageConfigRequest) returns(QueryStorageConfigResponse) {}

    rpc GetLeader(GetLeaderRequest) returns(GetLeaderResponse) {
        option (google.api.http) = {
            get: "/api/v1/leader"
        };
    }

    rpc ResignLeader(ResignLeaderRequest) returns(google.protobuf.Empty) {
        option (google.api.http) = {
            post: "/api/v1/leader/resign"
        };
    }
}

service TaskScheduler {
    rpc ScheduleTask(ScheduleTaskRequest) returns(ScheduleTaskResponse) {}
}

// Refer to: https://cloud.google.com/apis/design/standard_methods 
service JobManager {
    rpc CreateJob(CreateJobRequest) returns (Job){
        option (google.api.http) = {
            post: "/api/v1/jobs"
            body: "job"
        };
    };

    rpc GetJob(GetJobRequest) returns (Job){
        option (google.api.http) = {
            get: "/api/v1/jobs/{id=*}"
        };
    };

    rpc ListJobs(ListJobsRequest) returns (ListJobsResponse){
        option (google.api.http) = {
            get: "/api/v1/jobs"
        };
    };

    // NOTE: for the compatibility of existing openapi(ticdc) format,
    // we use `/cancel` but not `:cancel`(google api suggested)
    // refer to: https://cloud.google.com/apis/design/custom_methods
    rpc CancelJob(CancelJobRequest) returns (Job){
        option (google.api.http) = {
            post: "/api/v1/jobs/{id=*}/cancel"
        };
    };

    rpc DeleteJob(DeleteJobRequest) returns (google.protobuf.Empty){
        option (google.api.http) = {
            delete: "/api/v1/jobs/{id=*}"
        };
    }
}

message Selector {
    enum Op {
        OpUnknown = 0;
        Eq = 1;
        Neq = 2;
        Regex = 3;
    }

    string label = 1;
    string target = 2;
    Op op = 3;
}

message HeartbeatRequest {
    string executor_id = 1;
    uint64 timestamp = 2;
    uint64 ttl = 3;
}

message HeartbeatResponse {
}

message Executor {
    string id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    // name is the readable name of the executor.
    string name = 2;
    string address = 3;
    map<string, string> labels = 4;
}

message RegisterExecutorRequest {
    Executor executor = 1;
}

message ListExecutorsRequest {
}

message ListExecutorsResponse {
    repeated Executor executors = 1;
}

message Master {
    string id = 1;
    // name is the readable name of the master.
    string name = 2;
    string address = 3;
    bool is_leader = 4;
}

message ListMastersRequest {
}

message ListMastersResponse {
    repeated Master masters = 1;
}


message ScheduleTaskRequest {
    string task_id = 1;
    // resources required by the task.
    repeated ResourceKey resources = 2;
    repeated Selector selectors = 3;
}

message ScheduleTaskResponse {
    string executor_id = 1;
    string executor_addr = 2;
}

message GetLeaderRequest {
}

message GetLeaderResponse {
    string advertise_addr = 1;
}

message ResignLeaderRequest {
}

message Job {
    enum Type {
        TypeUnknown = 0;
        CVSDemo = 1;
        DM = 2;
        CDC = 3;
        FakeJob = 4;
    }

    enum State{
        StateUnknown = 0;
        Created = 1;
        Running = 2;
        Failed = 3;
        Finished = 4;
        Canceling = 5;
        Canceled = 6;
    }

    message Error {
        string code = 1;
        string message = 2;
    }

    string id = 1;
    Type type = 2;
    State state = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
    // Output will ignore this field by default unless include_config is set to true.
    bytes config = 4;
    bytes detail = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
    Error error = 6 [(google.api.field_behavior) = OUTPUT_ONLY];
    repeated Selector selectors = 7;
}

message CreateJobRequest {
    Job job = 1;
    string tenant_id = 2;
    string project_id = 3;
}

message GetJobRequest {
    string id = 1;
    string tenant_id = 2;
    string project_id = 3;
    // Whether to return the config of the job.
    // Config may contain sensitive information, it is not returned by default.
    bool include_config = 4;
}

message ListJobsRequest {
    // The maximum number of jobs to return.
    // If it is unspecified or less than 1, at most 100 jobs will be returned.
    // The maximum value is 1000. Larger values will be coerced to 1000.
    int32 page_size = 1;
    // The page token, returned by a previous call, to request the next page of results.
    string page_token = 2;
    string tenant_id = 3;
    string project_id = 4;
    // Whether to return the config of the job.
    // Config may contain sensitive information, it is not returned by default.
    bool include_config = 5;
    Job.Type type = 6;
    Job.State state = 7;
}

message ListJobsResponse {
    repeated Job jobs = 1;
    // A token to retrieve next page of results.
    // If this field is empty, it means no more pages.
    string next_page_token = 2;
}

message CancelJobRequest {
    string id = 1;
    string tenant_id = 2;
    string project_id = 3;
}

message DeleteJobRequest {
    string id = 1;
    string tenant_id = 2;
    string project_id = 3;
}

enum StoreType {
    SystemMetaStore = 0;
    AppMetaStore = 1;
}

message QueryMetaStoreRequest {
    StoreType tp = 1;
}

message QueryMetaStoreResponse {
    bytes config = 1;
}

message QueryStorageConfigRequest {
}

message QueryStorageConfigResponse {
    bytes config = 2;
}
