- Plugin basics
- Introduction
- Platform overview
- Plugin guides
- First UI package
- Package registration
- First Backend package
- Portal package creation
- REST API
- REST API using
-
Request Formats
- HTTP GET: Getting a list of elements of type Entity
- HTTP GET: Getting a specific Element of the Entity Type
- HTTP PUT: Editing an Entity Type Element
- HTTP POST: Creating a new Element of Entity Types
- HTTP POST: Doing an Action for Element of Entity Types
- HTTP POST: Creating an new element Sub-Entity of Types
- HTTP DELETE: Deleting the element of Entity Types
- HTTP PATCH: Partially editing the element of Entity Types
- Handling Errors When Calling HTTP Methods
Instruction: "How to create plugin project"
Для розробки знадобиться IDE з підтримкою написання коду на мові програмування C#. Для написання даної інструкції використовувалась VisualStudio.
Також необхідна наявність dotnet CLI
Якщо Ви використовували вже плагіни платформи, інструкція щодо створення плагінів на базі
iBackendPlugin
доступна за посиланням
Implementation
Платформа MEF.DEV підтримує архітектурний стиль розробки REST, де Ви можете створювати методи задля підтримки Ваших вимог - важливо тільки дотримуватись мінімальних правил. Реалізація методу повинна бути позначена атрибутами які є підкласом HttpMethodAttribute
та опційно атрибутом RouteAttribute
у разі якщо методів одного типу є декілька.
Важливо розуміти, що платформа надає спрощений доступ до частини HttpContext
через надання можливості реалізації загально відомих свойств, а саме HttpRequest
та HttpResponse
, які надають доступу до headers, query параметрів та інше. Також імплементуючи властивість IApiContext ми отримуємо доступ до контексту платформи, отримуючи інформацію щодо конфігурації плагіна, користувача, тенанта та інших доступних у тенанті сервісів (відправка повідомлень, тощо)
Для того щоб платформа MEF.DEV знала який клас має використовуватись для десералізації запитів використовується атрибут ExportAttribute
, який позначає клас як експортований (для прикладу restresource
нижче).
Важливо зазначити, що у процесі реєстрації плагіна у платформі використовується значення назви проєкту, а самє Assembly name (для прикладу TestPlugin
нижче).
Плагін можна створювати використовуючи цю інструкцію, або скориставшись уже готовим прикладом за посилання backend-template.
Project Creation
Для початку, потрібно створити новий проект. Вибираємо Create a new project
Також, вибрати Class library та мова програмування С#
Потрібно налаштувати конфігурацію нового проекту:
- Вказати Project Name
- Шлях, куди буде збережено Location
- Та поставити галочку у Checkbox для того, щоб зберігати solution у тій же папці що і проект
У додатковій інформації, рекомендовано вибрати версію .NET(Long Term Support)
Creating the Plugin Class
Додавання Class
Реалізація плагіну може включати декілька класів, які експортуються, тим самим надаючи окремі ендпоінти
namespace TestPlugin;
public class RestResource
{
}
Додавання NuGet залежності та атрибуту Export.
Шукаємо розширення MEF.DEV.Common.Plugin
та інсталюємо.
using System.Composition;
using UCP.Common.Plugin;
namespace TestPlugin;
[Export("restresource", typeof(IControllerPlugin))]
public class RestResource
Імплементація членів інтерфейсу
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
public class RestResource : IControllerPlugin
{
private HttpRequest _request;
private HttpResponse _response;
private IApiContext _apiContext;
public HttpRequest Request
{
get => _request;
set => _request = value;
}
public HttpResponse Response
{
get => _response;
set => _response = value;
}
public IApiContext ApiContext
{
get => _apiContext;
set => _apiContext = value;
}
}
Створення методу create-item
Важливо звернути увагу, що реалізація моделі запиту та відповіді можє бути довільна
[HttpGet, Route("{id}")]
public DataResponseModel GetItem([FromRoute] long id)
{
// How to get service configuration
var configProvider = _apiContext.ServiceProvider().GetService<IControllerPluginConfigProvider>();
var configuration = configProvider!.Get<IConfigurationRoot>();
return new DataResponseModel
{
Id = id,
Name = configuration?.GetSection("myurl").Value,
IsComplete = true
};
}
[HttpPost, Route("create-item")]
public object CreateItem(
[FromHeader] string lastModified,
[FromQuery] string lang,
[FromBody] DataRequestModel model
)
{
return new List<DataResponseModel>
{
new()
{
Name = model.Name,
LastModified = lastModified,
Lang = lang
}
};
}
public class DataRequestModel
{
public long Id
{
get; set;
}
public string Name
{
get; set;
}
}
public class DataResponseModel
{
public long Id
{
get; set;
}
public string Name
{
get; set;
}
public bool IsComplete
{
get; set;
}
public string LastModified
{
get; set;
}
public string Lang
{
get; set;
}
}
Populating the Swagger Specification
Цей етап є необов'язковим, але він потрібен для генерацію опису документації за допомогою Swagger
Активація Documentation file чекбокс
Втановіть галочку у чекбокс , щоб згенерувати файл, який міститиме API документацію
Або за допомогою коду:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
Заповнення основної інформації плагіну
Або ж за допомогою коду:
<PropertyGroup>
<Title>Todo title</Title>
<Version>1.0.0.1</Version>
<Company>Author</Company>
<Product>Todo API</Product>
<Description>Todo description</Description>
</PropertyGroup>
Заповнення інформації щодо методів
Щоб зв'язати моделі із відповідями методів додайте атрибути «Consumes» та «Produces»
[ProducesResponseType(typeof(List<DataRequestModel>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(DataResponseModel), StatusCodes.Status500InternalServerError)]
// [Consumes("application/json")] // http REQUEST content-type, application/json by default
// [Produces("application/json")] // http RESPONSE content-type, application/json by default
Додавання опису метода та прикладів використовуючи XML коментарі
Документація, яка відображатиметься при генерації
/// <summary>
/// Create Todo item short description
/// </summary>
/// <remarks>
/// Create Todo item long description
/// </remarks>
/// <param name="id"></param>
/// <param name='model'
/// examples='{
/// "UNKNOWN_CONTEXT": {
/// "summary": "Unknown Service Context",
/// "description": "The request failed completely due to an unknown service context value",
/// "value": {
/// "cause": "CHARGING_FAILED",
/// "title": "Incomplete or erroneous session or subscriber information",
/// "invalidParams": [
/// {
/// "param": "/serviceRating/0/serviceContextId",
/// "reason": "unknown context"
/// }
/// ]
/// }
/// },
/// "UNKNOWN_RESPONSE_CODE": {
/// "summary": "Unknown Response Code",
/// "description": "Internal Error",
/// "value": "405"
/// }
/// }'
/// >
/// DataRequestModel model for Create item
/// </param>
/// <param name="lastModified"></param>
/// <param name="lang"></param>
/// <response code='200'
/// example='{
/// "id": 1,
/// "name": "walk dog",
/// "isComplete": true
/// }'
/// >
/// Success
/// </response>
/// <response code='500'
/// examples='{
/// "UNKNOWN_CONTEXT": {
/// "summary": "Unknown Service Context",
/// "description": "The request failed completely due to an unknown service context value",
/// "value": {
/// "cause": "CHARGING_FAILED",
/// "title": "Incomplete or erroneous session or subscriber information",
/// "invalidParams": [
/// {
/// "param": "/serviceRating/0/serviceContextId",
/// "reason": "unknown context"
/// }
/// ]
/// }
/// },
/// "UNKNOWN_RESPONSE_CODE": {
/// "summary": "Unknown Response Code",
/// "description": "Internal Error",
/// "value": "405"
/// }
/// }'
/// headers='{
/// "Last-Modified":{
/// "description": "",
/// "schema": {
/// "type": "string",
/// "example": "2019-06-09T15:56:13.8498013Z"
/// }
/// }
/// }'
/// >
/// Error
/// </response>
Plugin Configuration
Цей етап є необов'язковим, але він потрібен для отримання дінамичної (в залежності від тенанту) конфігурації плагіна - Ви маєте реалізувати клас IPluginConfig
, наприклад:
using System.Composition;
using UCP.Common.Plugin.Models.Config;
namespace TestPlugin;
[Export("config", typeof(IPluginConfig))]
public class ConfigPlugin : IPluginConfig
{
public static class Keys
{
internal static string Connection = "Connection";
internal static string UiParameters = "UiParameters";
internal static string Logic = "Logic";
internal static string Report = "Report";
}
private readonly Dictionary<string, IEnumerable<PluginConfigSetting>> _configDictionary = new()
{
{ Keys.Connection, GetConnectionSection() }
};
public IDictionary<string, IEnumerable<PluginConfigSetting>> Get()
{
return _configDictionary;
}
public IDictionary<string, IEnumerable<PluginConfigSetting>> Set(IDictionary<string, IEnumerable<PluginConfigSetting>> config)
{
throw new NotImplementedException();
}
private static IEnumerable<PluginConfigSetting> GetConnectionSection()
{
yield return new PluginConfigSetting()
{
SettingType = PluginConfigSettingType.LongText,
Name = "ExampleName",
Value = @"{
""ConnectionStrings"": {
""ConnectionString"": ""Server=sqlserver;Database=database;User ID=userid;Password=password;Trusted_Connection=No"",
},
""DebugLevel"" : ""Trace""}"
};
yield break;
}
}
Package Build
Робимо portable
збірку та публікацію пакету із ключем self-contained
як показано на скріншоті нижче або використовуючи приклад команди нижче:
dotnet publish -c Release -r portable -p:PublishDir=bin\Publish\net6.0 --no-self-contained
Package Registration
Переходимо на сторінку створення плагіну (якщо це первічне створення плагіну) або переходимо до наступного пункту.
Вона знаходиться в пункті меню Плагіни
. Після чого, на сторінку створення плагінів.
Аліас - назва предметної області плагіну. Ім'я - назва плагіну. Вводимо ці дані, для нашого прикладу це значення test
для аліасу та portal-test
у якості назви. Далі переходимо до вибору типу. У платформі існує 4 основних типи плагінів. Про відмінності між ними написано у блоці допомоги. Зараз нас цікавить тип Service
- плагін, що містить лише API складову, без користувацької конфігурації. Вибираємо його.
Після вибору у нас активувався Backend
блок. Він містить лише одне поле PluginMefName
. Це є назва нашого проекту. Вводимо назву з репозиторію, у цьому прикладі це TestPlugin
, після чого нажимаємо кнопку Зберегти.
Uploading Package Version
Для завантаження готового ZIP-архіву плагіну на платформу mef.dev technical preview, необхідно, на сторінці конфігерації в блоці Backend натиснути кнопку Завантажити нову версію.
Після завантаження, в дропдауні необхідно вибрати необхідну версію та натиснути кнопку Зберегти
Також, альтернативою є завантаження плагіна за допомогою API-методу publish, який надає платформа:
curl --location 'https://preview.mef.dev/api/v2/plugins/<alias>/<PluginMefName>/publish' \
--header 'Authorization: Basic userpass' \
--form 'file=@"/local-path/to/file"' \
--form 'updateVersion="true"' \
--form 'updateTenantVersion="true"'
Важливо зауважити, що якщо ви використовуєте метод API publish, Вам потрібно додати обов’язковий файл metadata.json
із прикладом вмісту нижче:
{
"name":"TestPlugin Name",
"serviceType":"API",
"description":"This TestPlugin is used for tutorial goals under MIT license.",
"dependencies":[],
"config":{
"routesUI":[]
},
"externalUrl":"https://opensource.org/license/mit",
"configuration":null
}
Існує кілька додаткових файлів для цього способу публікації, які відповідають вимогам маркетплейсу платформи (https://preview.mef.dev/store), а саме:
description.html
зрозумілий опис вашого плагінаsmall.png
зображення вашого плагіна у візуалізації залежностейstandard.png
квадратне зображення вашого плагіна на маркетплейсі платформи
Ці файли потрібно додати до проекту з додатковою властивістю copy to output directory = copy if never
Package Dry run
Провіряти роботу API плагінів можна любою програмою-сніфером. В данному випадку використовуватиметься Postman.
Для відправлення запитів потрібно авторизуватися – зазвичай використовується схема авторизації із токеном користувача в платформі, алє для тестування ми застосуємо Basic Auth
. Необхідну пару логін-пароль доступу до API можна створити в розділі SETTINGS \ CREDENTIALS свого профілю, куди можна потрапити натиснувши на іконку користувача у верхньому правому кутку і вибравши пункт меню SETTINGS. Після натискання на кнопку ADD
Ви зможете задати логін користувача і пароль для авторизації Basic Auth
Basic Health Check
В межах платформи існує endpoint для перевірки працездатності плагіна:
https://preview.mef.dev/api/v2/<alias>/plugins/<PluginMefName>/version.json?detaillevel=detailed
У випадку схожого результату, важ плагін успішно завантажений на платформу, та готовий до роботи.
Sending Requests to the Plugin
Надсилання запитів до плагіна продемонструємо на прикладі GET запитів.
Для надсилання запитів використовується шаблон:
https://preview.mef.dev/api/v2/<alias>/<Export Name>
При цьому в тіло запиту можна добавляти будь-які параметри\хедери\поля, проте варто відобразити їх у вхідній моделі плагіна. Приклад запиту
curl --location --request GET 'https://preview.mef.dev/api/v2/test/restresource/1' \
--header 'authorization: Basic userpass' \
--header 'Content-Type: application/json'
curl --location --request POST 'https://preview.mef.dev/api/v2/test/restresource/create-item' \
--header 'authorization: Basic userpass' \
--header 'Content-Type: application/json' \
--data '{
"name": "walk dog"
}'
Useful Links:
Посилання на GitHub репозиторій із backend-plugin-example: https://github.com/mef-dev/tutorial-backend-plugin
Реалізацію використання бекенд плагіну із Front-плагіна можна побачити в Ангуляр-аплікаці tutorial-ui-plugin.