syntax = "proto3";

package nominal.data_connector.v1;

import "buf/validate/validate.proto";
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "nominal/protos/data_connector/v1/opc_ua_connector.proto";
import "nominal/protos/gen/v1/alias.proto";
import "nominal/protos/gen/v1/error.proto";

option java_multiple_files = true;
option java_package = "io.nominal.data_connector.v1";

// Request to create a new data connector
message CreateDataConnectorRequest {
  string name = 1 [(buf.validate.field).required = true];

  optional string description = 2;

  DataConnectorDetails connection_details = 3 [(buf.validate.field).required = true];

  string workspace_rid = 4 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.WorkspaceRid"}
  ];

  string target_dataset_rid = 5 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DatasetRid"}
  ];
}

// Response for creating a new data connector
message CreateDataConnectorResponse {
  DataConnector data_connector = 1;
}

// Error types for data connector operations
enum DataConnectorErrorType {
  DATA_CONNECTOR_ERROR_TYPE_UNSPECIFIED = 0;
  DATA_CONNECTOR_ERROR_TYPE_CONNECTOR_NOT_FOUND = 1 [(nominal.gen.v1.error) = {
    code: "NOT_FOUND"
    message: "Data connector not found"
  }];
  DATA_CONNECTOR_ERROR_TYPE_CONNECTOR_NOT_AUTHORIZED = 2 [(nominal.gen.v1.error) = {
    code: "PERMISSION_DENIED"
    message: "Data connector not found"
  }];
  DATA_CONNECTOR_ERROR_TYPE_TARGET_DATASET_NOT_IN_WORKSPACE = 3 [(nominal.gen.v1.error) = {
    code: "INVALID_ARGUMENT"
    message: "Data connector not found"
  }];
}

// A configured data connector
message DataConnector {
  string data_connector_rid = 1 [(nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DataConnectorRid"}];

  string name = 2;

  optional string description = 3;

  DataConnectorDetailsSecrets connection_details = 4;

  string workspace_rid = 5 [(nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.WorkspaceRid"}];

  optional string target_dataset_rid = 6 [(nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DatasetRid"}];

  google.protobuf.Timestamp created_at = 7;
}

// Union type for different data connector types
message DataConnectorDetails {
  oneof data_connector_details {
    option (buf.validate.oneof) = {required: true};

    OpcUaConnectorDetails opc_ua = 1;
  }
}

message DataConnectorDetailsUpdates {
  oneof data_connector_details_updates {
    option (buf.validate.oneof) = {required: true};

    OpcUaConnectorDetailsUpdates opc_ua = 1;
  }
}

// Union type for different data connector types with secrets
message DataConnectorDetailsSecrets {
  oneof data_connector_details {
    option (buf.validate.oneof) = {required: true};

    OpcUaConnectorDetailsSecret opc_ua = 1;
  }
}

message UpdateDataConnectorRequest {
  string data_connector_rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DataConnectorRid"}
  ];

  optional string name = 2;

  optional string description = 3;

  optional DataConnectorDetailsUpdates connection_details = 4;

  optional string target_dataset_rid = 5 [(nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DatasetRid"}];
}

message UpdateDataConnectorResponse {
  DataConnector data_connector = 1;
}

message GetDataConnectorRequest {
  string data_connector_rid = 1 [(nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DataConnectorRid"}];
}

message GetDataConnectorResponse {
  DataConnector data_connector = 1;
}

message NameFilter {
  string name = 1 [(buf.validate.field).required = true];
}

message DescriptionFilter {
  string description = 1 [(buf.validate.field).required = true];
}

message TargetDatasetFilter {
  string target_dataset_rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DatasetRid"}
  ];
}

message AndFilter {
  repeated SearchFilter clauses = 1;
}

enum SortField {
  SORT_FIELD_UNSPECIFIED = 0;
  SORT_FIELD_UPDATED_AT = 1;
}

enum SortOrder {
  SORT_ORDER_UNSPECIFIED = 0;
  SORT_ORDER_ASC = 1;
  SORT_ORDER_DESC = 2;
}

message SortBy {
  // If unspecified, defaults to SORT_FIELD_UPDATE_AT
  SortField field = 1;
  // If unspecified, defaults to SORT_ORDER_DESC
  SortOrder order = 2;
}

message SearchFilter {
  oneof filter {
    option (buf.validate.oneof) = {required: true};

    NameFilter name = 1;
    DescriptionFilter description = 2;
    TargetDatasetFilter target_dataset = 3;
    AndFilter and = 4;
  }
}

message SearchRequest {
  string workspace_rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.WorkspaceRid"}
  ];
  SearchFilter filter = 2 [(buf.validate.field).required = true];
  // If unspecified, defaults to 100
  int32 page_size = 3;
  optional string page_token = 4;
  SortBy sort_by = 5 [(buf.validate.field).required = true];
}

message SearchResponse {
  repeated DataConnector data_connectors = 1;
  optional string page_token = 2;
}

message StartSessionRequest {
  string data_connector_rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DataConnectorRid"}
  ];
}

message StartSessionResponse {}

message StopSessionRequest {
  string data_connector_rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DataConnectorRid"}
  ];
}

message StopSessionResponse {}

message GetSessionStatusRequest {
  string data_connector_rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DataConnectorRid"}
  ];
}

enum ReplicaStatus {
  REPLICA_STATUS_UNSPECIFIED = 0;
  REPLICA_STATUS_STARTING = 1;
  REPLICA_STATUS_SUBSCRIBING = 2;
  REPLICA_STATUS_RUNNING = 3;
  REPLICA_STATUS_STOPPING = 4;
  REPLICA_STATUS_STOPPED = 5;
}

message ActiveSessionStatus {
  repeated ReplicaStatus replica_status = 1;
}

message GetSessionStatusResponse {
  oneof status {
    google.protobuf.Empty none = 1;
    ActiveSessionStatus active = 2;
  }
}

message DeleteDataConnectorRequest {
  string data_connector_rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.DataConnectorRid"}
  ];
}

message DeleteDataConnectorResponse {}

// Service for managing data connectors to external data sources.
// Supports OPC-UA with plans for MQTT, Kafka, and other protocols.
service DataConnectorService {
  // Creates a new data connector configuration
  rpc CreateDataConnector(CreateDataConnectorRequest) returns (CreateDataConnectorResponse) {
    option (google.api.http) = {
      post: "/data-connector/v1/connector"
      body: "*"
    };
  }

  // Gets the settings for a data connector
  rpc GetDataConnector(GetDataConnectorRequest) returns (GetDataConnectorResponse) {
    option (google.api.http) = {
      get: "/data-connector/v1/{data_connector_rid}"
      response_body: "*"
    };
  }

  // Updates the settings for a data connector
  rpc UpdateDataConnector(UpdateDataConnectorRequest) returns (UpdateDataConnectorResponse) {
    option (google.api.http) = {
      post: "/data-connector/v1/{data_connector_rid}"
      response_body: "*"
    };
  }

  // List the data connectors matching the search criteria
  rpc Search(SearchRequest) returns (SearchResponse) {
    option (google.api.http) = {
      get: "/data-connector/v1/search"
      response_body: "*"
    };
  }

  // Starts a session for a connection
  rpc StartSession(StartSessionRequest) returns (StartSessionResponse) {
    option (google.api.http) = {
      post: "/data-connector/v1/{data_connector_rid}/session/start"
      response_body: "*"
    };
  }

  // Stops a session for a connection
  rpc StopSession(StopSessionRequest) returns (StopSessionResponse) {
    option (google.api.http) = {
      post: "/data-connector/v1/{data_connector_rid}/session/stop"
      response_body: "*"
    };
  }

  // Gets the session status for a connection
  rpc GetSessionStatus(GetSessionStatusRequest) returns (GetSessionStatusResponse) {
    option (google.api.http) = {
      get: "/data-connector/v1/{data_connector_rid}/session"
      response_body: "*"
    };
  }

  // Deletes a data connector. This operation is idempotent.
  rpc DeleteDataConnector(DeleteDataConnectorRequest) returns (DeleteDataConnectorResponse) {
    option (google.api.http) = {
      delete: "/data-connector/v1/{data_connector_rid}"
      response_body: "*"
    };
  }
}
