openapi: 3.0.3
info:
  title: QUIZ AMOCRM integration
  description: Интеграция с Амо. Ключевая задача - создавать заявки в соответствующей воронкой подключенного аккаунта
  version: 1.0.0

tags:
  - name: main
    description: Операции связанные с AmoCRM

paths:
  /account:
    post:
      operationId: ConnectAccount
      description: подключение аккаунта амо к аккаунту quiz. На вход получает только токен. На выход отдаёт ссылку для подключения. Создаёт модель аккаунта, имеющую связь с основным аккаунтом
      tags:
        - main
      responses:
        '200':
          description: успешное создание ссылки для авторизации
          content:
            'application/json':
              schema:
                $ref: '#/components/schemas/ConnectAccountResp'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: SoftDeleteAccount
      description: мягкое удаление аккаунта. Юзер должен иметь возможность создать новый аккаунт, взамен удалённого
      tags:
        - main
      responses:
        '200':
          $ref: '#/components/responses/200'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
    get:
      operationId: GetCurrentAccount
      description: получение текущего аккаунта, компании текщего пользователя
      tags:
        - main
      responses:
        '200':
          description: аккаунт интеграции амо
          content:
            'application/json':
              schema:
                $ref: '#/components/schemas/AccountAmo'
        '401':
          $ref: '#/components/responses/401'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /webhook/create:
    get:
      operationId: WebhookCreate
      description: это метод для получения пары токенов для аккаунта. Пары токенов стоит хранить в отдельной таблице и завести воркер, который будет обновлять рефреш. https://www.amocrm.ru/developers/content/oauth/step-by-step - вот дока для этого метода
      tags:
        - main
      responses:
        '200':
          description: Success
  /webhook/delete:
    get:
      operationId: WebhookDelete
      description: это метод для оповещения об удалении итеграции из учетки в амо. При его вызове надо мягко удалить соответствующий аккаунт. https://www.amocrm.ru/developers/content/oauth/step-by-step#%D0%A5%D1%83%D0%BA-%D0%BE%D0%B1-%D0%BE%D1%82%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B8-%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D1%86%D0%B8%D0%B8
      tags:
        - main
      responses:
        '200':
          description: Success
  /users:
    get:
      operationId: GettingUserWithPagination
      tags:
        - main
      description: получение списка юзеров для текущего пользователя и его компании, закешированных у нас, с пагинацией https://www.amocrm.ru/developers/content/crm_platform/users-api#users-list
      parameters:
        - in: query
          name: Pagination
          description: Параметры пагинации
          required: false
          schema:
            type: object
            properties:
              page:
                type: integer
                description: Номер страницы пагинации. Если не указан, используется значение по умолчанию.
                example: 1
              size:
                type: integer
                description: Размер страницы пагинации. Если не указан, используется значение по умолчанию.
                example: 25
            required:
              - page
              - size
      responses:
        '200':
          description: успешное получение списка пользователей для текущего пользователя и его компании
          content:
            'application/json':
              schema:
                $ref: "#/components/schemas/UserListResp"
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
    patch:
      operationId: UpdateListUsers
      description: обновление списка юзеров
      tags:
        - main
      responses:
        '200':
          $ref: '#/components/responses/200'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
  /pipelines:
    get:
      operationId: GetPipelinesWithPagination
      tags:
        - main
      description: получение списка воронок, закешированных у нас, с пагинацией https://www.amocrm.ru/developers/content/crm_platform/leads_pipelines
      parameters:
        - in: query
          name: Pagination
          description: Параметры пагинации
          required: false
          schema:
            type: object
            properties:
              page:
                type: integer
                description: Номер страницы пагинации. Если не указан, используется значение по умолчанию.
                example: 1
              size:
                type: integer
                description: Размер страницы пагинации. Если не указан, используется значение по умолчанию.
                example: 25
            required:
              - page
              - size
      responses:
        '200':
          description: успешное получение списка воронок
          content:
            'application/json':
              schema:
                $ref: "#/components/schemas/UserListPipelinesResp"
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
    patch:
      operationId: UpdateListPipelines
      description: обновление списка воронок
      tags:
        - main
      responses:
        '200':
          $ref: '#/components/responses/200'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
  /steps:
    get:
      operationId: GetStepsWithPagination
      tags:
        - main
      description: получение списка этапов воронок, закешированных у нас, с пагинацией https://www.amocrm.ru/developers/content/crm_platform/leads_pipelines#%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D1%81%D1%82%D0%B0%D1%82%D1%83%D1%81%D0%BE%D0%B2-%D0%B2%D0%BE%D1%80%D0%BE%D0%BD%D0%BA%D0%B8-%D1%81%D0%B4%D0%B5%D0%BB%D0%BE%D0%BA
      parameters:
        - in: query
          name: Pagination
          description: Параметры пагинации
          required: false
          schema:
            type: object
            properties:
              pipelineID:
                type: integer
                description: id воронки которой принадлежат шаги
                example: 123
              page:
                type: integer
                description: Номер страницы пагинации. Если не указан, используется значение по умолчанию.
                example: 1
              size:
                type: integer
                description: Размер страницы пагинации. Если не указан, используется значение по умолчанию.
                example: 25
            required:
              - pipelineID
              - page
              - size
      responses:
        '200':
          description: успешное получение списка шагов воронок
          content:
            'application/json':
              schema:
                $ref: "#/components/schemas/UserListStepsResp"
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
    patch:
      operationId: UpdateListSteps
      description: обновление списка этапов воронок
      tags:
        - main
      responses:
        '200':
          $ref: '#/components/responses/200'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
  /fields:
    get:
      operationId: GetFieldsWithPagination
      tags:
        - main
      description: получение списка кастомных полей, закешированных у нас, с пагинацией https://www.amocrm.ru/developers/content/crm_platform/custom-fields#%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%BF%D0%BE%D0%BB%D0%B5%D0%B9-%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8
      parameters:
        - in: query
          name: Pagination
          description: Параметры пагинации
          required: false
          schema:
            type: object
            properties:
              page:
                type: integer
                description: Номер страницы пагинации. Если не указан, используется значение по умолчанию.
                example: 1
              size:
                type: integer
                description: Размер страницы пагинации. Если не указан, используется значение по умолчанию.
                example: 25
            required:
              - page
              - size
      responses:
        '200':
          description: успешное получение списка тегов
          content:
            'application/json':
              schema:
                $ref: "#/components/schemas/UserListFieldsResp"
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
    patch:
      operationId: UpdateListCustom
      description: обновление списка кастомных полей
      tags:
        - main
      responses:
        '200':
          $ref: '#/components/responses/200'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
  /tags:
    get:
      operationId: GetTagsWithPagination
      tags:
        - main
      description: получение списка тегов, закешированных у нас, с пагинацией https://www.amocrm.ru/developers/content/crm_platform/tags-api#%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D1%82%D0%B5%D0%B3%D0%BE%D0%B2-%D0%B4%D0%BB%D1%8F-%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8
      parameters:
        - in: query
          name: Pagination
          description: Параметры пагинации
          required: false
          schema:
            type: object
            properties:
              page:
                type: integer
                description: Номер страницы пагинации. Если не указан, используется значение по умолчанию.
                example: 1
              size:
                type: integer
                description: Размер страницы пагинации. Если не указан, используется значение по умолчанию.
                example: 25
            required:
              - page
              - size
      responses:
        '200':
          description: успешное получение списка тегов
          content:
            'application/json':
              schema:
                $ref: "#/components/schemas/UserListTagsResp"
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
    patch:
      operationId: UpdateListTags
      description: обновление списка тегов
      tags:
        - main
      responses:
        '200':
          $ref: '#/components/responses/200'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
#   таких запросах для блупринта обязательно нужно указывать параметры
  /rules/{quizID}:
    get:
      operationId: GettingQuizRules
      tags:
        - main
      description: получение настроек интеграции для конкретного квиза
      parameters:
        - in: path
          name: quizID
          required: true
          schema:
            type: string
          description: Id квиза
      responses:
        '200':
          description: успешное получение настройки интеграции
          content:
            'application/json':
              schema:
                $ref: "#/components/schemas/Rule"
        '400':
          $ref: '#/components/responses/400'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      operationId: SetQuizSettings
      description: создание настроек интеграции для конкретного квиза, при успешном исходе кладет задачу в кафку, на 1 квиз 1 правило
      tags:
        - main
      parameters:
        - in: path
          name: quizID
          required: true
          schema:
            type: string
          description: Id квиза
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RulesReq"
      responses:
        '200':
          $ref: '#/components/responses/200'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    patch:
      operationId: ChangeQuizSettings
      description: изменение настроек интеграции для конкретного квиза, при успешном исходе кладет задачу в кафку, на 1 квиз 1 правило
      tags:
        - main
      parameters:
        - in: path
          name: quizID
          required: true
          schema:
            type: string
          description: Id квиза
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RulesReq"
      responses:
        '200':
          $ref: '#/components/responses/200'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'

components:
  schemas:
    Rule:
      type: object
      description: объект настройки правил сохранения данных в амо
      properties:
        ID:
          type: integer
          description:  айдишник в нашей системе Primary Key
        AccountID:
          type: integer
          description: связь с аккаунтом в интеграции амо id в амо
        QuizID:
          type: integer
          description: айдишник опроса
        PerformerID:
          type: integer
          description: айдишник ответственного за сделку
        PipelineID:
          type: integer
          description: айдишник воронки
        StepID:
          type: integer
          description: айдишник этапа
        FieldsRule:
          type: object
          description: правила заполнения полей сущностей в амо
          properties:
            lead:
              $ref: '#/components/schemas/FieldRule'
            contact:
              $ref: '#/components/schemas/ContactRules'
            company:
              $ref: '#/components/schemas/FieldRule'
            customer:
              $ref: '#/components/schemas/FieldRule'
        TagsToAdd:
          type: object
          properties:
            Lead:
              type: array
              items:
                type: integer
                format: int64
              description: Массив ID тегов для Leads, теги с id который наш Primary
            Contact:
              type: array
              items:
                type: integer
                format: int64
              description: Массив ID тегов для Contacts, теги с id который наш Primary
            Company:
              type: array
              items:
                type: integer
                format: int64
              description: Массив ID тегов для Company, теги с id который наш Primary
            Customer:
              type: array
              items:
                type: integer
                format: int64
              description: Массив ID тегов для Customer, теги с id который наш Primary
          example:
            Lead: [ 101, 102, 103 ]
            Contact: [ 201, 202, 203 ]
            Company: [ 301, 302, 303 ]
            Customer: [ 401, 402, 403 ]
        Deleted:
          type: boolean
          description: флаг мягкого удаления
        CreatedAt:
          type: integer
          description: таймштамп создания воронки в нашей системе
    FieldRule:
      type: object
      description:  правила сопоставления вопроса полю
      properties:
        QuestionID:
          type: integer
          additionalProperties:
            type: integer
            description: сопоставление айдишника вопроса полю, которое будет заполняться ответом. соответственно QuestionID это айдишник вопроса. это я так мэпу пытался записать. Мапа, где ключи - QuestionID, значения - ID кастомного поля
    Pipeline:
      type: object
      description: объект воронки амо
      properties:
        ID:
          type: integer
          description:  айдишник в нашей системе Primary Key
        AmoID:
          type: integer
          description: айдишник воронки в амо
        AccountID:
          type: integer
          description: связь с аккаунтом в интеграции амо id аккаунта в амо
        Name:
          type: string
          description:  название воронки в амо
        IsArchive:
          type: boolean
          description: флаг архивной воронки в амо
        Deleted:
          type: boolean
          description: флаг мягкого удаления
        CreatedAt:
          type: integer
          description: таймштамп создания воронки в нашей системе
    Step:
      type: object
      description: объект шага воронки амо
      properties:
        ID:
          type: integer
          description:  айдишник в нашей системе Primary Key
        AmoID:
          type: integer
          description: айдишник шага воронки в амо
        PipelineID:
          type: integer
          description: айдишник воронки в амо
        AccountID:
          type: integer
          description: связь с аккаунтом в интеграции амо id в амо
        Name:
          type: string
          description:  название воронки в амо
        Color:
          type: string
          description:  цвет шага в амо
        Deleted:
          type: boolean
          description: флаг мягкого удаления
        CreatedAt:
          type: integer
          description: таймштамп создания воронки в нашей системе
    Field:
      type: object
      description: объект кастомного поля амо
      properties:
        ID:
          type: integer
          description:  айдишник в нашей системе Primary Key
        AmoID:
          type: integer
          description: айдишник кастомного поля в амо
        Code:
          type: string
          description: кодовое слово в амо
        AccountID:
          type: integer
          description: связь с аккаунтом в интеграции амо id аккаунта в амо
        Name:
          type: string
          description:  название воронки в амо
        EntityType:
          type: string
          description: тип сущности в амо, для которой это кастомное поле
        Type:
          type: string
          description: тип поля https://www.amocrm.ru/developers/content/crm_platform/custom-fields#%D0%94%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D1%8B%D0%B5-%D1%82%D0%B8%D0%BF%D1%8B-%D0%BF%D0%BE%D0%BB%D0%B5%D0%B9
        Deleted:
          type: boolean
          description: флаг мягкого удаления
        CreatedAt:
          type: integer
          description: таймштамп создания воронки в нашей системе
    UTM:
      type: object
      description: объект настройки UTM, данные о которых мы сохраняем в амо
      properties:
        ID:
          type: integer
          description:  айдишник в нашей системе Primary Key
        AmoFieldID:
          type: integer
          description: айдишник кастомного поля в амо
        QuizID:
          type: integer
          description: айдишник квиза
        AccountID:
          type: integer
          description: связь с аккаунтом в интеграции амо id амо
        Name:
          type: string
          description:  название тега в амо
        Deleted:
          type: boolean
          description: флаг мягкого удаления
        CreatedAt:
          type: integer
          description: таймштамп создания тега в нашей системе
    Tag:
      type: object
      description: объект тега из амо
      properties:
        ID:
          type: integer
          description:  айдишник в нашей системе Primary Key
        AmoID:
          type: integer
          description: айдишник тега в амо
        AccountID:
          type: integer
          description: связь с аккаунтом в интеграции амо id аккаунта в амо
        Entity:
          type: string
          description:  сущность, к которой принадлежит этот тег. Наверное, стоит сделать через enum в базе
        Name:
          type: string
          description:  название тега в амо
        Color:
          type: string
          description:  цвет тега в амо
        Deleted:
          type: boolean
          description: флаг мягкого удаления
        CreatedAt:
          type: integer
          description: таймштамп создания тега в нашей системе
    AccountAmo:
      type: object
      description: модель основного аккаунта "компании" амо
      properties:
        id:
          type: integer
          format: int64
          description: postgres big serial
        accountID:
          type: string
          description: ID аккаунта нашей системы
        amoID:
          type: integer
          format: int32
          description: ID компании в AmoCRM
        name:
          type: string
          description: Название компании
        deleted:
          type: boolean
          description: Флаг, указывающий на удаление
        createdAt:
          type: string
          format: date-time
          description: Время создания
        subdomain:
          type: string
          description: Поддомен компании в амо
        country:
          type: string
          description: Страна
        driveURL:
          type: string
          description: URL объектного хранилища
        stale:
          type: boolean
          description: флаг "не свежести" если с токенами все в порядке - false, если просрочились то true
    UserAmoAccount:
      type: object
      description: объект пользователя аккаунта амо, который находится под компанией которая подключилась
      properties:
        id:
          type: integer
          format: int64
          description: postgres big serial
        amoID:
          type: integer
          format: int32
          description: ID компании в амо, к которой пользователь принадлежит
        amoUserID:
          type: integer
          format: int32
          description: ID пользователя в амо
        name:
          type: string
          description: Имя
        email:
          type: string
          description: Email
        role:
          type: integer
          format: int32
          description: Роль
        group:
          type: integer
          format: int32
          description: Группа
        deleted:
          type: boolean
          description: Флаг, указывающий на удаление
        createdAt:
          type: string
          format: date-time
          description: Время создания записи

    ConnectAccountResp:
      type: object
      properties:
        link:
          type: string
          description: ссылка для авторизации в амо
    GetListUserUTMResp:
      type: object
      properties:
        count:
          type: integer
          description: общее количество юзеров, которые у нас закешированы для этого пользователя
        items:
          type: array
          description: список юзеров, которые были закешированы нашим сервисом
          items:
            $ref: "#/components/schemas/UTM"
    SaveUserListUTMReq:
      type: object
      properties:
        utms:
          type: array
          description: список utm для сохранения. сохранять только те, которых в этом аккаунте ещё нет
          items:
            $ref: "#/components/schemas/UTM"
    ListSavedIDUTMResp:
      type: object
      properties:
        IDs:
          type: array
          description: список айдишников сохранённых меток
          items:
            type: string
    ListDeleteUTMIDsReq:
      type: object
      properties:
        utms:
          type: array
          description: список айдишников utm которые удалить
          items:
            type: integer
    UserListResp:
      type: object
      properties:
        count:
          type: integer
          description: общее количество юзеров, которые у нас закешированы для этого пользователя и его компании
        items:
          type: array
          description: список юзеров, которые были закешированы нашим сервисом
          items:
            $ref: "#/components/schemas/UserAmoAccount"
    UserListPipelinesResp:
      type: object
      properties:
        count:
          type: integer
          description: общее количество воронок, которые у нас закешированы для этого пользователя
        items:
          type: array
          description: список воронок, которые были закешированы нашим сервисом
          items:
            $ref: "#/components/schemas/Pipeline"
    UserListStepsResp:
      type: object
      properties:
        count:
          type: integer
          description: общее количество шагов воронок, которые у нас закешированы для этого пользователя
        items:
          type: array
          description: список шагов воронок, которые были закешированы нашим сервисом
          items:
            $ref: "#/components/schemas/Step"
    UserListFieldsResp:
      type: object
      properties:
        count:
          type: integer
          description: общее количество кастомных полей, которые у нас закешированы для этого пользователя
        items:
          type: array
          description: список кастомных полей, которые были закешированы нашим сервисом
          items:
            $ref: "#/components/schemas/Field"
    UserListTagsResp:
      type: object
      properties:
        count:
          type: integer
          description: общее количество тегов, которые у нас закешированы для этого пользователя
        items:
          type: array
          description: список тегов, которые были закешированы нашим сервисом
          items:
            $ref: "#/components/schemas/Tag"
    PaginationReq:
      type: object
      properties:
        page:
          type: integer
          description: указание страницы пагинации. Если страница не указана, применять 0
        size:
          type: integer
          description: указание размера страницы пагинации. По умолчанию применять 25
    RulesReq:
      type: object
      properties:
        PerformerID:
          type: integer
          description: "айдишник ответственного за сделку"
        PipelineID:
          type: integer
          description: "айдишник воронки"
        StepID:
          type: integer
          description: "айдишник этапа"
        Fieldsrule:
          type: object
          properties:
            Lead:
              $ref: '#/components/schemas/FieldRule'
            Contact:
              $ref: '#/components/schemas/ContactRules'
            Company:
              $ref: '#/components/schemas/FieldRule'
            Customer:
              $ref: '#/components/schemas/FieldRule'
        TagsToAdd:
          type: object
          properties:
            Lead:
              type: array
              items:
                type: integer
                format: int64
              description: Массив ID тегов для Leads, теги с id который наш Primary
            Contact:
              type: array
              items:
                type: integer
                format: int64
              description: Массив ID тегов для Contacts, теги с id который наш Primary
            Company:
              type: array
              items:
                type: integer
                format: int64
              description: Массив ID тегов для Company, теги с id который наш Primary
            Customer:
              type: array
              items:
                type: integer
                format: int64
              description: Массив ID тегов для Customer, теги с id который наш Primary
          example:
            Lead: [ 101, 102, 103 ]
            Contact: [ 201, 202, 203 ]
            Company: [ 301, 302, 303 ]
            Customer: [ 401, 402, 403 ]

    ContactRules:
      type: object
      description: правила заполнения сущности контакта, название поля = id кастомного поля в амо
      properties:
        ContactRuleMap:
          type: string
          additionalProperties:
            type: integer
        QuestionID:
          type: integer
          additionalProperties:
            type: integer
            description: сопоставление айдишника вопроса полю, которое будет заполняться ответом. соответственно QuestionID это айдишник вопроса. это я так мэпу пытался записать. Мапа, где ключи - QuestionID, значения - ID кастомного поля

  responses:
    '200':
      description: Success
      content:
        application/json:
          schema:
            type: string
            description: Success
    '201':
      description: Created
      content:
        application/json:
          schema:
            type: string
            description: Created
    '204':
      description: No content
      content:
        application/json:
          schema:
            type: string
            description: No content
    '400':
      description: Bad Request
      content:
        application/json:
          schema:
            type: string
            description: Bad Request
    '401':
      description: Unauthorized
      content:
        application/json:
          schema:
            type: string
            description: Unauthorized
    '403':
      description: Forbidden
      content:
        application/json:
          schema:
            type: string
            description: Forbidden
    '404':
      description: Not Found
      content:
        application/json:
          schema:
            type: string
            description: Not Found
    '500':
      description: Internal Server Error
      content:
        application/json:
          schema:
            type: string
            description: Internal Server Error
