External WebAPI
The system includes a set of root services that are used for communication with the server on SDK level. The services mainly provide authorization, receiving system metadata, reading entity data, tracking modifications of entities on the server.
Apart from the root services, there are many functional services available on the server. They provide functions of the system’s modules such as creation and execution of tasks and calendar events.
The root namespace for all roots is http://www.demo.elma-bpm.com/API/Help
System root services
ELMA provides the root services for:
- Authorization - AuthorizationService
- Reading entity data - EntityService
- Reading entity headers - EntityHeadService
- Tracking entity modifications - EntityChangesService
- Sending and receiving files - FilesService
- Receiving system metadata – MetadataService
- Often you need to receive different types of data in a query. You can do that with BatchOperationService that allows executing several queries simultaneously. It implements the Query method of executing several queries to objects within one web-query, and it supports filtering by object properties too.
You can access the root services in 2 ways: via WCF HTTP and WCF SOAP. Here is how you can access either of them:
- WCF SOAP: the address is formed from the service name without the Service For AuthorizationService it would be~/API/Authorization (for example, http://demo.elma-bpm.com/API/Authorization)
- WCF HTTP: the address is formed similarly to SOAP but as root address, you should use ~/API/REST. For AuthorizationService it would be ~/API/REST/Authorization (for example http://bpm-demo.elma-bpm.ru/API/REST/Authorization/help)
- Also, for WCF HTTP, standard Help is generated in the .NET WCF format, available at ~/API/REST/<Service name>/Help, for AuthorizationService it would be ~/API/REST/Authorization/Help (for example http://demo.elma-bpm.com/API/REST/Authorization/Help).
In the WCF HTTP mode, all the services (except for MetadataService) support both XML and JSON data formats. The format is specified in the header:
- Accept: application/xml (or Content-Type: application/xml for a POSTrequest) – for exchange in XML format
- Accept: application/json (or Content-Type: application/json for a POST request) - for exchange in JSON format
The MetadataService service only supports the XML format for data exchange.
Authorization of requests
For security reasons, the system applies request authorization, which works as follows:
1. You use AuthorizationService
- The LoginWithBasic method authorizes you according to the basic protocol
- The LoginWithUserName method authorizes you by login and password
2. In the response, you receive the AuthTokenauthorization token (it is a string representation of the System.Guid structure)
- Later on, you will need to pass this token as the header of each request to methods (for WCF HTTP simply use the HTTP: AuthToken: ... headers). If the method requires authorization, it will be mentioned in the Help.
- Additionally, in the response, you will receive the SessionToken (see below), CurrentUserId, and its current locale Lang
Trusted applications and connection sessions
ELMA provides tokens ApplicationToken and SessionToken for external trusted applications. The external application token identifies a trusted application (e.g. ELMA for IPad, ElmaAgent, Outlook). Tokens have an expiration time. In the first authorization, you must pass the application token in a header: ApplicationToken: ...
You can set and adjust this token on ELMA server page ~/Security/PublicApplication
External application tokens are set up in the Administration section. There are also system tokens - their expiration time is set specifically for each application.
Session token – session here stands for a unique Application + User pair. The server uses the session token to identify this pair on the server. If a user authorizes for the first time and they already have a session token, they must pass it in the header: SessionToken: …. Server needs this token to track changes on the server linked to the client.
Creating a new external application and token
Let’s create an external application and a token for it. You can do it in ELMA online demo http://demo.elma-bpm.com. To add a token, go to Web Application – Administration -> System -> External Applications and click Add.
Fill out the form and click Save.
Go to the page of the application that you created and click Enable.
Now you can add the token. Open the Application Tokens tab – click Add Token – Set the expiration date – click Create token:
Now the application is ready for work.
WebAPI Help
The WebAPI Help generates automatically. It contains information about the available services, objects, enumerations and data types for properties. You can see it at ~/API/Help (for example, http://demo.elma-bpm.com/API/Help). Here you can find descriptions of all the available services, both root and functional, and their methods.
Let’s take a closer look at each section of the WebAPI Help.
List of available public web services
Features information about web services available to be called by external applications.
Each web service contains information about its methods. In addition, it provides information about the method’s parameters and return value.
Also, each service contains information about how the methods are called, via WSDL or Http requests.
Using WSDL
Using HTTP Requests
Go to the method’s description to see learn how to create requests and what response you will get.
List of object (entity) types
Provides information about all the system objects including processes.
Each type that you can see here contains information about its properties. The processes contain information about their context.
List of enumerations
Contains information about all the enumerations available in the system.
Each of the given enumerations stores information about its values.
List of data types for properties
Features information about data types IDs:
- Data type ID is a unique identifier of an object category. For the standard .Net objects, it is always different. For ELMA objects, it can be the same. Below you can see a list of main categories and their identifiers:
- Entity (72ed98ca-f260-4671-9bcd-ff1d80235f47)
- Project type (d1397b84-f5ab-4665-984f-fdeb8f77eb0b)
- Document type (a3a41ae4-30e2-4c46-bba2-ee55efdc7b90)
- Record card type (691ed407-4007-4530-834b-0c6a34bb9af1)
- Enumeration (849c1ac9-4d46-4194-8cbb-43f84adf9c17)
2. Data subtype ID is a unique identifier of each object type. It cannot be the same for various objects.
Example of using WebAPI
There are two ways of interacting with WebAPI: using WSDL services and using Http requests. Below you can see examples of minimal work with WebAPI for each option.
Using Http requests
For our example, we shall use a console application. To create it, open VisualStudio -> New project -> VisualC# -> ConsoleApplication -> Create.
Then connect the System.Net, System.IO and EleWise.ELMA.SDK namespace;
First of all, you need to receive the authorization token. For that, write the following code:
// Add the application token you created for the external applciation before
public static string ApplicationToken = "AC59057A6D8C13D938CC65DBD45B322874DB7918C5F08C4261D2078A1D11F74D87DEF8ACC1AFA812D4DC35C601E522A8B19BFF591C26B43FDEB7F4AF633CD18C";
static void Main(string[] args)
{
// Create web request
HttpWebRequest req = WebRequest.Create(String.Format("http://demo.elma-bpm.com/API/REST/Authorization/LoginWith?username=admin")) as HttpWebRequest;
req.Headers.Add("ApplicationToken", ApplicationToken);
req.Method = "POST";
req.Timeout = 10000;
req.ContentType = "application/json; charset=utf-8";
// Data for sending. We use it to pass the password (replace the empty string with the password)
var sentData = Encoding.UTF8.GetBytes("");
req.ContentLength = sentData.Length;
Stream sendStream = req.GetRequestStream();
sendStream.Write(sentData, 0, sentData.Length);
// Get response
var res = req.GetResponse() as HttpWebResponse;
var resStream = res.GetResponseStream();
var sr = new StreamReader(resStream, Encoding.UTF8);
var result = sr.ReadToEnd();
Console.WriteLine(result);
Console.ReadKey();
// Get necessary data out of response
var dict = new JsonSerializer().Deserialize(result, typeof(Dictionary<string, string>)) as Dictionary<string, string>;
var authToken = dict["AuthToken"];
var sessionToken = dict["SessionToken"];
{
"AuthToken":"e8a30aa3-875f-4887-918b-adc86318c5e4",
"CurrentUserId":"1","Lang":"en-US",
"SessionToken":"EA47A8222C78598256B3BC440A72D7D7D87FD5ABC52E410A334FDD5AC
811719EA3D2B09F21B4B966EB4BE41728C6435A20291369CAF91BE031D2A24571BA2A10"
}Here:
- AuthToken– authorization token (what you need to receive to work with ELMA)
- CurrentUserId– ID of the current authorized user
- Lang– the current user’s locale
- SessionToken – session token (it also required to ensure that there aren’t too many sessions on the server; it is passed together with AuthToken)
Once the authorization token is received, you can use it. For example, load a task. Use the following code:
public static void LoadTask(string authToken, string sessionToken)
{
var typeUid = "f532ef81-20e1-467d-89a4-940c57a609bc";
var entityId = "472";
HttpWebRequest taskReq = WebRequest.Create(String.Format("http://demo.elma-bpm.com/API/REST/Entity/Load?type={0}&id={1}", typeUid, entityId)) as HttpWebRequest;
taskReq.Method = "GET";
taskReq.Headers.Add("AuthToken", authToken);
taskReq.Headers.Add("SessionToken", sessionToken);
taskReq.Timeout = 10000;
taskReq.ContentType = "application/json; charset=utf-8";
var res = taskReq.GetResponse() as HttpWebResponse;
var resStream = res.GetResponseStream();
var sr = new StreamReader(resStream, Encoding.UTF8);
var myTask = sr.ReadLine();
Console.WriteLine(myTask);
Console.ReadKey();
}
Here:
- typeUid– UID of the Task
- entityId– Object ID
Result:
{"Items":
[
{
"Data":null,"DataArray":[],
"Name":"Id",
"Value":"472"
},
{
"Data":null,
"DataArray":[],
"Name":"TypeUid",
"Value":"298b2c71-619f-463c-95b2-8e029085680d"
},
{
"Data":null,
"DataArray":[],
"Name":"Uid",
"Value":"318dede0-78a3-4a80-aa0d-327291d1ce58"
},
{
"Data":null,
"DataArray":[],"Name":"Subject",
"Value":"My test task"
}
} ],
"Value":null }
We did not include the whole response because it is too long.
If you only need certain fields of an object, use the LoadTree function. To load several objects with one request, use the
Query (to load all the object fields) or QueryTree functions (to load certain object fields). Please see this article to learn more about these methods.
Below you can see an example of using the Query method.
public static void LoadTaskList(string authToken, string sessionToken)
{
var typeUid = "f532ef81-20e1-467d-89a4-940c57a609bc";
var eqlQuery = "";
var limit = "15";
var offset = "0";
var sort = "";
var filterProviderUid = "";
var filterProviderData = "";
var filter = "";
HttpWebRequest queryReq = WebRequest.Create(String.Format(@"http://demo.elma-bpm.com/API/REST/Entity/Query?type={0}&q={1}&limit={2}&offset={3}&sort={4}&filterProviderUid={5}&filterProviderData={6}&filter={7}",
typeUid, eqlQuery, limit, offset, sort, filterProviderUid, filterProviderData, filter)) as HttpWebRequest;
queryReq.Method = "GET";
queryReq.Headers.Add("AuthToken", authToken);
queryReq.Headers.Add("SessionToken", sessionToken);
queryReq.Timeout = 10000;
queryReq.ContentType = "application/json; charset=utf-8";
var res = queryReq.GetResponse() as HttpWebResponse;
var resStream = res.GetResponseStream();
var sr = new StreamReader(resStream, Encoding.UTF8);
var queryResult = sr.ReadLine();
Console.WriteLine(queryResult);
Console.ReadKey();
}
- typeUid – Uid of the Task type.
- eqlQuery – EQL query
- limit – number of received items
- offset – initial item
- sort – sorting
- filterProviderUid – Uid of the filtering provider
- filterProviderData – data for the filtering provider
- filter – property values for the entity filter
Now let’s complete the task that we loaded using the Load function. When completing a task, we will also leave a comment.
public static void ExecuteTask(string authToken, string sessionToken)
{
HttpWebRequest taskReq = WebRequest.Create("http://localhost:2016/API/REST/Tasks/Complete") as HttpWebRequest;
taskReq.Method = "POST";
taskReq.Headers.Add("AuthToken", authToken);
taskReq.Headers.Add("SessionToken", sessionToken);
taskReq.Timeout = 10000;
taskReq.ContentType = "application/json; charset=utf-8";
var request = @"
{
""Items"":
[
{
""Data"":null,
""DataArray"":[],
""Name"":""TaskId"",
""Value"":""1""
},
{
""Data"":
{
""Items"":
[
{
""Data"":null,
""DataArray"":[],
""Name"":""Text"",
""Value"":""Text for comment""
}
],
""Value"":null
},
""DataArray"":[],
""Name"":""Comment"",
""Value"":null
},
],
""Value"":null
}";
var sentData = Encoding.UTF8.GetBytes(request);
taskReq.ContentLength = sentData.Length;
Stream sendStream = taskReq.GetRequestStream();
sendStream.Write(sentData, 0, sentData.Length);
var res = taskReq.GetResponse() as HttpWebResponse;
var resStream = res.GetResponseStream();
var sr = new StreamReader(resStream, Encoding.UTF8);
Console.WriteLine(sr.ReadToEnd());
Console.ReadKey();
}
Take a look at the information passed into the method. This is the minimal structure to complete a task with a comment.
Execution result:
{
"Items":
[
{
"Data":null,
"DataArray":[],
"Name":"TypeUid",
"Value":"e1859f3d-c62f-11c5-828b-3b38521cab82"
},
{
"Data":null,
"DataArray":[],
"Name":"Result",
"Value":"True"
}
],
"Value":null
}
True was returned to the Result property. This means that the task was successfully accomplished. If not, False is returned.
Note, that your authorization token might become invalid if, for example, the server was restarted and authorization data of your application is outdated. To check if the token is valid, use the CheckToken function of AuthorizationService.
public static void LoadTask(string authToken, string sessionToken)
{
var typeUid = "f532ef81-20e1-467d-89a4-940c57a609bc";
var entityId = "472";
HttpWebRequest checkTokenReq = WebRequest.Create(String.Format("http://demo.elma-bpm.com/API/REST/Authorization/CheckToken?token={0}", authToken)) as HttpWebRequest;
checkTokenReq.Method = "GET";
checkTokenReq.Timeout = 10000;
checkTokenReq.ContentType = "application/json; charset=utf-8";
try
{
//If no exception, get SessionToken and AuthorizationToken from the response
var res1 = checkTokenReq.GetResponse() as HttpWebResponse;
var resStream1 = res1.GetResponseStream();
var sr1 = new StreamReader(resStream1, Encoding.UTF8);
var result = sr1.ReadToEnd();
Console.WriteLine(result);
Console.ReadKey();
var dict = new JsonSerializer().Deserialize(result, typeof(Dictionary<string, string>)) as Dictionary<string, string>;
authToken = dict["AuthToken"];
sessionToken = dict["SessionToken"];
}
catch (Exception)
{
// If got an exception, authorize again
HttpWebRequest req = WebRequest.Create(String.Format("http://demo.elma-bpm.com/API/REST/Authorization/LoginWith?username=admin")) as HttpWebRequest;
req.Headers.Add("ApplicationToken", ApplicationToken);
req.Headers.Add("SessionToken", sessionToken);
req.Method = "POST";
req.Timeout = 10000;
req.ContentType = "application/json; charset=utf-8";
var sentData = Encoding.UTF8.GetBytes("");
req.ContentLength = sentData.Length;
Stream sendStream = req.GetRequestStream();
sendStream.Write(sentData, 0, sentData.Length);
// Get response
var res1 = req.GetResponse() as HttpWebResponse;
var resStream1 = res1.GetResponseStream();
var sr1 = new StreamReader(resStream1, Encoding.UTF8);
var result = sr1.ReadToEnd();
var dict = new JsonSerializer().Deserialize(result, typeof(Dictionary<string, string>)) as Dictionary<string, string>;
authToken = dict["AuthToken"];
sessionToken = dict["SessionToken"];
}
HttpWebRequest taskReq = WebRequest.Create(String.Format("http://demo.elma-bpm.com/API/REST/Entity/Load?type={0}&id={1}", typeUid, entityId)) as HttpWebRequest;
taskReq.Method = "GET";
taskReq.Headers.Add("AuthToken", authToken);
taskReq.Headers.Add("SessionToken", sessionToken);
taskReq.Timeout = 10000;
taskReq.ContentType = "application/json; charset=utf-8";
var res = taskReq.GetResponse() as HttpWebResponse;
var resStream = res.GetResponseStream();
var sr = new StreamReader(resStream, Encoding.UTF8);
var myTask = sr.ReadLine();
Console.WriteLine(myTask);
Console.ReadKey();
}
Using WSDL
Let’s do the same operations only using WSDL services. To use them, add them to the project. For that, right-click on References -> AddServiceReference -> in the Address field type the required link -> click GO -> if successful, click ОК
If successful, the service will appear in the Solution Explorer.
In a similar way, add a link to IEntityService. Here, you will need the System.ServiceModel.Web namespace.
Let’s start with creating the WsdlService class:
public class WsdlService
{
public Guid AuthToken { get; set; }
public string ApplicationToken { get; set; }
public string SessionToken { get; set; }
private ChannelFactory<EntityService.IEntityServiceChannel> _entityServiceFactory;
private ChannelFactory<Authorization.IAuthorizationServiceChannel> _authServiceFactory;
private ChannelFactory<EntityChangeService.IEntityChangesServiceChannel> _changesServiceFactory;
public void TestStart()
{
_entityServiceFactory = new ChannelFactory<EntityService.IEntityServiceChannel>(new BasicHttpBinding() { MaxReceivedMessageSize = Int32.MaxValue }, "http://demo.elma-bpm.com/API/Entity");
_authServiceFactory = new ChannelFactory<Authorization.IAuthorizationServiceChannel>(new BasicHttpBinding(), "http://demo.elma-bpm.com/API/Authorization");
_changesServiceFactory = new ChannelFactory<EntityChangeService.IEntityChangesServiceChannel>(new BasicHttpBinding(), "http://demo.elma-bpm.com/API/EntityChanges");
GetAuthToken();
}
private string sessionToken;
private Guid authToken;
public void GetAuthToken()
{
var authorizationService = _authServiceFactory.CreateChannel();
using (new OperationContextScope((IContextChannel)authorizationService))
{
WebOperationContext.Current.OutgoingRequest.Headers.Add("ApplicationToken", "AC59057A6D8C13D938CC65DBD45B322874DB7918C5F08C4261D2078A1D11F74D87DEF8ACC1AFA812D4DC35C601E522A8B19BFF591C26B43FDEB7F4AF633CD18C");
var token = authorizationService.LoginWithUserName("admin", "");
authToken = token.AuthToken;
sessionToken = token.SessionToken;
}
Console.WriteLine(authToken);
Console.WriteLine(sessionToken);
}
The _entityServiceFactory variable (and other similar ones) serves to create a communication channel with the server. These variables are initialized in the TestStart() function. The channel itself is created in CreateChannel().
Let’s take a look at the GetAuthToken()function. The authorizationService variable is the service created; it contains methods described in the WebAPI help.
The channel is used in using( … ) { … }. It is necessary for passing required headers when using various functions of the service.
Here, the application token is passed in the headers. Then we use the LoginWithUserName function. If successful, you receive the authorization token in the System.Guid format, and the session token in the String format.
To check if the authorization token is valid, use the CheckToken function.
using (new OperationContextScope((IContextChannel)authService))
{
try
{
//If no exception, get the data out of the response
var auth = authService.CheckToken(AuthToken);
AuthToken = auth.AuthToken;
SessionToken = auth.SessionToken;
}
catch (FaultException<Authorization.PublicServiceException> ex)
{
if (ex.Detail.StatusCode == 401)
{
//if 401 exception was thrown, get the authorization data again
WebOperationContext.Current.OutgoingRequest.Headers.Add("ApplicationToken", "AC59057A6D8C13D938CC65DBD45B322874DB7918C5F08C4261D2078A1D11F74D87DEF8ACC1AFA812D4DC35C601E522A8B19BFF591C26B43FDEB7F4AF633CD18C");
var token = authService.LoginWithUserName("admin", "");
AuthToken = token.AuthToken;
SessionToken = token.SessionToken;
WebOperationContext.Current.OutgoingRequest.Headers.Add("SessionToken", SessionToken);
}
else
{
//otherwise throw new exception or process it in other way
throw;
}
}
}
Pay attention to the type of the error that you want to catch. Each service has a PublicServiceException class; you have to specify the error type with its namespace. In this case, we are looking for an error that comes from the authorization service.
By catching the error, you can get the following information:
- Error message
- Error status code (for example: 400, 401, 500)
- Internal error (if any)
and a lot more.
The most frequent errors are 401 and 500. The 401 error appears when the authorization fails, more often for one of the following reasons:
- Invalid login/password
- Outdated/invalid authorization token
- Invalid session token
- Invalid application token
500 error refers to an internal server error. Usually, it appears for one of the following reasons:
- Got an error when executing code on the server
- No specific object (e.g. using the Load function)
- No specific object type (when no specified typeUid exists)
There might be errors with other codes, but they are rather rare.
Now go to task loading. Add the LoadTask() function to the created class:
public void LoadTask()
{
var entityService = _entityServiceFactory.CreateChannel();
var authService = _authServiceFactory.CreateChannel();
var typeUid = "f532ef81-20e1-467d-89a4-940c57a609bc";
var id = "474";
using (new OperationContextScope((IContextChannel)entityService))
{
WebOperationContext.Current.OutgoingRequest.Headers.Add("AuthToken", AuthToken.ToString());
WebOperationContext.Current.OutgoingRequest.Headers.Add("SessionToken", SessionToken);
var task = entityService.Load(typeUid, id);
foreach (var item in task.Items)
{
Console.WriteLine(item.Name + ": " + item.Value);
}
}
Console.ReadKey();
}
Execution result:
Id: 474
TypeUid: 298b2c71-619f-463c-95b2-8e029085680d
ExecutorReplaced:
Uid: a7bf049f-7cfd-4ac4-a8a5-ffd585149bbe
Subject: Test WSDL task
Description:
CreationDate: 08/30/2016 11:37:49
CreationAuthor:
Executor:
StartDate: 08/30/2016 00:00:10
EndDate: 08/30/2016 23:59:50
Priority: 2
Comments:
Attachments:
Tags:
ParentTask:
ChildTasks:
Status: 34387afa-6b70-476f-9d34-748732059003
StartWorkDate:
EndWorkDate:
InformTo:
Harmonizator:
TimeSet:
Permissions:
NotShowInLists: False
InformToHash:
Category:
ExpiredNotificationSent: False
PlanWorkLog:
FactWorkLog:
IsEmulation: False
ExecutorIsEmulation:
Contractor:
Contact:
Lead:
Sale:
DocumentAttachments:
Resolution:
Project:
WorkflowBookmark:
AssignedToResponsible: False
These are just some of the fields returned by the function.
When executing certain Web API functions, the following error might appear:
Unhandled exception of the "System.ServiceModel.CommunicationException" type, in mscorlib.dll
Additional information: Maximal size of the incoming message is exceeded (65536). To increase the size, use MaxReceivedMessageSize property of a corresponding linked element. This happens if the response from the server is too large and your application cannot receive it. To avoid this problem, use the
This happens if the response from the server is too large and your application cannot receive it. To avoid this problem, use the _entityServiceFactory variable initialization in the following way:
_entityServiceFactory = new ChannelFactory<EntityService.IEntityServiceChannel>(new BasicHttpBinding() { MaxReceivedMessageSize = Int32.MaxValue }, "http://demo.elma-bpm.com/API/Entity");