Заметки по разработке на платформе BPM Online
Основные ссылки и ресурсы
Во первых, нужно обязательно зарегистрироваться на форуме http://www.community.terrasoft.ru/ Именно тут можно найти ответы на практически любые вопросы по системе.
Ну и конечно нужно изучить офф документацию для разработчиков.
Окружение
Если всего этого у вас нет, всегда можно просто взять бесплатное демо на 14 дней (при регистрации можно выбрать пакет team) и попробовать. Но в этом случае у вас не будет возможности полноценно отлаживать серверный код.
Отладка кода
Серверный код (c#)
Одним из ключевых моментов, который влияет на скорость и качество разработки конечно является возможность серверной отладки кода. По большому счету не буду переписывать оригинал, даю просто ссылку, где достаточно подробно расписано как все настроить. https://academy.terrasoft.ru/documents/technic-sdk/7-7-0/otladka-servernogo-koda
Клиентский код
К сожалению так и не удалось узнать, каким образом можно разрабатывать клиентский код, не вставляя
его в пакеты, а напрямую из ФС, но по моему это можно каким то образом сделать.
В результате, приходится добавлять debugger
в код, что бы автоматом вызывалась отладка.
Подробнее можно почитать тут.
Backup рабочей схемы
- нужно создать файл backup.bat в директории
<ДиректорияСайта>\Terrasoft.WebApp\DesktopBin\WorkspaceConsole\
- наполнить сайт содержимым
Terrasoft.Tools.WorkspaceConsole.exe -operation=SaveDBContent -contentTypes=Repository -userName=Supervisor -userPassword=Supervisor -workspaceName=Default -destinationPath=C:\backup
где
Supervisor
- логин и пароль администратора BPM. - выполнить созданый файл через командную строку
Примеры работы с серверным кодом (написание WCF сервисов)
Получение данных
var userConnection = (UserConnection)HttpContext.Current.Session["UserConnection"];
// Создание экземпляра запроса EntitySchemaQuery с корневой схемой "City".
var esqQuery = new EntitySchemaQuery(userConnection.EntitySchemaManager, "Property");
// Добавление в запрос колонки с наименованием страны, которой принадлежит город.
esqQuery.AddAllSchemaColumns();
esqQuery.AddColumn("PropertyType.Name");
esqQuery.AddColumn("City.Name");
// Выполнение запроса к базе данных и получение объекта с заданным идентификатором.
var collection = esqQuery.GetEntityCollection(userConnection);
var entity = esqQuery.GetEntity(userConnection, new Guid(id));
Важно! - что бы получить значение колонки из связаной таблицы, нужно обратиться к ней
вот таким образом: ИмяСушности_ИмяКолонки
. Например:
item.GetColumnValue("RegionId").ToString();
item.GetColumnValue("Region_Name").ToString();
Изменение сущностей
// Выполнение запроса к базе данных и получение объекта с заданным идентификатором.
var entity = esqResult.GetEntity(userConnection, new Guid(id));
entity.SetColumnValue("Link", link);
entity.SetBytesValue("Data", new byte[] { });
return entity.Save();
Способ конвертации сущности terrasoft в свой объект
private T convertObject<T>(Entity entity)
{
var json = Entity.SerializeToJson(entity);
dynamic a = JsonConvert.DeserializeObject(json);
var b = JsonConvert.SerializeObject(a.Entity);
return JsonConvert.DeserializeObject<T>(b);
}
Пример рабочего сервиса
using System.ServiceModel;
using System.ServiceModel.Web;
using System.ServiceModel.Activation;
using System.Configuration;
using System;
using System.IO;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using Terrasoft.Common;
using Terrasoft.Core;
using Terrasoft.Core.DB;
using Terrasoft.Core.Entities;
using System.Web;
using Websharks.Loader;
namespace Terrasoft.Configuration.CustomConfigurationService
{
// Класс сервиса помечен обязательными атрибутами [ServiceContract] и
// [AspNetCompatibilityRequirements] с параметрами.
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class Loader
{
// Метод сервиса помечен обязательными атрибутами [OperationContract] и
// [WebInvoke] с параметрами.
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json)]
public LoadResult LoadPhoto(string photoId)
{
var errorMessage = "";
try
{
var appSettings = ConfigurationSettings.AppSettings;
var loader = new Websharks.Loader.Loader(
appSettings.Get("AWSS3BucketName"),
appSettings.Get("CloudFrontDomainName")
);
var result = loader.LoadPhoto(GetFileFromDb(photoId), photoId, appSettings.Get("PhotoLoaderwaterMarkPath"));
if (result != null)
{
var link = result.ImageUrl;
if (SetFileLink(photoId, link))
{
return result;
}
}
}
catch (Exception s3Exception)
{
errorMessage = s3Exception.Message;
}
// fail result
return new LoadResult()
{
Error = true,
ErrorMessage = errorMessage
};
}
private Stream GetFileFromDb(string id)
{
var userConnection = (UserConnection)HttpContext.Current.Session["UserConnection"];
// Создание запроса к схеме City, добавление в запрос колонки Name.
var esqResult = new EntitySchemaQuery(userConnection.EntitySchemaManager, "PropertyGalleryImage");
esqResult.AddColumn("Data");
// Выполнение запроса к базе данных и получение объекта с заданным идентификатором.
var entity = esqResult.GetEntity(userConnection, new Guid(id));
var value = entity.GetBytesValue("Data");
return new MemoryStream(value);
}
private bool SetFileLink(string id, string link)
{
var userConnection = (UserConnection)HttpContext.Current.Session["UserConnection"];
// Создание запроса к схеме City, добавление в запрос колонки Name.
var esqResult = new EntitySchemaQuery(userConnection.EntitySchemaManager, "PropertyGalleryImage");
esqResult.AddAllSchemaColumns();
// Выполнение запроса к базе данных и получение объекта с заданным идентификатором.
var entity = esqResult.GetEntity(userConnection, new Guid(id));
entity.SetColumnValue("Link", link);
entity.SetBytesValue("Data", new byte[] { });
return entity.Save();
}
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json)]
public string RemovePhoto(string photoId)
{
try
{
var appSettings = ConfigurationSettings.AppSettings;
var loader = new Websharks.Loader.Loader(
appSettings.Get("AWSS3BucketName"),
appSettings.Get("CloudFrontDomainName")
);
return loader.RemovePhotos(new List<string> {
photoId + ".jpg", photoId + "_thumb.jpg"
}.ToArray()).ToString();
}
catch (Exception s3Exception)
{
return s3Exception.Message;
}
}
}
}
Доступ по данного сервиса после установки будет следующим: http://exmaple.com/0/rest/ServiceName/MethodName
Важно! Доступ до сервисов осуществляется ТОЛЬКО если пройдена процедура авторизации, соответсвенно для того, что бы взаимодействовать со своими сервисами извне, требуется первым делом авторизоваться в системе и далее уже осуществлять запросы, предоставляя авторизационные куки. Вот небольшой пример, написаный с использованием yii2-browser.
<?php
/**
* Created by PhpStorm.
* User: User
* Date: 20.07.2016
* Time: 15:31
*/
namespace common\components;
use yii\base\Component;
use yii\httpclient\Client;
use yii\web\Cookie;
class TerrasoftODataClient extends Component
{
public $domain;
public $userName;
public $userPassword;
protected $_entityServiceUri;
protected $_authServiceUri;
/**
* @var Client
*/
protected $_client;
protected $_autoCookies;
public function init() {
$this->_entityServiceUri = sprintf('http://%s/0/ServiceModel/EntityDataService.svc/', $this->domain);;
$this->_authServiceUri = sprintf('http://%s/ServiceModel/AuthService.svc/Login', $this->domain);
$this->_client = new Client();
}
public function authorize() {
$response = $this->_client->createRequest()
->setMethod('POST')
->setFormat(Client::FORMAT_JSON)
->setUrl($this->_authServiceUri)
->setHeaders([
'ContentType' => 'application/json',
'Accept' => 'application/json'
])->setData(
[
'UserName' => $this->userName,
'UserPassword' => $this->userPassword,
'TimeZoneOffset' => '-180'
]
)->send();
if ($response->statusCode == 200 && $response->cookies->get('.ASPXAUTH') instanceof Cookie) {
$this->_autoCookies = $response->cookies;
return true;
}
return false;
}
public function getContacts()
{
$url = $this->_constructSelectUrl('ContactCollection', ['Id', 'Name']);
$data = $this->_makeDataResponse($url);
return $data;
}
private function _constructSelectUrl($collection, array $selectFields)
{
return $this->_entityServiceUri . $collection . '?select=' . implode(',', $selectFields);
}
private function _makeDataResponse($url) {
if ($this->_autoCookies) {
$response = $this->_client->createRequest()
->setMethod('GET')
->setUrl($url)
->setCookies($this->_autoCookies)
->setHeaders(['ContentType' => 'application/json'])
->send();
if ($response->getIsOk()) {
return $response->data;
}
}
return false;
}
}