Меню

Использование Media Links в SAP Gateway

|

Введение

При реализации мобильных и web-приложений часто возникает необходимость обмениваться с Корпоративной Информационной Системой (EIS) не только бизнес-данными, но и так называемыми медиа-ресурсами (Media resource). Под медиа-ресурсами понимают неструктурированные (бинарные) данные или потоки данных, такие как документы, видео и аудио ресурсы.

Примеров такой необходимости масса, труднее найти пример, когда работа с бинарными файлами не требуется. Так, например, при работе с договором на мобильном устройстве вместе с бизнес-данными пользователь хотел бы иметь доступ к скан-копии этого договора, а при выставлении счета на оплату возможность передать его клиенту в формате PDF. Или пример из другой области: обходчик, производя осмотр технического объекта, хотел бы вместе с описанием неисправности прикрепить к сообщению фотографию, ведь не даром говорят - “лучше один раз увидеть, чем сто раз услышать”. Таким образом, обмен медиа-ресурсами между клиентом и сервером - это задача которую необходимо решать при реализации почти любого мобильного или web-приложения.

Протокол OData стал стандартом де-факто для взаимодействия мобильных и web-приложений с решениями SAP. Компонент SAP Gateway позволяет без затруднений создавать OData-сервисы, предоставляющие доступ как ERP, так и к BW и HANA-системам.

Данная статья показывает, как с помощью SAP Gateway реализовать OData сервис, осуществляющий передачу медиа-ресурсов от клиента к серверу и наоборот.

Любую технологию удобнее и интереснее разбирать на примере, и вот наш пример. Сотрудник технологической службы производит снятие показаний с контрольно-измерительного оборудования. С помощью мобильного приложения он передает информацию в SAP ERP, где создаются документы измерений. Контролирующие сотрудники имеют возможность получить доступ к показаниям через web-интерфейс. Для передачи информации используется OData-сервис, модель которого приведена на Рис. 1.

Рис. 1. Модель OData сервиса для работы с документами измерения

К данному процессу выдвинуто дополнительное требование: при снятии показаний сотрудник с помощью мобильного устройства должен сделать фото показаний прибора, и передать его вместе с показаниями. Фотография с помощью функциональности Generic Object Services (GOS) «прикрепляется» к документу измерения. В случае ошибки сотрудник может заменить или удалить фото. Контролирующие сотрудники имеют доступ к фотографиям через web-интерфейс.

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

Давайте посмотрим, как нам это сделать. Исходный код проекта относящийся к Media Links доступен на GitHub https://github.com/mobui/sap-media-links.

Media Links

Для работы с медиа-контентом протокол OData определяет специальный тип ресурсов, который называется Media Link. Этот вид ресурсов отличается от обычных тем, что помимо текстовых (структурированных) атрибутов, с ресурсом ассоциировано ещё некое бинарное содержание (Media Resource). Это содержимое может быть отдано OData-сервисом либо как тело http-ответа, либо с помощью ссылки на внешний ресурс, по которой медиа-контент доступен.

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

Давайте посмотрим, как реализовать работу с Media Link используя возможности SAP Gateway. Для создания сервиса мы будем использовать SAP Gateway Service Builder как основной инструмент создания OData-сервиса.

В нашем примере медиа-файлы связаны с документами измерения. Для того, чтобы управлять этими файлами нам необходимо создать новый тип сущности, назовем его MesuringDocumentBLOB. “Приспособить” к работе с медиа-контентом непосредственно MesuringDocument нельзя. Сделав этом, мы потеряем возможность полноценно работать с ресурсом (см. операцию создания). MesuringDocumentBLOB мы ассоциируем с MesuringDocument используя кардинальность много к одному (m:1). Это даст возможность прикреплять несколько файлов к одному документу измерения (Рис. 2).

Рис. 2 Новый тип сущности

Ключом новой сущности будет номер точки измерения - DocumentID и имя файла приложения – Filename (Рис. 3). Никаких дополнительный ограничений, по сравнению с обычными сущностями, на ключевые поля Media Link не накладывается.

Рис. 3 Поля типа сущности MesuringDocumentBLOB

Далее нам понадобятся два служебных поля, у меня это: Mimetype (выделено красным) и SourceURI (выделено зелёным); эти поля необходимы SAP Gateway, чтобы заставить работать данный вид сущности как Media Link.

  • Mimetype - предназначено для хранения mime-type (RFC2045) медиа-контента, ассоциированного с ресурсом. Поле обязательное, без него вообще не получится реализовать Media Link.
  • SourceURI - необязательное поле, представляет собой ссылку на внешний ресурс. Если вы не планируете хранить медиа-контент на внешних серверах, данное поле вообще можно не создавать.

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

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

Для того, чтобы ускорить и упростить создание нового типа сущности, удобнее сначала создать в ABAP-словаре структуру (Рис. 4), описывающую поля будущей сущности, а затем воспользоваться функцией импорта.

Рис. 4 Структура для бедующего типа сущности

Обратите внимания на типы служебных полей, они должны быть совместимы с типом Edm.String.

Сейчас MesuringDocumentBLOB - это просто обычный тип, ничем не отличающийся от других. Для того, чтобы он стал Media Link, нужно сделать совсем немного - установить индикатор Media (Рис. 5).

Рис. 5 Установка индикатора Media

Также необходимо внести небольшое изменение в метод define-класса *mpc_ext (Рис. 6), генерирующий Service Metadata Document. В этом методе мы присвоим атрибутам Mimetype и SourceURI те роли, для которых мы их и создавали. Атрибут Mimetype будет содержать тип контента (напомню, это обязательно). Атрибут SourceURI используется для ссылки на внешний контент.

Рис. 6 Назначение ролей полям Mimetype и SourceURI

Теперь, когда новый тип сущности готов, создаем ассоциацию, связывающую его с типом сущности, представляющим документ измерения. После этого OData-сервис примет вид, приведенный на Рис. 7.

Рис. 7 OData сервис с новым типом сущности

Получив Service Metadata Document ($metadata) этого сервиса, мы увидим в нём новый тип сущности MesuringDocumentBLOB с атрибутом HasStream, которому присвоено значение true (Рис. 8). Клиент получивший такой $metadata поймет, что MesuringDocumentBLOB имеет бинарное содержимое и является Media Link.

Рис. 8 Service Metadata Document

HasStream появилось благодаря индикатору Media; на что повлияют Mimetype и SourceURI мы увидим ниже.

Как и для любого другого типа сущности, нам необходимо реализовать методы /iwbep/if_mgw_appl_srv_runtime~get_entity и /iwbep/if_mgw_appl_srv_runtime~get_entityset, предназначенны для получения ресурсов с помощью GET-запросов. Реализация этих методов состоит из поиска документов измерений, удовлетворяющих критериям запроса, получения списка файлов, приложенных с помощью GOS к документу измерения и заполнению атрибутов сущности. Никаких бинарных данных здесь получать и передавить не нужно. Необходимо только правильно заполнить атрибуты сущности, особенно Mimetype и SourceURI. Если внешний ресурс для предоставления контента не используется, SourceURI заполнять не нужно, неверное заполнение этого поля осложнит работы с бинарным контентом сервиса.

Попробуем выполнить GET запрос к нашему (OData-сервис) сервису исходя из следующих предпосылок. В системе SAP ERP имеется документ измерения 8167 и нему прикреплен файл, фотография в формате jpeg - example.jpg (Рис. 9).

Рис. 9 GET запрос к MediaLink

Ответом сервиса будет информация о ресурсе в формате Atom Pub. Как видим, появился новый тэг content с атрибутами type и src. Этот тэг как раз и говорит нам о том, что с данным ресурсом ассоциирован медиа-контент. Рассмотрим эти атрибуты подробнее:

  • type ‑ содержит mime-type медиа-контента, этот атрибут заполняется на основании атрибута Mimetype сущности MesuringDocumentBLOB (Рис. 4)
  • src ‑ содержит путь, по которому данный контент доступен. С заполнением атрибута немного сложнее, он заполняется на основании атрибута SourceURI. Если SourceURI пуст, содержимое атрибута формируется автоматически с учетом того, что бинарный контент будет отдан с того же сервера, что и OData сервис. Как мы увидим ниже, GET-запрос, отправленный на данный адрес, приведет к вызову специальных методов Data Provider Class. Этот пример изображен на Рис. 9. Если SourceURI заполнен, то это значение будет перенесено в атрибут src и будет использоваться для ссылки на внешние ресурсы (методы Data Provider Class не вызываются). Некорректное заполнение src может привести к тому, что бинарное содержимое будет недоступно.

Таким образом, запросив информацию о ресурсе, описывающем бинарное приложение к точке измерения, мы получили ссылку на содержимое файла. Это как раз то, что в REST называется HATEOAS (https://en.wikipedia.org/wiki/HATEOAS).

Можно попробовать получить бинарное содержимое, выполнив GET-запрос по указанному пути, но ждать чуда, конечно. не следует, ведь мы ничего еще не сделали для получения контента. Ответом будь статус 400 Bad Request и сообщение об ошибке Method 'GET_STREAM' not implemented in data provider class. Давайте перейдем к следующему шагу - реализации получения контента.

Получение медиа-контента

Прежде чем приступить к реализации, рассмотрим путь, по которому сервис предлагает «отдавать» нам контент (Рис. 9). Очевидно, это тот же самый путь к Media Link-ресурсу плюс /$value. Так будет всегда, это правило. То есть при работке с Media Link нам становится доступна еще одна операция – получение медиа-контента.

GET serviсe root uri/resource path/$value

Выполнив такой запрос к ресурсу, не являющемуся Media Link, мы получим ошибку (Рис. 10) 400 Bad Request с описанием The request URI is not valid. The segment before '$value' must be a Media Link Entry.

Рис. 10 Ошибка при запросе контента

Научим наш сервис отдавать медиа-контенты. Тут всё просто, достаточно переопределить и реализовать в классе *dpc_ext метод /iwbep/if_mgw_appl_srv_runtime~get_stream. Рассмотрим сигнатуру этого метода подробнее (Рис. 11).

Рис. 11 Сигнатура метода /iwbep/if_mgw_appl_srv_runtime~get_stream

Сейчас нам интересны три параметра:

1. Название типа сущности, для которого нужно получить бинарное содержимое - MesuringDocumentBLOB

2. Ключи, которые идентифицируют сущность, в нашем случае - номер точки измерения и название файла

DocumentId

8167

Filename

example.jpg

3. А вот это уже экспорт-параметр и именно, с помощью него мы будем передавать бинарный контент клиенту. Хотя этот  параметр и объявлен как ref to data, система ждёт от нас не любой ссылки на любые данные, она ждёт от нас ссылки на переменную типа /iwbep/if_mgw_appl_types=>ty_s_media_resource (Рис. 12). Такая  переменная - это структура, состоящая из двух полей: mime_type и value. Первое поле должно содержать mime-type контента, второе сам контент.

Рис. 12 Тип /iwbep/if_mgw_appl_types=>ty_s_media_resource

Реализуем описанный метод (Рис. 13).

Рис. 13 Реализация метода /iwbep/if_mgw_appl_srv_runtime~get_stream

Метод measuringdocum01_get_stream (исходный код см. на GitHub) - это обычное получение бинарного файла, прикреплённого к объекту SAP с помощью функциональности GOS. Всю информацию, необходимую для получения GOS-приложений можно найти в документации к классу cl_gos_api.

Вот теперь давайте попробуем получить медиа контент (для тестирования HTTP запросов я использую расширение для Google Chrome под названием postman - https://www.getpostman.com/). Выполним GET запрос (Рис. 14).

Рис. 14 Получение медиа контента

Результатом будет ответ со статусом 200 OK и телом в виде бинарного контента.

Добавление медиа-контента.

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

POST serviсe root uri/collection path

Для Media Link тип запроса и URL не меняются, но вот его содержимое становится другим, Если для обычных сущностей телом запроса являются данные в формате xml или json, то для Media Link телом являются бинарные данные. Но в этом случае возникает вопрос, как передавать данные для атрибутов записи: номер точки измерения, название и описание файла.

Для передачи этой информации согласно RFC5023 - The Atom Publishing Protocol 9.7 используется специальный заголовок запроса slug. Что именно должен содержать заголовок, решает разработчик сервиса.

В нашем случае,заголовок slug будет иметь следующий вид:

<номер точки измерения>;<имя файла>;<описание в случае необходимости>

Такую строку легко разобрать с помощью следующий ABAP-конструкции (Рис. 15)

Рис. 15 Разбор slug

Для указания типа контента (его mime-type), содержащегося в теле запроса, обязательно должен быть добавлен ещё один заголовок Content-Type.

Перейдем к реализации операции создания нового ресурса, содержащего медиа-контент в SAP Gateway. Теперь необходимо переопределить и реализовать метод /iwbep/if_mgw_appl_srv_runtime~create_stream класса *dpc_ext.

Рассмотрим сигнатуру этого метода (Рис. 16).

Рис. 16 Сигнатура метода /iwbep/if_mgw_appl_srv_runtime~create_stream

  1. Название типа сущности для которого нужно добавить бинарное содержимое – MesuringDocumentBLOB.
  2. Структура типа /iwbep/if_mgw_appl_types=>ty_s_media_resource (Рис. 12), содержащая mime-type (информация из заголовка Content-Type) и бинарный контент (тело запроса).
  3. Ключи, эта переменная всегда будет пустой, ключи при POST-запросе не передаются.
  4. Содержимое заголовка slug -  например 8167;test.png;Test.
  5. Экспорт-параметр. Структура с атрибутами MesuringDocumentBLOB должна быть заполнена в процессе обработки запроса.

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

После того как реализация завершена, выполним запрос для добавления нового бинарного приложения к документу измерения (Рис. 17)

Рис. 17 POST запрос для создание нового Media Link

В результате, мы получили ответ со статусом 201 Created и с телом, содержащим данные созданной сущности, а в системе ERP к документу измерения 8167 было добавлено новое приложение (Рис. 18).

Рис. 18 Новое приложение к документу измерения

Изменение

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

PUT serviсe root uri/resource path/$value

Структура запроса в похожа на «смесь» GET и POST-запроса.

С одной стороны, запрос мы уже выполняем к конкретному ресурсу, а не к коллекции, как в GET-запросе, никакой Slug нам больше не нужен, ключи присутствуют в самом URL запроса.

С другой стороны, тело запроса является бинарным контентом, как в POST-запросе.

Но с запросами на изменения контента возникает своя сложность. Что делать, если пользователь не обладает информацией о текущем состоянии контента, если файл которой он пытается заменить уже давно изменён и не требует актуализации?

Чтобы избежать такой ситуации «придумали» так называемые условные запросы. Данные запросы содержат заголовок if-Modified-Since (RFC2616 14.25 If-Modified-Since) или If-Match (RFC2616 14.24 If-Match), передающий информацию о версии ресурса, которой располагает клиент. Если эта версия уже устарела, запрос на изменение отклоняется. Все PUT-запросы для протокола OData должны быть условными. Это касается не только Media Link, но и простых сущностей.

Для того, чтобы дать возможность OData-сервису изменять медиа-контент, необходимо переопределить и реализовать метод /iwbep/if_mgw_appl_srv_runtime~update_stream класса *dpc_ext.

Рассмотрим сигнатуру этого метода (Рис. 19).

Если хотите прочитать статью полностью и оставить свои комментарии присоединяйтесь к sapland

У вас уже есть учетная запись?

Войти