=======================================================
 План перехода на новые-кленовые события от Директории
=======================================================

Преамбула
=========

Сейчас формат событий от директории чересчур запутан, местами избыточен
и неинтуитивен. Когда мы их проектировали, то перемудрили и теперь
вынуждены признать, что это место требует переделки. Вот лишь некоторые
из проблем:

* Формат разных событий, хотя и похож, но тем не менее иногда сильно
  отличается и обманывает ожидания.
* Наличие опции expand_content усложняет понимание того, каких данных
  ожидать в payload.
* В коде Директории генерация одного и того же типа событий может
  происходить в разных местах, но из-за того, что почти всегда это
  делается через обобщённую функцию, иногда возникает ситуация, что
  данные для события складываются в базу и payload в чуть разном
  состоянии. Из-за этого, payload одного и того же события может
  отличаться.


Далее изложен план по переделке формата событий и переходу со старого
формата на новый.

Что нужно сделать
=================

Основные вещи которые нужно сделать:

* поменять формат событий на более интуитивный и понятный;
* сделать код для генерации событий более защищённым от ошибок;
* позволить клиентам API постепенно мигрировать со старого формата на новый.

Изменение формата
-----------------

Текущий формат обусловлен тем, что мы пытались сделать унификацию
разных типов событий. Отсюда и получилось что данные выглядят примерно так::

  {
      "content": {
          "diff": {
              "contacts": [
                  [
                      {
                          "type": "skype",
                          "value": "the-bob"
                      },
                      {
                          "type": "email",
                          "value": "bob@home.com"
                      }
                  ],
                  [
                      {
                          "type": "skype",
                          "value": "the-bob"
                      }
                  ]
              ],
              "position": [
                  {
                      "ru": "Тестировщик"
                  },
                  {
                      "ru": "Разработчик"
                  }
              ]
          },
          "directly": true
      },
      "event": "user_property_changed",
      "obj": {
          "about": null,
          "aliases": [],
          "birthday": null,
          "contacts": [
              {
                  "type": "skype",
                  "value": "the-bob"
              }
          ],
          "department_id": 2,
          "email": "bob@art-dev.yaconnect.com",
          "external_id": null,
          "gender": "male",
          "groups": [],
          "id": 100500,
          "is_admin": false,
          "is_dismissed": false,
          "login": "bob",
          "name": {
              "first_name": {
                  "ru": "Bob"
              },
              "last_name": {
                  "ru": "Hopkins"
              }
          },
          "nickname": "bob",
          "position": {
              "ru": "Разработчик"
          }
      },
      "org_id": 12,
      "revision": 1444
  }

Это пример события ``user_property_changed``

Тут мы видим два поля с данными - ``obj`` и ``content``. Почему ``obj`` не находится в ``content``,
но в там есть ``diff`` - не понятно.

Вот другой пример, событие ``department_user_deleted``::

  {
      "content": {
          "diff": {
              "members": {
                  "add": {},
                  "remove": {
                      "users": [
                          100500
                      ]
                  }
              }
          },
          "directly": true,
          "subject": {
              "about": null,
              "aliases": [],
              "birthday": null,
              "contacts": [
                  {
                      "type": "skype",
                      "value": "the-bob"
                  }
              ],
              "department_id": 2,
              "email": "bob@example.com",
              "external_id": null,
              "gender": "male",
              "groups": [],
              "id": 100500,
              "is_dismissed": false,
              "login": null,
              "name": {
                  "first_name": {
                      "ru": "Bob"
                  },
                  "last_name": {
                      "ru": "Hopkins"
                  }
              },
              "nickname": "bob",
              "org_id": 12,
              "position": {
                  "ru": "Разработчик"
              }
          }
      },
      "event": "department_user_deleted",
      "obj": {
          "description": {
              "ru": ""
          },
          "email": null,
          "external_id": null,
          "id": 1,
          "label": null,
          "members_count": 3,
          "name": {
              "en": "All employees",
              "ru": "Все сотрудники"
          },
          "parent_id": null,
          "removed": false
      },
      "org_id": 12,
      "revision": 1444
  }

Тут в словаре ``content`` появляются ещё два поля: ``subject`` и ``direct``.
При этом в ``subject`` полностью описание сотрудника, которого удалили из
отдела, а в ``direct`` - признак того, что пользователь входил непосредственно
в тот отдел, который указан в поле ``obj``.

Как видно, всё очень сложно. Поэтому предлагается формат событий упростить,
сделать более лаконичным и привязанным к конкретному типу событий.

К примеру, вышеупомянутый ``user_property_changed`` будет выглядеть как-то так::

  {
      "org_id": 12,
      "revision": 1444
      "event": "user_property_changed",
      "user_id": 100500,
      "diff": {
          "contacts": {
              "removed": [
                  {
                      "type": "email",
                      "value": "bob@home.com"
                  }
              ],
          },
          "position": {
              "before": "Тестировщик",
              "after": "Разработчик"
          ]
      }
  }

При этом поля ``org_id``, ``revision`` и ``event`` - обязательные, а остальные опциональные.

А вот так будет выглядеть событие ``department_user_deleted``::

  {
      "event": "department_user_deleted",
      "org_id": 12,
      "revision": 1444
      "user_id": 100500,
      "departement_id": 1,
      "directly": true,
  }

Ну разве не прелесть!?

٩(◕‿◕)۶


Так как же добиться этой прелести и начать её использовать?
-----------------------------------------------------------

В коде Директории придётся какое-то время поддерживать оба формата. Для этого,
нужно ввести версионирование, такое же, как и для остальной части API.
Подписки на события, события в базе и ручки для получения событий, должны быть
версионированы.

Код, генерирующий события, придётся дублировать, создавая одновременно
события старого, и нового образца.

Предлагается в коде генерировать события с помощью одной функции
``generate_event``, которая будет принимать название события, и kwargs,
затем строить словарь, соответствующий payload события и проверять его
на соответствие заданной json схеме. Сами JSON схемы каждого из событий,
будут частью API и доступны, как часть документации.

Переход
-------

Переходить на новый формат событий можно будет постепенно. Для перехода
надо будет сделать несколько вещей:

1. На время поддержать обработку и старого и нового формата payload,
   опираясь на версию API, передаваемую в заголовке X-API-Version.
2. Одномоментно подписаться на новую версию событий и отписаться от
   старой.
3. Если есть код, использующий ручку ``/events/``, то перейти там на
   использование новой версии API.

И это можно делать поэтапно - сначала для одного типа событий, затем для
другого, и так далее.


