User Data API

Статус документа

Оглавление

Общее описание

Данное API предназначено для централизованного хранения данных пользователя. Здесь описывается не физическая структура хранения, а логическая структура данных пользователя. Сервис предполагает доступ только аутентифицированного пользователя.

Примерами использования данного API являются:

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

Термины и определения

В этом документе используются термины и определения, описанные в документах «О программных интерфейсах сервисов Инетры» и «Термины и определения».

Структура данных

Данные пользователя имеют древовидную структуру элементов, на одном уровне иерархии которого не может существовать двух элементов с одинаковым идентификатором. Элементы, лежащие уровнем ниже относительно элемента называются «дочерними» элементами, для которых он, в свою очередь, называется «родительским».

Каждый элемент имеет идентификатор и относится к одному или более классам. Класс определяет набор атрибутов элемента.

Атрибуты элементов могут быть следующих типов данных:

Виды атрибутов:

Каждый элемент имеет следующие атрибуты:

С точки зрения структур передаваемых данных id элемента не является его атрибутом.

Путем до элемента называется строка, состоящая из идентификаторов его родительских элементов, разделенных символом /. При этом идентификаторы элементов должны быть закодированы также, как кодируются элементы URI.

Фильтр элементов

При выборке элементов используется понятие фильтра элементов. Фильтр — это выражение, определяющее критерии отбора элементов. Если фильтр не указан, в выборку попадают все элементы.

Выражение фильтра элементов записывается в префиксной нотации, состоящей из логических операторов и операндов, в свою очередь являющихся внутренними операторами сопоставления значений атрибутов (в левой части) с выражениями (в правой части) с использованием знака оператора.

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

Все остальные операторы работают по–разному в зависимости того, применяются они к единичным атрибутам или ко множественным.

Операторы над единичными атрибутами

Так как для всех типов данных атрибутов определено отношение порядка, для единичных атрибутов определены операторы больше и меньше. Полная таблица операторов:

название обозначение количество аргументов описание
строго меньше < 1 фильтрует элементы с меньшими значениями атрибута, чем операнд в правой части
строго больше > 1 фильтрует элементы с большими значениями атрибута, чем операнд в правой части
равенство = 1 или больше фильтрует элементы со значениями атрибута, равными одному из перечисленных в операнде в правой части

Оператор равенства для единичных атрибутов позволяет сокращать запись для огранизации множественных выборок, указывая имя атрибута один раз (ср. с оператором IN в SQL)

Операторы над множественными атрибутами

Для множественных атрибутов операции равносильны работе со множествами, при этом правый операнд трактуется как множество, состоящее из перечисленных через запятую элементов.

При этом вводится дополнительное отношение порядка «строго меньше» для множеств, содержащих упорядоченные элементы (лексикографический). Множество A считается «строго меньшим» множества B, если после сортировки элементов множеств A и B, существует такой индекс n, что выполняется:

  1. для всех i, (i < n) => (A[i] = B[i]),
  2. и одно из двух:
название обозначение количество аргументов описание
строго меньше < 1 или больше фильтрует элементы, атрибут которых «строго меньше» множества значений из оператора в правой части
строго больше > 1 или больше фильтрует элементы, атрибут которых «строго больше» множества значений из оператора в правой части
равенство = 1 или больше фильтрует элементы, атрибут которых равен множеству значений из операнда в правой части
является подмножеством << 1 или больше фильтрует элементы, атрибут которых включает в себя все значения из операнда в правой части
является надмножеством >> 1 или больше фильтрует элементы, атрибут которых является подмножеством множества значений из операнда в правой части

Например, имеется следующий набор элементов:

{ id: 'a', class_name: ['1'] },
{ id: 'b', class_name: ['1', '2'] },
{ id: 'c', class_name: ['1', '2', '3'] },
{ id: 'd', class_name: ['2', '3'] }
Результат применения фильтров множественных атрибутов
фильтр список идентификаторов элементов после применения фильтра
class_name>1,2 [ 'b', 'c' ]
class_name<1,2 [ 'a', 'b' ]
class_name=1,2 [ 'b' ]
class_name>>1,2 [ 'c', 'd' ]
class_name>>2,3 [ ]

Примеры фильтров

Самым простым фильтром является выборка элементов, содержащих заполненным необязательный атрибут. В таком случае в качестве фильтра стоит использовать строковое имя атрибута или его числовой код в протоколе Google Protocol Buffers.

Так, фильтр id выберет все элементы. А фильтр class_name — только элементы, содержащие атрибуты не только базового класса.

Чтобы выбрать элементы, в которых атрибут не только задан, но и имеет определенное значение, необходимо указать имя (строковое или числовое) атрибута, операцию и значения.

Например,

  1. created_by=app_1 выберет элементы, созданные приложением app_1
  2. created_by=app_1,app_2 выберет элементы, созданные приложением app_1 или приложением app_2. Атрибут created_by является единичным, поэтому символ , интерпретируется как операция «ИЛИ»
  3. class_name=action выберет элементы с классом action (и только с ним)
  4. class_name=action,catalogue_item выберет элементы с классами action и catalogue_item (и только с ними). Атрибут class_name является множественным, поэтому символ , интерпретируется как операция «И»
  5. class_name>action,catalogue_item выберет элементы, у которых имеются классы action и catalogue_item (но могут быть заданы и другие классы)

Для того, чтобы выбрать элементы для которых нужное условие является ложным, необходимо перед условием поставить символ !:

  1. !class_name выберет только элементы базового класса
  2. !created_by=app_1,app_2 выберет элементы, созданные не в приложении app_1 и не в приложении app_2

Для построения более сложных конструкций используются префиксные операции «И» и «ИЛИ», в которых операнды отделяются друг от друга символом ;. Стоит обратить внимание, что для корректной обработки фильтра список операндов должен также завершаться символом ;.

Например, фильтр created_by=app_1,app_2 эквивалентен фильтру |created_by=app_1;created_by=app_2;.

Напоследок рассмотрим сложный фильтр: &!created_by=app_1,app_2;class_name=cl_1,cl_2;|id=1;mtime>10;;. Выберет элементы, для которых выполняются:

  1. создан не приложениями app_1 или app_2
  2. класс равен cl_1+cl_2 и ничего больше
  3. и при этом выполнено одно из условий:

Аналогичное высказывание на ECMAScript выглядело бы так:

!( created_by === "app_1" || created_by === "app_2" ) &&
 ( class_name === [ "cl_1", "cl_2"] ) &&
 ( id === "1" || mtime > "10" )

Языковое описание фильтра

Filter := Equation
Filter := "!" Filter
Filter := "&" Filter ";" [ ... Filter ";" ]
Filter := "|" Filter ";" [ ... Filter ";" ]

Equation := attribute
Equation := attribute "=" multi_value
Equation := attribute "<" multi_value
Equation := attribute ">" multi_value
Equation := attribute "<<" multi_value
Equation := attribute ">>" multi_value

multi_value := value [ ... "," value ]

attribute := экранированная_строка
value := экранированная_строка

Атрибуты и значения атрибутов могут содержать символы, которые в языке фильтра несут особую смысловую нагрузку, — такие символы необходимо экранировать при помощи символа обратного слэша (\):

Cимвол Замена
\ \\
! \!
& \&
| \|
; \;
= \=
< \<
> \>
, \,

Сортировка элементов

При выдаче элементов важной частью является порядок, в котором возвращаются элементы. Порядок сортировки определяется пользователем указанием последовательности имен атрибутов через запятую, предваряя символом - при необходимости сортировки в убывающем порядке.

Если в именах атрибутов встречаются символы - или ,, они должны быть экранированы с помощью символа обратного слэша: \- и \,, соответственно.

Для множественных атрибутов сортировка осуществляется c помощью операций «строго больше» и «строго меньше».

Авторизация доступа для приложений

Авторизация доступа для приложений состоит из

Наличие доступа до элемента автоматически влечет наследование авторизации доступа приложения для всех его дочерних элементов.

Ограничения на создание, изменение элементов

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

Ограничения накладывается на дочерние элементы, список ограничений может быть получен при помощи метода quotas.

Полученная клиентом информация об ограничениях предназначена только для интерпретации ошибок или вывода ознакомительной информации для пользователя.

Для ограничений можно провести аналогию с хранением данных в файловой системе. Допустим, в файловой системе создан отдельный логический раздел D, который ограничен объемом хранения данных в 10Гб. Пользователь может заполнить все свободное место на логическом разделе D, но не сможет превысить ограничение, несмотря на то, что логический раздел D представляет только часть физического диска, суммарный объем которого может быть значительно больше 10Гб.

Алгоритм слияния элементов

При объединении учетных записей необходимо также произвести слияние элементов пользовательских данных. Для слияния элементов используется следующий алгоритм:

  1. Произвести слияние атрибутов.
  2. Для каждого дочернего элемента в источнике:

Пример реализации:

function merge_elements(dst, src, resolve_conflicts_in_favor_of_dst) {
  merge_attributes(dst.attributes, src.attributes, resolve_conflicts_in_favor_of_dst);

  for (child in src.children) {
    var new_name;
    if (!(child.id in dst)) {
       dst[child.id] = child;
    } else {
      if (child.autogenerated_id) {
         new_name = generate_child_id(dst);
         dst[new_name] = child;
         dst[new_name].id = new_name;
      } elif (dst[child.name].autogenerated_id) {
         new_name = generate_child_id(dst);
         dst[new_name] = dst[child.id];
         dst[new_name].id = new_name;
         dst[child.id] = child;
      } else {
         merge_elements(dst[child.id], child, resolve_conflicts_in_favor_of_dst);
      }
    }
  }
}

Алгоритм слияния атрибутов элемента

При слиянии атрибутов элемента в результате всегда получается корректное множество атрибутов, в котором заполненность обязательных атрибутов полностью соответствует описанию результирующего списка классов элемента.

Поэтому слияние множества атрибутов равносильно слиянию значений атрибутов источника в элемент–получатель.

  1. Для всех атрибутов элемента–источника:
    1. Если атрибут с данным protobuf_numbered_tag отсутствует в получателе, то сохранить его в получателе без изменений
    2. Иначе,
      1. Если атрибут является многозначным, то произвести слияние значений данного атрибута
      2. Иначе, если принято решение принимать решение конфликтов в пользу источника, записать значение атрибута источника в атрибут получателя

Пример реализации:

merge_attributes(dst, src, resolve_conflicts_in_favor_of_dst) {
  for (protobuf_numbered_tag in src) {
    if (!(protobuf_numbered_tag in dst)) {
      dst[protobuf_numbered_tag] = src[protobuf_numbered_tag];
    } else {
      if (AttributeDescription[protobuf_numbered_tag].multivalue) {
        dst[protobuf_numbered_tag] = merge_values(
          dst[protobuf_numbered_tag],
          src[protobuf_numbered_tag]
        );
      } else {
        if (resolve_conflicts_in_favor_of_dst) {
          dst[protobuf_numbered_tag] = src[protobuf_numbered_tag];
        }
      }
    }
  }
}

Слияние значений множественных атрибутов

Для множественных атрибутов одно и то же значение может храниться несколько раз. При этом в результате слияния количество каждого уникального значения возрасти не может.

Так, при слиянии массива значений [1, 2, 2, 3, 3, 3] с массивом [1, 1, 1, 2, 2, 3] результат: [1, 1, 1, 2, 2, 3, 3, 3].

Реализация может иметь следующий алгоритм:

  1. Завести результативный массив
  2. Отсортировать оба массива (src, dst)
  3. Завести указатели на элементы в массивах src и dst, s и d, соответственно
  4. Пока находимся в рамках обоих массивов:
    1. Если значения src[s] и dst[d] равны, сохраняем это значение в результативном массиве и увеличиваем значения обоих индексов, s и d
    2. Иначе, находим меньшее из значений на текущих позициях, вставляем его в результативный массив и увеличиваем значение индекса того массива, где было найдено меньшее значение
  5. Когда дошли до конца любого из массивов, в результативный массив записываем оставшиеся элементы из второго массива.

Пример реализации:

function merge_values(dst, src) {
  result = [];
  dst.sort();
  src.sort();

  for (var d = 0, s = 0; d < dst.length && s < src.length; ) {
    if (dst[d] === src[s]) {
      result.push(dst[d]);
      s++;
      d++;
    } else if (dst[d] > src[s]) {
      result.push(src[s]);
      s++;
    } else { // dst[d] < src[s]
      result.push(dst[d]);
      d++;
    }
  }
  return result.concat(dst.slice(d), src.slice(s));
}

Взаимодействие приложений с API

API работает по протоколу HTTPS (передаваемые данные должны шифроваться), корень доступа до API получается приложением из Регистратуры. Запросы к API составляются следующим образом:

https://<path_to_api>/<path_to_element>?method=<method>&format=<format>&[extra_data]

Где:

Формат упаковки параметров в теле ответа (или в теле запроса) может быть одним из:

Значения всех параметров перед передачей серверу необходимо кодировать для предотвращения неоднозначной интерпретации так, как это рекомендуется в RFC3986.

Некоторые запросы, такие как вставка элементов и изменение атрибутов элементов, могут потребовать передачи большого количества параметров, в таком случае они передаются в теле запроса методом POST. Возможны также «облегченные» варианты, когда параметров не очень много, они могут быть переданы в строке запроса. Способ кодирования параметров в таком случае обычно принимается как атрибут=значение, в отдельных случаях, когда данный способ кодирования не подходит для элементов класса, он должен быть описан вместе с описанием класса.

Аутентификация

Для аутентификации пользователя клиент должен передать полученный ранее токен доступа, выданный Auth API контрагента, по схеме аутентификации в соответствии с типом токена, например, «Bearer».

Для авторизации по типу токена «Bearer» рекомендуется использовать HTTP заголовок Authorization, пример:

Authorization: Bearer 18dasd81230dah12032

Стандартные коды ответов

Для каждого метода API предусмотрены стандартные коды ответов (RFC2616). При этом, если это была групповая операция, то существует код ответа для групповой операции вцелом, а также коды ответов для каждой элементарной операции. Ниже приведены стандартные коды ответов:

В случае ошибки при выполнении запроса необходимо возвращать пустое тело ответа, а для кодов 401, 403 должен быть передан HTTP заголовок WWW-Authenticate с указанием ожидаемой схемы аутентификации и дополнительнной информации (если применимо). Например, для схемы «Bearer» может быть указан дополнительный атрибут error со значением:

Методы

list — список дочерних элементов

Дополнительные параметры (передаются в строке запроса):

параметр по умолчанию смысл
filter отсутствует фильтр элементов
order отсутствует порядок возвращаемых элементов
quantity отсутствует максимальное количество возвращаемых элементов
skip 0 количество элементов, которые необходимо пропустить при выдаче

Формат ответа: сообщение ElementsList.

Также в заголовках ответа должно присутствовать поле X-Ordered-By с описанием сортировки, использованной при выдаче элементов. Если порядок возвращаемых элементов не задан, сервер должен поддерживать порядок при постраничной выдаче элементов, например, с использованием внутренних идентификаторов.

За сервером остается право уменьшить количество возвращаемых элементов (рекомендуемое ограничение — 100 элементов).

Клиент может передать желаемое количество элементов в параметре quantity, однако за сервером остается право уменьшить данное значение на своей стороне.

modify — добавление и изменение элементов

Дополнительные параметры передаются в теле запроса методом POST. Формат запроса — сообщение ModifyRequests, кодируется форматом, указанным в параметре запроса.

Параметр operation отвечает за тип производимых изменений и может принимать следующие значения:

Формат ответа: сообщение ActionResults.

В теле ответа содержатся результаты операций (сгруппированные по элементам) в том же порядке, в котором они были запрошены. В случае неуспешного запроса к элементу не применяется ни одна из операций, участвующих в нем.

Изменение элемента с несуществующим (или не указанным) идентификатором фактически означает запрос на его создание, идентификатор в таком случае создается сервером. При этом, последний должен сохранять признак самостоятельной генерации идентификатора элемента (выставлять атрибут id_autogenerated в значение true). В дальнейшем данный атрибут необходим для эффективной работы алгоритма объединения данных пользователей.

Удаление атрибутов элемента производится по значению, при этом отсутствие указанного значения атрибута молча игнорируется (считается, что действие успешно произведено). При этом, если множественный атрибута имеет несколько одинаковых значений, удаляется лишь столько из них, сколько было указано в запросе.

Например, если для целочисленного множественного атрибута foo, имеющего значения [0, 1, 1, 1, 2, 2, 3], поступил запрос на удаление значений [1, 1, 2, 2, 3, 3, 4, 4], то в результате операции значение атрибута станет равным [0, 1].

Запросы на изменения производятся поэлементно и для каждого элемента должны быть произведены сервером либо все, либо ни один. При этом после всех произведенных операций:

Если хотя бы одно из данных условий не выполняется, изменения атрибутов элемента не производится, а в ответ клиенту отправляется код ошибки 409 для операции изменения данного элемента.

Дополнительные коды ответов:

delete — удаление дочерних элементов

Дополнительные параметры:

параметр по умолчанию смысл
filter отсутствует фильтр элементов
id отсутствует идентификаторы удаляемых дочерних элементов (устарело с версии 1.13)

При удалении элемента также удаляются все его дочерние элементы.

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

Параметры id и filter являются взаимоисключающими, если будут указаны оба параметра, то сервер проигнорирует параметр id. Отсутствие заданного значения для параметра id (либо filter) должно трактоваться сервером как запрос на удаление всех дочерних элементов.

Если количество удаляемых элементов достаточно велико, допускается передача параметра id методом POST, кодирование производится таким же способом, как в при использовании метода GET.

Формат ответа: сообщение ActionResults.

Рекомендации к реализации на сервере:

  1. данный метод не удаляет элементы, а помечает их таким образом, чтобы они не участвовали в выдаче метода list

quotas — получение списка ограничений

Данный метод позволяет получить список установленных ограничений. Если ограничения отсутствуют, то метод вернет пустой список. Не допускается наличие нескольких ограничений одного типа.

Каждое ограничение описывает текущий и максимальный уровень потребления ресурсов.

Для указания языка, на котором стоит возвращать описание квот, необходимо в запросе использовать HTTP заголовок Accept-Language.

Формат ответа: сообщение QuotaList

classes — получение известного списка классов

Данный метод позволяет приложениям получить доступ к описанию классов, зарегистрированных в системе. Элемент, переданный в параметрах, игнорируется.

Дополнительные параметры:

параметр значение по умолчанию смысл
quantity 10 максимальное количество возвращаемых классов
skip 0 количество классов, которые необходимо пропустить при выдаче

Формат ответа: сообщение Classes.

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

message AttributeDescription {
  optional string name = 1;
  optional AttributeValueType value_type = 2;
  optional bool mandatory = 3 [ default = false ];
  optional bool multivalue = 4 [ default = false ];
  optional int32 protobuf_numbered_tag = 5;
  optional bool read_only = 6 [ default = false ];
}

Описание класса сводится к описанию атрибутов, которые принадлежат ему. Описание упаковывается в сообщение AttributeDescription, в котором:

  1. name — имя атрибута. Используется для представления атрибута в формате JSON
  2. value_type — тип данных
  3. mandatory — признак обязательности
  4. multivalue — признак множественности
  5. protobuf_numbered_tag — число, которым кодируются значения атрибута в формате Google Protocol Buffers
  6. read_only — признак того, что атрибут доступен только для чтения

Аналогия с деревом каталогов и файлов

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

При этом:

Спецификация API для Google Protocol Buffers

import "google/protobuf/descriptor.proto";
package UserDataAPIv1;

extend google.protobuf.FieldOptions {
  optional bool mandatory = 50000;
  optional bool read_only = 50001;
}

extend google.protobuf.MessageOptions {
  optional string class_name = 50000;
}

message Element {
  optional string id = 1;
  optional bytes attributes = 2;
}

message ElementsList {
  repeated Element elements = 1;
  optional int32 total_count = 2;
  optional int32 items_skipped = 3 [default = 0];
}

message ModifyRequest {
  message Change {
    enum Operation {
      ADD = 0;
      DELETE = 1;
      REPLACE = 2;
    }

    optional Operation operation = 1;
    optional bytes attributes = 2;
  }

  optional string id = 1;
  repeated Change changes = 2;
}

message ModifyRequests {
  repeated ModifyRequest modify_requests = 1;
}

message ActionResult {
  optional string id = 1;
  optional int32 code = 2;
  optional string message = 3;
}

message ActionResults {
  repeated ActionResult results = 1;
}

enum AttributeValueType {
  BOOLEAN = 0;
  INTEGER = 1;
  FLOAT = 2;
  STRING = 3;
}

message AttributeDescription {
  optional string name = 1;
  optional AttributeValueType value_type = 2;
  optional bool mandatory = 3 [ default = false ];
  optional bool multivalue = 4 [ default = false ];
  optional int32 protobuf_numbered_tag = 5;
  optional bool read_only = 6 [ default = false ];
}

message Class {
  optional string name = 1;
  repeated AttributeDescription attributes = 2;
}

message Classes {
  repeated Class classes = 1;
  optional int32 total_count = 2;
  optional int32 items_skipped = 3 [default = 0];
}

Замечания:

Сообщения QuotaList, Quota — ограничение на использование ресурсов

message QuotaUnit {
  optional int64 quantity = 1;
  optional string title = 2;
}

message Quota {
  optional int64 level = 1;
  optional int64 limit = 2;
  optional string title = 3;
  repeated QuotaUnit units = 4;
}

message QuotaList {
  repeated Quota quotas = 1;
}

Замечания:

Рекомендованные классы элементов

С точки зрения Google Protocol Buffers, как формата упаковки данных, все атрибуты упаковываются в одно сообщение, которое расширяется каждым классом по отдельности.

При описании класса атрибут может снабжаться опцией mandatory, означающей обязательность атрибута для данного класса. Отсутствие или значение false данной опции означает необязательность атрибута.

Необходимо заметить, что обязательность специально не оформляется в рамках протокола Google Protocol Buffers для поддержания возможности иметь различный набор обязательных атрибутов у различных классов.

Базовый класс

Как было описано ранее, в базовом классе определены следующие атрибуты:

  1. class_name
  2. created_by
  3. autogenerated_id
  4. ctime
  5. mtime

Описание в формате JSON:

{
  'description': [
    {
      'name': 'class_name',
      'value_type': 3,
      'mandatory': false,
      'multivalue': true,
      'protobuf_numbered_tag': 1
    },
    {
      'name': 'created_by',
      'value_type': 3,
      'mandatory': true,
      'multivalue': false,
      'protobuf_numbered_tag': 2,
      'read_only': true
    },
    {
      'name': 'autogenerated_id',
      'value_type': 0,
      'mandatory': true,
      'multivalue': false,
      'protobuf_numbered_tag': 3,
      'read_only': true
    },
    {
      'name': 'ctime',
      'value_type': 1,
      'mandatory': true,
      'multivalue': false,
      'protobuf_numbered_tag': 4,
      'read_only': true
    },
    {
      'name': 'mtime',
      'value_type': 1,
      'mandatory': true,
      'multivalue': false,
      'protobuf_numbered_tag': 5,
      'read_only': true
    }
  ]
}

При переводе описания атрибута на язык Google Protocol Buffers производятся следующие действия:

  1. name становится строковым именем поля
  2. value_type становится типом данных поля, соответствие типов:
  3. значение mandatory и read_only становятся значениями одноименных опций
  4. множественные атрибуты становится repeated полями, единичные — optional
  5. значение protobuf_numbered_tag говорит само за себя

Таким образом описание базового класса в формате Google Protocol Buffers:

message BaseClass {
  repeated string class_name = 1 [(mandatory) = false];
  optional string created_by = 2 [(mandatory) = true];
  optional bool autogenerated_id = 3 [(mandatory) = true, (read_only) = true];
  optional int64 ctime = 4 [(mandatory) = true, (read_only) = true];
  optional int64 mtime = 5 [(mandatory) = true, (read_only) = true];
}

device — устройство

message DeviceClass {
  option (class_name) = "device";

  // человеко-читаемое название
  optional string title = 100;

  // операционная система, под управлением которой работает устройство
  optional int64 platform = 101;
  // версия операционной системы
  optional string platform_version = 102;

  // название модели устройства
  optional string model_name = 122;
  // версия модели(ревизия)
  optional string model_version = 123;
}

platform может принимать следующие значения:

платформа значение
Apple iOS 0
Apple OS X 1
Google Android 2
Microsoft Windows 3
Blackberry 4
Linux 5
Eltex NV100/101 6

Пример использования класса

application — приложение

message ApplicationClass {
  option (class_name) = "application";

  // человеко-читаемое название
  optional string title = 103;
  // идентификатор устройства, под управлением которого работает приложения
  optional string device_id = 104;
  // токен для отправки push-уведомлений
  optional string push_token = 114;
  // версия приложения
  optional string version = 120;
}

Пример использования класса

catalogue_item — элемент каталога

message CatalogueItemClass {
  option (class_name) = "catalogue_item";

  // идентификатор элемента каталога
  optional string catalogue_item_id = 106 [(mandatory) = true];
  // идентификатор каталога
  optional string catalogue_id = 107;
}

Пример использования класса

action — действие

message ActionClass {
  option (class_name) = "action";

  // время события, в UTC
  optional int64 action_time = 108 [(mandatory) = true];
  // идентификатор приложения, в котором случилось событие
  optional string application_id = 109;
}

time_limited_playback — воспроизведение медиа, ограниченного во времени

message TimeLimitedPlaybackClass {
  option (class_name) = "time_limited_playback";

  // позиция в воспроизводимом элементе, в миллисекундах
  optional int64 playback_position = 110 [(mandatory) = true];
  // приостановлено ли воспроизведение
  optional bool playback_stopped = 111 [(mandatory) = true];
}

bookmark — закладка

message Bookmark {
  option (class_name) = "bookmark";

  // заголовок закладки
  optional string bookmark_title = 112 [(mandatory) = true];
  // URL закладки
  optional string bookmark_url = 113 [(mandatory) = true];
}

Пример использования класса

location — геолокация

message Location {
  option (class_name) = "location";

  // идентификатор территории из Регистратуры
  optional int64 territory_id = 115;
  // идентификатор контрагента из Регистратуры
  optional int64 contractor_id = 116;
  // географические координаты: широта
  optional float latitude = 117;
  // географические координаты: долгота
  optional float longitude = 118;
}

Пример использования класса

subscription — подписка

enum EventType {
  // Событие от редакции
  EDITOR_CHOICE = 1;
  // Новый рип (для элемента каталога)
  NEW_RIP_AVAILABLE = 2;
}

message CatalogueItemSubscription {
  option (class_name) = "subscription";

  optional int64 event_type = 119;
}

Пример использования класса

storage_constraint — политика хранения элемента

message StorageConstraint {
  option (class_name) = "storage_constraint";
  // Длительность хранения в секундах
  optional int64 storage_time = 121;
}

Пример использования класса

credential — секретная фраза: PIN–код, пароль, токен

message Credential {
  option (class_name) = "credential";
  // Секретная фраза
  optional string secret_phrase = 124;
}

Пример использования класса

application_reference — ссылка на установку приложения

message ApplicationReference {
  option (class_name) = "application_reference";
  // идентификатор приложения
  optional string application_id = 125 [(mandatory) = true];
}

Пример использования класса

purchase — покупка пользователя

message Purchase {
  option (class_name) = "purchase";

  optional string store_id = 126 [(mandatory) = true];
  optional bytes purchase_info = 127 [(mandatory) = true];
}

message GooglePlayReceipt {
  // идентификатор пакета приложения
  optional string package_name = 1;
  // идентификатор продукта
  optional string product_id = 2;
  // токен покупки
  optional string token = 3;
}

message AppStoreReceipt {
  // идентификатор пакета приложения
  optional string package_name = 1;
  // информация о покупке, закодированная в base64
  optional string receipt_data = 2;
  // признак sandbox-окружения
  optional bool sandbox = 3;
}

message RobokassaReceipt {
  // идентификатор счета
  optional string invoice_id = 1;
}

Замечания:

Пример использования класса

Рекомендации по иерархии элементов

Рекомендованная иерархия элементов (первым указывается id элемента, во вложенных списках указываются дочерние элементы):

ВАЖНО: использование объявленных классов и рекомендуемой иерархии не по назначению может иметь негативные последствия при объединении данных пользователя. Если сценарий использования класса (-ов), не описан в данной документации, то необходимо обратиться к инженеру, сопровождающему документацию.

/devices

Хранит устройства пользователя. Может использоваться для организации проталкивающих уведомлений на устройства пользователя.

Дочерними выступают элементы класса device, дочерние элементы являются терминальными.

В качестве идентификаторов элементов выступают идентификаторы устройств.

/applications

Хранит приложения пользователя для указания их, например, в списке истории просмотра фильмов.

Дочерними выступают элементы класса application, дочерние элементы являются терминальными.

В качестве идентификаторов элементов выступают идентификаторы экземпляра инсталляции программы.

/history/playback/rips

Хранит историю воспроизведения рипов фильмов. Используется для удобства продолжения просмотра.

Дочерними выступают элементы классов action+time_limited_playback+catalogue_item.

В качестве идентификаторов элементов используются идентификаторы рипов (MediaPart::id в терминах Films API). Уточнения по атрибутам:

/history/playback/channels

Хранит историю воспроизведения телевизионных каналов.

Дочерними выступают элементы классов action+catalogue_item.

Именование элементов можно как делегировать серверу, так и использовать идентификаторы каналов из «Телепрограммы». Уточнения по атрибутам:

/history/locations

Хранит историю определения геолокации пользователя.

Дочерними выступают элементы класса location.

Используется сервером для таргетирования проталкивающих уведомлений по абонентам операторов и территориям. Создание идентификаторов элементов следует производить на стороне сервера.

/history/playback/telecasts

Хранит историю просмотров выпусков передач. Используется для удобства продолжения просмотра.

Дочерними выступают элементы классов time_limited_playback + catalogue_item.

В качестве идентификаторов элементов используются идентификаторы выпусков передач. Уточнения по атрибутам:

/favourites/tv/channels

Хранит список избранных телевизионных каналов.

Дочерними выступают элементы класса catalogue_item.

В качестве идентификаторов элементов следует использовать идентификаторы каналов из сервиса «Телепрограмма». Рекомендуется указывать в качестве каталога:

Если для канала с названием удалось определить идентификатор, то необходимо удалить существующий элемент и добавить новый с определенным идентификатором.

/bookmarks/playlists

Хранит закладки на пользовательские списки воспроизведения.

Дочерними выступают элементы класса bookmark.

Создание идентификаторов элементов следует производить на стороне сервера.

/subscriptions/catalogue

Хранит подписки пользователя на события медийного каталога.

Дочерними выступают элементы класса subscription и, возможно catalogue_item, если подписка привязана к элементу каталога.

Возможные виды подписок:

Отказ от подписки осуществляется удалением элемента с описанием подписки.

/cloud/telecasts

Хранит список выпусков телепередач из личного архива пользователя.

Дочерними выступают элементы классов catalogue_item и storage_constraint.

При добавлении элементов в личных архив допускается не указывать время хранения элементов при помощи атрибута storage_time. В этом случае сервер автоматически установит значение по умолчанию. Стоит учитывать, что максимальный период хранения может быть ограничен.

В качестве идентификаторов элементов каталога должны выступать идентификаторы выпусков телепередач.

Допускается добавление выпуска телепередачи, который еще не был полностью записан. В этом случае выпуск будет поставлен в очередь на добавление в облачное хранилище, после завершения записи выпуск автоматически будет скопирован в облачное хранилище.

Выпуск телепередачи не будет сохранен в следующих случаях:

/credentials/parental_control

Хранит список PIN–кодов родительского контроля.

Дочерними выступают элементы классов credential и application-reference (опционально).

Рекомендуется использовать автоматическую генерацию идентификатора элемента.

Класс application-reference используется для связывания PIN–кода с конкретной установкой приложения. Если связывание не требуется, то атрибуты данного класса и класс не указываются для элемента.

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

В случае наличия нескольких элементов клиент должен:

Отсутствие элементов по данному пути означает, что PIN–код родительского контроля не был получен ранее.

Рекомендуется хранить не более одного PIN–кода, привязанного к конкретной установке, т.е. при добавлении нового PIN–кода необходимо удалить старый.

/purchases

Хранит информацию о покупках, выполненных пользователем во внешних платежных системах.

Дочерними выступают элементы класса purchase. Необходимо использовать автоматическую генерацию идентификатора элемента.

При добавлении покупки сервер синхронно произведет автоматическую верификацию покупки, в случае негативного результата верификации (некорректная покупка) элемент не будет создан.

Элемент может быть удален автоматически по истечению срока действия покупки, если покупка ограничена сроком действия.

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

История версий

Изменения в версии 1.1

Изменения в версии 1.1.1 (Решение #TVREC–49)

Изменения в версии 1.1.2

Изменения в версии 1.1.3

Изменения в версии 1.1.4

Изменения в версии 1.2

Изменения в версии 1.3

Изменения в версии 1.3.1

Изменения в версии 1.3.2

Изменения в версии 1.4

Изменения в версии 1.5

Изменения в версии 1.6

Изменения в версии 1.7 (TVREC–425)

Изменения в версии 1.8

Изменения в версии 1.8.1

Изменения в версии 1.8.2 (TVREC–530)

Изменения в версии 1.8.3

Изменения в версии 1.9.0 (TVREC–629)

Изменения в версии 1.10.0 (TVREC–652)

Изменения в версии 1.11.0 (TVREC–650)

Изменения в версии 1.11.1

Изменения в версии 1.11.2

Изменения в версии 1.11.3

Изменения в версии 1.12.0

Изменения в версии 1.12.1

Изменения в версии 1.13 (TVREC–1047)

Изменения в версии 1.13.1

Изменения в версии 1.13.2

Изменения в версии 1.13.3

Изменения в версии 1.13.4