Технология APS: фронтенд контрольной панели и возможности JS SDK

    В прошлый раз мы рассказали об APS (Application Packaging Standard) — нашей открытой технологии интегрирования приложений в платформу по продаже облачных сервисов (SaaS marketplace) Odin Automation. Наша платформа связывает разработчиков и потребителей облачных сервисов через инфраструктуру крупных сервис-провайдеров (поставщиков телекоммуникационных и хостинг-услуг), одновременно предоставляя точку входа для конечных пользователей: контрольную панель или портал, с помощью которого можно создать сайт, настроить почту, купить антивирус или виртуальную машину в облаке. В этом посте мы более подробно остановимся на том, как устроен фронтенд контрольной панели и APS-приложений и какие возможности предоставляет APS JavaScript SDK.



    Контрольная панель и экраны приложений


    Для управления приложениями, докупки (upsell) и перекрестной продажи (cross-sell) cервисов конечные пользователи могут использовать контрольную панель. Она предоставляет общий интерфейс, в который разработчики APS-приложений встраивают свой пользовательский интерфейс.

    Каждый сервис-провайдер брендирует инсталляцию Odin Automation в свои цвета. Поэтому мы отказались от использования проприетарной разметки и используем популярную разметку Twitter Bootstrap с CSS-препроцессором LESS. Поскольку все приложения используют APS JS SDK, разработчику темы достаточно указать всего несколько параметров, чтобы получить оформление, соответствующее бренд-буку сервис-провайдера или реселлера.

    В поддержке мобильных устройств мы не стали ограничиваться адаптивной разметкой и сеткой из Bootstrap, которой может пользоваться разработчик приложения для определения размеров виджетов на различных устройствах, а пошли дальше. Некоторые сложные виджеты полностью меняют свое отображение на мобильных устройствах. Например, таблица отображается в виде плиток; а слайдер с ползунком, которым, очевидно, неудобно пользоваться на touch-устройствах, превращается в спиннер, состоящий из инпута и кнопок "+"/"-".



    Сама панель, и каждое приложение являются Single Page Applications. В простейшем случае каждое APS-приложение изолировано в своем IFrame, которые роутер контрольной панели не удаляет, а скрывает и показывает. Аналогично, внутри IFrame экраны при смене не удаляются, а прячутся и показываются. Iframe необходимы, чтобы изолировать приложения друг от друга, т.к. приложения пишут различные вендоры. При этом изоляция между экранами одного приложения слабее: каждый экран — это просто JavaScript-модуль, унаследованный от соответствующего класса, а его виджеты отображения помещаются в div. Таким образом, у нас получился SPA поверх SPA.

    Single Page Application


    Рассмотрим более подробно наш SPA. Каждое APS-приложение описывается специальным файлом APP-Meta. В частности, этот файл содержит описание всех экранов приложения и связанных с ними данных. Там же описываются взаимосвязи приложений. Например, если приложение А предоставляет визард, в который хочет встроиться приложение Б, то приложение А декларирует поддержку встраивания, объявляя так называемый placeholder, а приложение Б декларирует желание встроить свой экран в этот placeholder.

    <!—Приложение А --> 
    <wizard id="addUser" label="Add New Users" …> 
        <placeholder id="http://www.aps-standard.org/ui/service/suwizard.new/2"/> 
            … 
    </wizard> 
    
    <!—Приложение B --> 
    <view id="signupfilesharing" label="File Sharing" … > 
             <plugs-to id="http://www.aps-standard.org/ui/service/suwizard.new/2"/> 
    </view> 

    При этом placeholder не привязан к конкретному приложению. Несколько приложений может объявить один и тот же placeholder, и тогда экран приложения Б будет встроен во все эти приложения.

    Вернемся в браузер и поясним, как работает SPA на небольшом примере.

    1. Пользователь открывает в контрольной панели экран view-11 приложения A.
    2. Роутер контрольной панели создаёт IFrame и загружает в него стартовый bootstrapApp.html, общий для всех приложений. После чего подключает модуль view А1. Инстанс этого модуль останется в IFrame, даже если пользователь переключится на другой экран.
    3. В том же приложении пользователь переключается на экран view А2.
    4. Роутер подключает в IFrame модуль view А2. Теперь все переходы между этими экранами будут происходить в одном IFrame.
    5. Пользователь переключается на экран view В1 приложения В.
    6. Роутер создаёт новый IFrame и загружает в него bootstrapApp.html с исходным кодом view В1. Теперь этот код останется в IFrame, даже если пользователь переключится на другой экран.

    Дальше всё точно так же, как в приложении A.



    Остановимся подробнее на жизненном цикле экрана приложения. Он состоит из следующих фаз:

    • Инициализация (метод init). В ней приложение должно объявить виджеты и связать их с моделью.
    • Подготовка к показу (метод onShow). Здесь приложение может выполнить подготовительные действия, которые не требуют получения данных.
    • Показ (метод onContext). В этой фазе экран получил от контрольной панели данные, которые были заранее описаны в файле APP-Meta. Конечно, экран может сам сходить на сервер за данными, но мы советуем использовать декларацию, так как это позволяет экономить время загрузки. Дело в том, что для показа каждого экрана фронтенд делает запрос к серверу, потому что структура экранов могла изменится. Если данные были заранее описаны, то они будут получены в том же запросе. Если же экран сам пойдет за данными, то конечному пользователю придется ждать, пока будет получен первый запрос, а потом еще ждать второй и последующие. После того, как экран получил данные, он кладет их в модель, и виджеты меняют свое состояние.
    • Скрытие (метод onHide). Здесь приложение очищает виджеты и возвращает их в нейтральное состояние. Для этого у экрана есть специальный метод.

    Выше был описан простейший способ интеграции. Но бывают ситуации посложнее. Возьмём такой пример: есть dashboard-приложение А, и приложение Б хочет с помощью виджета выводить в А какую-то информацию. Для такой точечной интеграции мы разработали view-плагины. Встраивание view-plugin-ов полностью аналогично описанному выше механизму placeholder-ов. Чтобы сохранить изоляцию между А и Б, всё общение между ними осуществляется через медиатор. Это специальный объект, который содержит описание API view-плагина в виде JSON-схемы, и сначала проверяет плагин на наличие всех обязательных свойств и методов, а потом контролирует всё общение между экраном-хостом и view-плагином.



    Разберем на примере. Медиатор предоставляет данные о resourceUsage и кастомную операцию getWidget, которая принимает опциональный аргумент в виде булева значения:

    { 
        "properties": { 
            "resourceUsage": { 
            // type указывает на тип объектов, которые должны содержатся в поле resourceUsage 
            // URI-подобные типы описывают специальные APS ресурсы, которые хранятся в post-noSQL базе данных APS, о которой мы расскажем в следующих постах 
            "type": "http://aps-standard.org/types/core/subscription/1.0#SubscriptionResource", 
        } 
    }, 
    "operations": { 
        "getWidget": { 
             "parameters": { 
                "withData": { "type": "boolean", "required": false } 
             }, 
             "response": { 
        "type": "string", 
             "required": false 
             } 
        } 
    } 
    } 

    View-плагины могут предоставлять не только UI, но и логику. В этом случае в медиаторе размещается некий API, скрывающий логику из внешней системы, например, биллинг-системы. Приложение не должно знать о том, какая биллинг-система развернута у сервис-провайдера. Поэтому специфика работы с ней спрятана за унифицированный, описанный в стандарте API, а имплементация этого API в каждой конкретной системе делается в виде подключаемого view-плагина.

    SDK


    Первая версия APS JS SDK была разработана больше пяти лет назад и с тех пор непрерывно развивается вместе со стандартом APS. За основу был взят фреймворк Dojo. Сейчас это может показаться странным, но по меркам Web-мира это было целую эпоху назад. Тогда Angular только начинался, а React-а вообще еще не существовало.

    Что же нам понравилось в Dojo:

    • готовый загрузчик и модульная система на базе AMD;
    • встроенная поддержка классов с множественным наследованием;
    • имплементация промисов;
    • большое количество различных вспомогательных модулей;
    • продуманные API и развитая документация.

    Сейчас наш фреймворк предоставляет разработчикам APS-приложений следующие модули:

    • большое количество различных виджетов (spinner, slider, grid, password и т.д.);
    • модули для работы с данными (как клиентские, так и серверные хранилища) и модули для двухстороннего связывания данных и отображения;
    • различные вспомогательные модули: API для работы с биллинговыми системами, утилиты локализации и интернационализации, генератор паролей по заданной политике безопасности и многое другое.

    Также разработчики могут подключать сторонние библиотеки как напрямую в AMD-формате, так и в виде ES2015 модулей, которые будут преобразованы в AMD.

    Виджеты


    Виджеты — это «строительные блоки» пользовательского интерфейса. В APS JS SDK они логически отделены от HTML-представления и могут:

    • динамически менять значения своих свойств;
    • наследоваться друг от друга;
    • включать в себя другие виджеты на уровне шаблона.

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

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

    import Button from "aps/Button"; 
    var btn = new Button({ 
       id: "example1", 
       label: "I am simple button" 
    }); 

    Создания виджетов с помощью декларации. Иерархия виджетов и их свойства определяются в виде JSON-подобной структуры, которая передается в функцию load. Декларация каждого виджета представляет собой JavaScript-массив, который может содержать три элемента:

    • имя виджета;
    • (опционально) набор свойств виджета,
    • (опционально) массив, содержащий дочерние элементы.

    import load from "aps/load"; 
    load([ "aps/ProgressBar", { value: "35%" } ]); 

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

    load([ "aps/ProgressBar", { id: "myProgBar", value: 0 } ]) 
       .then(function(pb) { 
           pb.set("value", 41); 
       }); 

    При использовании load-a код получается более логичным и удобочитаемым: сначала идёт родительский виджет, а затем дочерние. Большие структуры можно разделить на секции и разложить в отдельные переменные с понятными названиями, а потом уже соединить в одной структуре.

    Работа с данными


    Очевидно, что одними виджетами при создании UI не обойтись — им нужны данные. Источники данных для виджетов бывают в виде модулей двух типов: модули типа Model и модули типа Store.

    Модули типа Model — набор модулей для двустороннего или одностороннего связывания виджетов и данных. С помощью метода at() выполняется связка с виджетом. Для отслеживания изменений в Model применяется метод watch(). Для работы со свойствами Model используются методы get() и set().

    Пример инициализации Model из JSON-представления:

    require([ 
       "dojox/mvc/getStateful", 
       ... 
       "aps/json!./newoffer.json" 
    ], function (getStateful, ..., newOffer) { 
       /* Declare the data source */ 
          var model = getStateful(JSON.parse(newOffer)); 
          ... 
    }); 

    Привязка Model к виджетам:

    var widgets = 
       ["aps/PageContainer", { id: "page"}, [ 
          ["aps/FieldSet", { title: true}, [ 
             ["aps/TextBox", { 
                id: "offerName", 
                label: _("Offer Name"), 
                value: at(model, "name"), 
                required: true 
             }], 
             ["aps/TextBox", { 
                label: _("Description"), 
                value: at(model, "description") 
             }] 
          ]], 
          ... 
       ]]; 
    load(widgets); 

    Модули типа Store предназначены для работы с различными источниками данных. Источники бывают локальными, когда все данные находятся на клиенте, и удаленными, когда данные находятся на бекенде. Так как удаленным источником обычно является APS-контроллер, то модуль для работы с ним обеспечивает передачу аутентификационной информации и поддерживает свойства, связанные со спецификой APS, например, apsType. Вне зависимости от типа источника данных взаимодействие с виджетами, отображающими данные, идет в одностороннем порядке. Для отражения изменений в виджетах необходимо явно вызывать обновление данных.

    Запросы к любым источникам данных в конечном итоге делаются с помощью Resource Query Language (RQL). RQL является языком запросов, разработанным для использования в URI, для работы с объектно-подобными структурами данных. Более подробно о нем мы расскажем в следующих постах.

    Пример объявления Store:

    import Store from  "aps/ResourceStore"; 
    var offerStore = new Store({ 
        apsType:    "http://aps-standard.org/samples/vpscloud/offer/1.0", 
        target:     "/aps/2/resources/" + aps.context.vars.cloud.aps.id + "/offers" 
    ... 
    }); 

    Привязка Store к виджету, отображающему таблицу:

    load(["aps/PageContainer", { id: "page" }, [ 
       ["aps/Grid", { 
          id:                "grid", 
          columns:   [ 
             { field: "offername",             name: "Name", type: "resourceName" }, 
             { field: "hardware.memory",       name: "RAM, MB" }, 
             ... 
          ], 
          store: offerStore 
       }, ... 
       ] 
    ]]); 
    

    Документация и песочница


    Наша платформа APS ориентирована на сторонних разработчиков, поэтому мы должны обеспечивать стабильность и простоту разработки. Без проработанной документации это было бы невозможно.

    Мы создали портал для разработчиков, на котором доступна вся необходимая документация по созданию UI с примерами кода. Это полноценный справочник: сначала даётся некое общее описание интерфейса, модуля или метода, а во вложенных уровнях — более подробная информация. При этом часть документации генерируется автоматически на основании текущего кода нашей платформы. Внутри описываются свойства, методы и возвращаемые значения.

    Ещё одной «фишкой» является песочница, интегрированная в портал для разработчиков. Попасть в неё очень просто: нажмите кнопку “Run demo”, которая есть в каждом примере кода:



    Наш APS Fiddle:

    • знает все API наших виджетов и умеет подсказывать названия свойств и сигнатуры методов;
    • позволяет сравнивать поведение кода в разных версиях стандарта APS;
    • умеет переключаться с мобильного представления на десктопное;
    • предоставляет ссылки на фрагменты кода, которые можно отправлять своим коллегам или службе поддержки (Share);
    • позволяет работать над кодом совместно (Collab);
    • может сгенерировать готовый файл с вашим кодом, словно это отдельный экран приложения, и этот файл можно сразу закидывать в реальный проект и тестировать.



    Подробное описание работы с песочницей доступно тут: Development Tools —> APS Fiddle.

    В заключение


    Мы предоставляем публичный API, от которого зависит работоспособность свыше 500 приложений с суммарной аудиторией в несколько миллионов пользователей. Это большая ответственность. Чтобы облегчить труд сторонних разработчиков и максимально упростить работу с нашей платформой, мы сделали подробную документацию и песочницу. А чтобы ненароком что-нибудь не сломать, мы обеспечили очень высокое покрытие кода тестами. Как мы этого добились — об этом читайте в следующем посте.
    Ingram Micro Cloud
    60,00
    Дистрибьюторская платформа для облачных сервисов
    Поделиться публикацией

    Похожие публикации

    Комментарии 0

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

    Самое читаемое