/*
  Сервис реализует функционал управления партнерами
*/
syntax = "proto3";

import "google/protobuf/timestamp.proto";
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/descriptor.proto";
import "google/protobuf/wrappers.proto";
import "keyapis/multiapp/v1/keyapis_multiapp_app_v1.proto";

package keyapis.multiapp.v1;

option java_package = "ru.keyapis.multiapp.v1";
option java_outer_classname = "KeyapisMultiappV1Proto";
option java_multiple_files = false;
option java_string_check_utf8 = true;
option go_package = "/keyapis_multiapp_v1";
option cc_enable_arenas = true;
option csharp_namespace = "Keyapis.Multiapp.V1";
option objc_class_prefix = "KEYAPISMULTIAPPV1";
option php_namespace = "Keyapis\\Multiapp\\V1";
option ruby_package = "Keyapis::Multiapp::V1";
option optimize_for = LITE_RUNTIME;

// Сервис управления партнерами  для суперапа
service PartnerService {
  // Метод сохранения партнера.
  // Поддерживает создание и обновление.
  // Сценарий использования метода: https://confluence.rt.ru/pages/viewpage.action?pageId=664695525.
  // Метод доступен для: admin, manager, service
  rpc PostPartner(PostPartnerRequest) returns (PostPartnerResponse) {
    option (google.api.http) = {
      post: "/multiapp/api/v1/partner"
      body: "*"
    };
  }
  // Метод получения партнера.
  // Метод доступен для: admin, manager, service, ltp_first
  rpc GetPartner(GetPartnerRequest) returns (GetPartnerResponse) {
    option (google.api.http) = {
      get: "/multiapp/api/v1/partner/{id}"
    };
  }
  // Метод получения списка партнеров.
  // Метод доступен для: admin, manager, service, ltp_first
  rpc GetPartnerList(GetPartnerListRequest) returns (stream GetPartnerListResponse) {
    option (google.api.http) = {
      get: "/multiapp/api/v1/partner/list"
    };
  }
  // Метод получения количества партнеров.
  // Метод доступен для: admin, manager, service, ltp_first
  rpc GetPartnerCount(GetPartnerCountRequest) returns (GetPartnerCountResponse) {
    option (google.api.http) = {
      get: "/multiapp/api/v1/partner/count"
    };
  }
  // Метод получения токена.
  // Access токен возможно получить только для пользователей с типом master в статусе active, demo и new.
  // Логика работы метода: https://confluence.rt.ru/pages/viewpage.action?pageId=664695865.
  // Не требует авторизации
  rpc GetPartnerToken(GetPartnerTokenRequest) returns (GetPartnerTokenResponse) {
    option (google.api.http) = {
      get: "/multiapp/api/v1/partner/token"
    };
  }
  // Метод обновления публичного ключа партнёра.
  // Поддерживает создание и обновление.
  // Метод доступен для: partner.
  // Требует передачи аутентификационного ключа в заголовке X-API-KEY.
  // Сценарий использования метода: https://confluence.rt.ru/pages/viewpage.action?pageId=664695525
  rpc PostPartnerPublicKey(PostPartnerPublicKeyRequest) returns (PostPartnerPublicKeyResponse) {
    option (google.api.http) = {
      post: "/multiapp/api/v1/partner/public_key"
      body: "*"
    };
  }
  // Метод удаления партнёра.
  // Сценарий использования метода: https://confluence.rt.ru/pages/viewpage.action?pageId=804302306.
  // Метод доступен для: admin, manager, service
  rpc DeletePartner(DeletePartnerRequest) returns (DeletePartnerResponse) {
    option (google.api.http) = {
      delete: "/multiapp/api/v1/partner/{id}"
    };
  }
  // Метод получения адреса дома по ОРПОНу.
  // Адресная строка должны быть в формате "{Регион}, {Город}, {Улица}, {Дом}, {Строение}".
  // Партнер может получить адрес только по доступному ему ОРПОНу.
  // Доступные ОРПОНы для партнера может получить через привязанные app в поле app_tags и user_tags.
  // Метод доступен для: admin, manager, service, partner.
  // Роль partner определяется по наличию заголовка X-API-KEY вместо Authorization
  rpc GetPartnerAddressOrpon(GetPartnerAddressOrponRequest) returns (GetPartnerAddressOrponResponse) {
    option (google.api.http) = {
      get: "/multiapp/api/v1/partner/address/orpon/{orpon}"
    };
  }
}

// Партнер.
// # Описание модели
message Partner {
  // Идентификатор.
  // Если не передан создаётся сервером.
  // # Тип: Guid
  string id = 1;
  // Наименование.
  // # Диапазон: 2..256
  string name = 2 [(google.api.field_behavior) = REQUIRED];
  // API ключ.
  // # Тип: Guid?
  google.protobuf.StringValue api_key = 3 [(google.api.field_behavior) = INPUT_ONLY];
  // Публичный ключ
  google.protobuf.StringValue public_key = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
  // Дата создания партнера.
  // # Тип: DateTime
  google.protobuf.Timestamp created_at = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
  // Дата обновления партнера.
  // # Тип: DateTime
  google.protobuf.Timestamp changed_at = 6;
  // Дата удаления партнера.
  // # Тип: DateTime?
  google.protobuf.Timestamp deleted_at = 7 [(google.api.field_behavior) = OUTPUT_ONLY];
  // Хэш от API ключа
  google.protobuf.StringValue api_key_hash = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
  // API ключ маскированный
  google.protobuf.StringValue api_key_masked = 9 [(google.api.field_behavior) = OUTPUT_ONLY];
  // Ошибка сохранения.
  // Эти проверки выполняются при работе с базой данных и сторонними сервисами
  message SavingError {
    // Конфликт версий.
    // Причины:
    // - В базе хранится другая версия строки, значения changed_at отличаются
    message Conflict {}
    // API-key уже существует.
    // Может появиться только в случае создания партнера.
    // Причины:
    // - В базе уже хранится такой api_key, он должен быть уникален для каждого партнера
    message ApiKeyExists {}
    // Партнёр удалён.
    // Причины:
    // - Переданный публичный ключ принадлежит удалённому партнеру
    message PartnerIsDeleted {}
    // Причина ошибки
    oneof reason {
      // Конфликт версий
      Conflict conflict = 1;
      // API-key уже существует
      ApiKeyExists api_key_exists = 2;
      // Партнёр удалён
      PartnerIsDeleted partner_is_deleted = 3;
    }
  }
}

// Ответ на запрос токена
message Token {
  // ID токен в формате nested JWT
  string id_token = 1 [(google.api.field_behavior) = OUTPUT_ONLY, json_name = "id_token"];
  // Авторизационный токен
  string access_token = 2 [(google.api.field_behavior) = OUTPUT_ONLY, json_name = "access_token"];
  // Тип авторизационного токена
  string type_of_access_token = 3 [(google.api.field_behavior) = OUTPUT_ONLY, json_name = "token_type"];
  // Токен, используемый для получения нового Access Token после истечения срока действия текущего токена доступа
  string refresh_token = 4 [(google.api.field_behavior) = OUTPUT_ONLY, json_name = "refresh_token"];
  // Время (в секундах), через которое истечет срок действия Access Token
  int32 expires_in = 5 [(google.api.field_behavior) = OUTPUT_ONLY, json_name = "expires_in"];
}

// Фильтр партнеров
message PartnerFilter {
  // По тексту.
  // Если значение не передано то поиск по нему не производится.
  // # Диапазон: 3..64.
  // # Поиск производится по полям:
  // # - Наименование
  google.protobuf.StringValue text = 1;
  // От даты обновления партнера включительно (больше или равно)
  google.protobuf.Timestamp begin_changed_at = 2;
  // До даты обновления партнера (меньше)
  google.protobuf.Timestamp end_changed_at = 3;
  // По идентификаторам доступных шаблонов.
  // # Тип: Guid
  repeated string template_ids = 4;
  // Показывать удаленные
  google.protobuf.BoolValue is_show_deleted = 5;
}

// Пагинация партнеров
message PartnerPaging {
  // Справочник типов значений сортировки.
  // # Тип: byte
  enum OrderByType {
    // Значение не указано
    ORDER_BY_TYPE_UNKNOWN = 0;
    // Дата последнего изменения
    CHANGED_AT = 1;
    // Дата создания
    CREATED_AT = 2;
    // По рангу для поиска по тексту.
    // Применяется когда передано поле для поиска по тексту.
    // В случае если текстовое поле не передано, применяется значение по умолчанию
    RANK = 3;
  }
  // Тип значения сортировки.
  // Если значение не передано, то будет взято значение по умолчанию.
  // # По умолчанию: CHANGED_AT
  OrderByType order_by_type = 1;
  // Справочник типов направлений сортировки.
  // # Тип: byte
  enum DirectionType {
    // Значение не указано
    DIRECTION_TYPE_UNKNOWN = 0;
    // От большего к меньшему
    DESC = 1;
    // От меньшего к большему
    ASC = 2;
  }
  // Тип направления сортировки.
  // # По умолчанию: DESC
  DirectionType direction_type = 2;
  // Количество записей на страницу.
  // Если значение 0 (не передано), то будет взято значение по умолчанию.
  // # Диапазон: 0..100.
  // # По умолчанию: 20
  int32 limit = 3;
  // Сдвиг.
  // # Диапазон: 0..2147483647
  int32 offset = 4;
}
// Запрос сохранения партнера
message PostPartnerRequest {
  // Партнер
  Partner data = 1 [(google.api.field_behavior) = REQUIRED];
}
// Ответ на запрос сохранения партнера
message PostPartnerResponse {
  // Ошибка запроса сохранения партнера
  message Error {
    // Api-Key обязателен при создании партнера
    message MissingApiKeyOnCreation {}
    // Причина ошибки
    oneof reason {
      // Ошибка валидации
      ValidationError validation = 1;
      // Ошибка сохранения
      Partner.SavingError saving = 2;
      // Api-Key обязателен при создании партнера
      MissingApiKeyOnCreation missing_api_key_on_creation = 3;
    }
  }
  // Тип результата
  oneof type {
    // Партнер
    Partner data = 1;
    // Ошибка
    Error error = 2;
  }
}

// Запрос получения партнера
message GetPartnerRequest {
  // Идентификатор партнера.
  // # Тип: Guid
  string id = 1 [(google.api.field_behavior) = REQUIRED];
}
// Ответ на запрос получения партнера
message GetPartnerResponse {
  // Ошибка запроса получения партнера
  message Error {
    // Причина ошибки
    oneof reason {
      // Ошибка валидации
      ValidationError validation = 1;
    }
  }
  // Тип результата
  oneof type {
    // Партнер
    Partner data = 1;
    // Ошибка
    Error error = 2;
  }
}


// Запрос получения списка партнеров
message GetPartnerListRequest {
  // Фильтр
  PartnerFilter filter = 1;
  // Вариант разбиения на страницы
  oneof pagination {
    // Пагинация
    PartnerPaging paging = 2;
  }
}
// Ответ на запрос получения списка партнеров
message GetPartnerListResponse {
  // Ошибка запроса получения списка партнеров
  message Error {
    // Причина ошибки
    oneof reason {
      // Ошибка валидации
      ValidationError validation = 1;
    }
  }
  // Тип результата
  oneof type {
    // Партнер
    Partner data = 1;
    // Ошибка
    Error error = 2;
  }
}

// Запрос получения количества партнеров
message GetPartnerCountRequest {
  // Фильтр
  PartnerFilter filter = 1;
}
// Ответ на запрос получения количества партнеров
message GetPartnerCountResponse {
  // Ошибка запроса получения количества партнеров
  message Error {
    // Причина ошибки
    oneof reason {
      // Ошибка валидации
      ValidationError validation = 1;
    }
  }
  // Тип результата
  oneof type {
    // Всего партнеров
    int32 data = 1;
    // Ошибка
    Error error = 2;
  }
}

// Запрос получения токена
message GetPartnerTokenRequest {
  // Код.
  // # Тип: Guid
  string code = 1 [(google.api.field_behavior) = REQUIRED];
  // Тип ответа.
  // # Допустимые значения: "id_token", "token", "id_token token"
  string response = 2 [(google.api.field_behavior) = REQUIRED, json_name = "response_type"];
  // Ваучер
  string nonce = 3 [(google.api.field_behavior) = REQUIRED];
}

// Ответ на запрос получения токена
message GetPartnerTokenResponse {
  // Ошибка
  message Error {
    // Данные пользователя для формирования идентификационного токена не найдены.
    // Причины:
    // - Пользователь с таким идентификатором отсутсвует в БД ключа;
    // - Ошибка взаимодействия с сервисом Домохозяйств
    message UserNotFoundError {}
    // Ошибка формирования токена.
    // Причины:
    // - Ошибка в сервисе KMS.
    // - Ошибка в сервисе Identity
    message TokenGenerationError {}
    // Некорректный код.
    // Причины:
    // - Передан неверный код;
    // - Превышен допустимый временной интервал между запросом на формирование кода и запросом на получение идентификационного токена
    message CodeNotFoundOrExpiredError {}
    // Переданный тип ответа не поддерживается
    message ResponseTypeIsNotSupported {}
    // Доступ запрещен.
    // Причины:
    // - У запрашивающего приложения нет разрешения на выбранный тип ответа
    message PermissionDenied {}
    // Причина ошибки
    oneof reason {
      // Ошибка валидации
      ValidationError validation = 1;
      // Ошибка: пользователь не найден
      UserNotFoundError user_not_found = 2;
      // Ошибка: формирования токена
      TokenGenerationError token_generation_error = 3;
      // Ошибка: некорректный код
      CodeNotFoundOrExpiredError code_not_found_or_expired_error = 4;
      // Переданный тип ответа не поддерживается
      ResponseTypeIsNotSupported response_type_is_not_supported = 5;
      // Доступ запрещен
      PermissionDenied permission_denied = 6;
    }
  }
  // Тип результата
  oneof type {
    // Токен
    Token data = 1;
    // Ошибка
    Error error = 2;
  }
}

// Запрос сохранения публичного ключа партнера
message PostPartnerPublicKeyRequest {
  // Публичный ключ
  string public_key = 1 [(google.api.field_behavior) = REQUIRED];
}
// Ответ на запрос сохранения публичного ключа партнера
message PostPartnerPublicKeyResponse {
  // Ошибка запроса сохранения публичного ключа партнера
  message Error {
    // Причина ошибки
    oneof reason {
      // Ошибка валидации
      ValidationError validation = 1;
      // Ошибка сохранения
      Partner.SavingError saving = 2;
    }
  }
  // Тип результата
  oneof type {
    // Партнер
    Partner data = 1;
    // Ошибка
    Error error = 2;
  }
}

// Запрос на удаление партнёра
message DeletePartnerRequest {
  // Идентификатор партнёра.
  // # Тип: Guid
  string id = 1 [(google.api.field_behavior) = REQUIRED];
}

// Ответ на запрос удаления партнёра
message DeletePartnerResponse {
  // Ошибка удаления партнёра
  message Error {
    // У партнера есть хотя бы одно приложение
    message LinkedAppExists {}
    // Причина ошибки
    oneof reason {
      // Ошибка валидации
      ValidationError validation = 1;
      // У партнера есть хотя бы одно приложение
      LinkedAppExists linked_app_exists = 2;
    }
  }
  // Тип результата
  oneof type {
    // Ошибка при удалении
    Error error = 1;
  }
}

// Запрос получения адреса по ОРПОНу
message GetPartnerAddressOrponRequest {
  // ОРПОН.
  // # Диапазон: 1..9223372036854775807
  int64 orpon = 1 [(google.api.field_behavior) = REQUIRED];
}

// Ответ на запрос получения адреса по ОРПОНу
message GetPartnerAddressOrponResponse {
  // Ошибка
  message Error {
    // Доступ запрещен.
    // Причины:
    // - У партнера нет разрешения на переданный ОРПОН
    message PermissionDenied {}
    // Причина ошибки
    oneof reason {
      // Ошибка валидации
      ValidationError validation = 1;
      // Доступ запрещен
      PermissionDenied permission_denied = 2;
    }
  }
  // Тип результата
  oneof type {
    // Адрес
    string data = 1;
    // Ошибка
    Error error = 2;
  }
}

// Идентификационный токен
message IdTokenPayload {
  // Идентификатор токена
  string jti = 1 [(google.api.field_behavior) = REQUIRED];
  // Ваучер
  string nonce = 2 [(google.api.field_behavior) = REQUIRED];
  // Идентификатор пользователя, для которого выдан токен
  string sub = 3 [(google.api.field_behavior) = REQUIRED];
  // Роль
  google.protobuf.StringValue role = 4;
  // Номер телефона
  google.protobuf.StringValue phone_number = 5;
  // Электронная почта
  google.protobuf.StringValue email = 6;
  // Идентифкатор ОРПОН
  google.protobuf.StringValue orpon = 7;
  // Номер квартиры
  google.protobuf.StringValue room = 8;
  // Идентифкатор компании
  google.protobuf.StringValue company_id = 9;
  // Наименование компании
  google.protobuf.StringValue company_name = 10;
  // Время авторизации пользователя unix timestamp
  string auth_time = 11 [(google.api.field_behavior) = REQUIRED];
  // Срок истечения действия токен unix timestamp
  string exp = 12 [(google.api.field_behavior) = REQUIRED];
  // Сервис, выдавший токен
  string iss = 13 [(google.api.field_behavior) = REQUIRED];
  // Url сервиса, для которого выпустили токен
  string aud = 14 [(google.api.field_behavior) = REQUIRED];
  // Идентификатор приложения партнёра 
  string app_id = 15 [(google.api.field_behavior) = REQUIRED];
  // КЛАДР
  google.protobuf.StringValue kladr_id = 16;
}
