syntax ="proto3";

package gmqtt.federation.api;
option go_package = ".;federation";

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

message Event {
    uint64 id = 1;
    oneof Event {
        Subscribe Subscribe = 2;
        Message message = 3;
        Unsubscribe unsubscribe = 4;
    }
}

// Subscribe represents the subscription for a node, it is used to route message among nodes,
// so only shared_name and topic_filter is required.
message Subscribe {
    string share_name = 1;
    string topic_filter = 2;
}

message Message{
    string topic_name = 1;
    bytes  payload = 2;
    uint32 qos = 3;
    bool retained = 4;
    // the following fields are using in v5 client.
    string content_type = 5;
    string correlation_data = 6;
    uint32 message_expiry = 7;
    uint32 payload_format = 8;
    string response_topic = 9;
    repeated UserProperty user_properties = 10;
}

message UserProperty {
    bytes K = 1;
    bytes V = 2;
}
message Unsubscribe{
    string topic_name = 1;
}

message Ack {
    uint64 event_id = 1;
}

// ClientHello is the request message in handshake process.
message ClientHello {
    string session_id =1;
}

// ServerHello is the response message in handshake process.
message ServerHello{
    bool clean_start = 1;
    uint64 next_event_id = 2;
}

message JoinRequest {
    repeated string hosts = 1;
}


message Member {
    string name = 1;
    string addr = 2;
    map<string,string> tags = 3;
    Status status = 4;
}

enum Status {
    STATUS_UNSPECIFIED = 0;
    STATUS_ALIVE = 1;
    STATUS_LEAVING=2;
    STATUS_LEFT = 3;
    STATUS_FAILED = 4;
}

message ListMembersResponse {
    repeated Member members = 1;
}

message ForceLeaveRequest {
    string node_name = 1;
}

service Membership {
    // Join tells the local node to join the an existing cluster.
    // See https://www.serf.io/docs/commands/join.html for details.
    rpc Join(JoinRequest) returns (google.protobuf.Empty){
        option (google.api.http) = {
            post: "/v1/federation/join"
            body:"*"
        };
    }
    // Leave triggers a graceful leave for the local node.
    // This is used to ensure other nodes see the node as "left" instead of "failed".
    // Note that a leaved node cannot re-join the cluster unless you restart the leaved node.
    rpc Leave(google.protobuf.Empty) returns (google.protobuf.Empty){
        option (google.api.http) = {
            post: "/v1/federation/leave"
            body:"*"
        };
    }
    // ForceLeave force forces a member of a Serf cluster to enter the "left" state.
    // Note that if the member is still actually alive, it will eventually rejoin the cluster.
    // The true purpose of this method is to force remove "failed" nodes
    // See https://www.serf.io/docs/commands/force-leave.html for details.
    rpc ForceLeave(ForceLeaveRequest) returns (google.protobuf.Empty){
        option (google.api.http) = {
            post: "/v1/federation/force_leave"
            body:"*"
        };
    }
    // ListMembers lists all known members in the Serf cluster.
    rpc ListMembers(google.protobuf.Empty) returns (ListMembersResponse){
        option (google.api.http) = {
            get: "/v1/federation/members"
        };
    }
}

service Federation {
    rpc Hello(ClientHello) returns (ServerHello){}
    rpc EventStream (stream Event) returns (stream Ack){}
}
