//------------------------------------------------------------------------------
// Copyright (c) 2020-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//------------------------------------------------------------------------------

syntax = "proto3";

option csharp_namespace = "Emqx.Exhook.V2";
option go_package = "emqx.io/grpc/exhook";
option java_multiple_files = true;
option java_package = "io.emqx.exhook";
option java_outer_classname = "EmqxExHookProto";

// The exhook proto version should be fixed as `v2` in EMQX v5.x
// to make sure the exhook proto version is compatible
package emqx.exhook.v2;

service HookProvider {

  rpc OnProviderLoaded(ProviderLoadedRequest) returns (LoadedResponse) {};

  rpc OnProviderUnloaded(ProviderUnloadedRequest) returns (EmptySuccess) {};

  rpc OnClientConnect(ClientConnectRequest) returns (EmptySuccess) {};

  rpc OnClientConnack(ClientConnackRequest) returns (EmptySuccess) {};

  rpc OnClientConnected(ClientConnectedRequest) returns (EmptySuccess) {};

  rpc OnClientDisconnected(ClientDisconnectedRequest) returns (EmptySuccess) {};

  rpc OnClientAuthenticate(ClientAuthenticateRequest) returns (ValuedResponse) {};

  rpc OnClientAuthorize(ClientAuthorizeRequest) returns (ValuedResponse) {};

  rpc OnClientSubscribe(ClientSubscribeRequest) returns (EmptySuccess) {};

  rpc OnClientUnsubscribe(ClientUnsubscribeRequest) returns (EmptySuccess) {};

  rpc OnSessionCreated(SessionCreatedRequest) returns (EmptySuccess) {};

  rpc OnSessionSubscribed(SessionSubscribedRequest) returns (EmptySuccess) {};

  rpc OnSessionUnsubscribed(SessionUnsubscribedRequest) returns (EmptySuccess) {};

  rpc OnSessionResumed(SessionResumedRequest) returns (EmptySuccess) {};

  rpc OnSessionDiscarded(SessionDiscardedRequest) returns (EmptySuccess) {};

  rpc OnSessionTakenover(SessionTakenoverRequest) returns (EmptySuccess) {};

  rpc OnSessionTerminated(SessionTerminatedRequest) returns (EmptySuccess) {};

  rpc OnMessagePublish(MessagePublishRequest) returns (ValuedResponse) {};

  rpc OnMessageDelivered(MessageDeliveredRequest) returns (EmptySuccess) {};

  rpc OnMessageDropped(MessageDroppedRequest) returns (EmptySuccess) {};

  rpc OnMessageAcked(MessageAckedRequest) returns (EmptySuccess) {};
}

//------------------------------------------------------------------------------
// Request
//------------------------------------------------------------------------------

message ProviderLoadedRequest {

  BrokerInfo broker = 1;

  RequestMeta meta = 2;
}

message ProviderUnloadedRequest {

  RequestMeta meta = 1;
}

message ClientConnectRequest {

  ConnInfo conninfo = 1;

  // MQTT CONNECT packet's properties (MQTT v5.0)
  //
  // It should be empty on MQTT v3.1.1/v3.1 or others protocol
  repeated Property props = 2;

  RequestMeta meta = 3;
}

message ClientConnackRequest {

  ConnInfo conninfo = 1;

  string result_code = 2;

  repeated Property props = 3;

  RequestMeta meta = 4;
}

message ClientConnectedRequest {

  ClientInfo clientinfo = 1;

  RequestMeta meta = 2;
}

message ClientDisconnectedRequest {

  ClientInfo clientinfo = 1;

  string reason = 2;

  RequestMeta meta = 3;
}

message ClientAuthenticateRequest {

  ClientInfo clientinfo = 1;

  bool result = 2;

  RequestMeta meta = 3;
}

message ClientAuthorizeRequest {

  ClientInfo clientinfo = 1;

  enum AuthorizeReqType {

    PUBLISH = 0;

    SUBSCRIBE = 1;
  }

  AuthorizeReqType type = 2;

  string topic = 3;

  bool result = 4;

  RequestMeta meta = 5;
}

message ClientSubscribeRequest {

  ClientInfo clientinfo = 1;

  repeated Property props = 2;

  repeated TopicFilter topic_filters = 3;

  RequestMeta meta = 4;
}

message ClientUnsubscribeRequest {

  ClientInfo clientinfo = 1;

  repeated Property props = 2;

  repeated TopicFilter topic_filters = 3;

  RequestMeta meta = 4;
}

message SessionCreatedRequest {

  ClientInfo clientinfo = 1;

  RequestMeta meta = 2;
}

message SessionSubscribedRequest {

  ClientInfo clientinfo = 1;

  string topic = 2;

  SubOpts subopts = 3;

  RequestMeta meta = 4;
}

message SessionUnsubscribedRequest {

  ClientInfo clientinfo = 1;

  string topic = 2;

  RequestMeta meta = 3;
}

message SessionResumedRequest {

  ClientInfo clientinfo = 1;

  RequestMeta meta = 2;
}

message SessionDiscardedRequest {

  ClientInfo clientinfo = 1;

  RequestMeta meta = 2;
}

message SessionTakenoverRequest {

  ClientInfo clientinfo = 1;

  RequestMeta meta = 2;
}

message SessionTerminatedRequest {

  ClientInfo clientinfo = 1;

  string reason = 2;

  RequestMeta meta = 3;
}

message MessagePublishRequest {

  Message message = 1;

  RequestMeta meta = 2;
}

message MessageDeliveredRequest {

  ClientInfo clientinfo = 1;

  Message message = 2;

  RequestMeta meta = 3;
}

message MessageDroppedRequest {

  Message message = 1;

  string reason = 2;

  RequestMeta meta = 3;
}

message MessageAckedRequest {

  ClientInfo clientinfo = 1;

  Message message = 2;

  RequestMeta meta = 3;
}

//------------------------------------------------------------------------------
// Response
//------------------------------------------------------------------------------

// Responsed by `ProviderLoadedRequest`

message LoadedResponse {

  repeated HookSpec hooks = 1;
}

// Responsed by `ClientAuthenticateRequest` `ClientAuthorizeRequest` `MessagePublishRequest`

message ValuedResponse {

  // The responded value type
  //  - contiune: Use the responded value and execute the next hook
  //  - ignore: Ignore the responded value
  //  - stop_and_return: Use the responded value and stop the chain executing
  enum ResponsedType {

    CONTINUE = 0;

    IGNORE = 1;

    STOP_AND_RETURN = 2;
  }

  ResponsedType type = 1;

  oneof value {

    // Boolean result, used on the 'client.authenticate', 'client.authorize' hooks
    bool bool_result = 3;

    // Message result, used on the 'message.*' hooks
    Message message = 4;
  }
}

// no Response by other Requests

message EmptySuccess { }

//------------------------------------------------------------------------------
// Basic data types
//------------------------------------------------------------------------------

message BrokerInfo {

  string version = 1;

  string sysdescr = 2;

  int64 uptime = 3;

  string datetime = 4;
}


message HookSpec {

  // The registered hooks name
  //
  // Available value:
  //   "client.connect",      "client.connack"
  //   "client.connected",    "client.disconnected"
  //   "client.authenticate", "client.authorize"
  //   "client.subscribe",    "client.unsubscribe"
  //
  //   "session.created",      "session.subscribed"
  //   "session.unsubscribed", "session.resumed"
  //   "session.discarded",    "session.takenover"
  //   "session.terminated"
  //
  //   "message.publish", "message.delivered"
  //   "message.acked",   "message.dropped"
  string name = 1;

  // The topic filters for message hooks
  repeated string topics = 2;
}

message ConnInfo {

  string node = 1;

  string clientid = 2;

  string username = 3;

  string peerhost = 4;

  uint32 sockport = 5;

  string proto_name = 6;

  string proto_ver = 7;

  uint32 keepalive = 8;
}

message ClientInfo {

  string node = 1;

  string clientid = 2;

  string username = 3;

  string password = 4;

  string peerhost = 5;

  uint32 sockport = 6;

  string protocol = 7;

  string mountpoint = 8;

  bool  is_superuser = 9;

  bool  anonymous = 10;

  // common name of client TLS cert
  string cn = 11;

  // subject of client TLS cert
  string dn = 12;
}

message Message {

  string node = 1;

  string id = 2;

  uint32 qos = 3;

  string from = 4;

  string topic = 5;

  bytes  payload = 6;

  uint64 timestamp = 7;

  // The key of header can be:
  //  - username:
  //    * Readonly
  //    * The username of sender client
  //    * Value type: utf8 string
  //  - protocol:
  //    * Readonly
  //    * The protocol name of sender client
  //    * Value type: string enum with "mqtt", "mqtt-sn", ...
  //  - peerhost:
  //    * Readonly
  //    * The peerhost of sender client
  //    * Value type: ip address string
  //  - allow_publish:
  //    * Writable
  //    * Whether to allow the message to be published by emqx
  //    * Value type: string enum with "true", "false", default is "true"
  //
  // Notes: All header may be missing, which means that the message does not
  //   carry these headers. We can guarantee that clients coming from MQTT,
  //   MQTT-SN, CoAP, LwM2M and other natively supported protocol clients will
  //   carry these headers, but there is no guarantee that messages published
  //   by other means will do, e.g. messages published by HTTP-API
  map<string, string> headers = 8;
}

message Property {

  string name = 1;

  string value = 2;
}

message TopicFilter {

  string name = 1;

  uint32 qos = 2;
}

message SubOpts {

  // The QoS level
  uint32 qos = 1;

  // The group name for shared subscription
  string share = 2;

  // The Retain Handling option (MQTT v5.0)
  //
  //  0 = Send retained messages at the time of the subscribe
  //  1 = Send retained messages at subscribe only if the subscription does
  //       not currently exist
  //  2 = Do not send retained messages at the time of the subscribe
  uint32 rh = 3;

  // The Retain as Published option (MQTT v5.0)
  //
  //  If 1, Application Messages forwarded using this subscription keep the
  //        RETAIN flag they were published with.
  //  If 0, Application Messages forwarded using this subscription have the
  //        RETAIN flag set to 0.
  // Retained messages sent when the subscription is established have the RETAIN flag set to 1.
  uint32 rap = 4;

  // The No Local option (MQTT v5.0)
  //
  // If the value is 1, Application Messages MUST NOT be forwarded to a
  // connection with a ClientID equal to the ClientID of the publishing
  uint32 nl = 5;
}

message RequestMeta {

  string node = 1;

  string version = 2;

  string sysdescr = 3;

  string cluster_name = 4;
}
