Comment on page
GraphQL API
As of version 7.0.0, WorkflowGen features the new GraphQL API, which is a modern solution to create process-driven solutions such as mobile apps, web apps, and microservices that require a powerful workflow and BPM engine.
The WorkflowGen GraphQL API is a Node.js application that runs in IIS using iisnode. It enables a high level of customization such as extending the GraphQL schema with custom types, queries or operations, or implementing new authentication methods.
"GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools."
GraphQL is a production-ready and an open source technology created by Facebook. In September 2016, GitHub announced its GraphQL API.
"We’ve often heard that our REST API was an inspiration for other companies; countless tutorials refer to our endpoints. Today, we’re excited to announce our biggest change to the API since we snubbed XML in favor of JSON: we’re making the GitHub API available through GraphQL."
GraphQL is a modern API solution for React, React-Native, Angular 2, and Vue based applications.
In addition to the standard WorkflowGen installation, the following components are required:
- Visual C++ Redistributable ✏️ Note: This library is required if you encounter the error
The specified module could not be found
regarding theedge
andedge-js
libraries when accessing the/wfgen/graphql
,/wfgen/hooks
, or/wfgen/scim
web apps.
The following endpoints are available:
- GraphQL API:
http://localhost/wfgen/graphql
- GraphiQL IDE:
http://localhost/wfgen/graphql
- GraphQL Schema (definition language):
http://localhost/wfgen/graphql/schema
The HTTP GET method is supported on queries only. The HTTP POST method is supported on queries and operations.
GraphQL will first look for each parameter in the URL's query-string:/graphql?query=query+getUser($id:ID){user(id:$id){name}}&variables={"id":"4"}
If not found in the query-string, it will look in the POST request body. If the POST body has not yet been parsed, express-graphql will interpret it depending on the providedContent-Type
header:
application/json
: the POST body will be parsed as a JSON object of parameters.application/x-www-form-urlencoded
: this POST body will be parsed as a url-encoded string of key-value pairs.application/graphql
: the POST body will be parsed as GraphQL query string, which provides the query parameter.
You can use GraphiQL, "a graphical interactive in-browser GraphQL IDE", to test queries and operations, and to browse the schema documentation.
As of WorkflowGen version 7.15.0, the GraphiQL tool is disabled by default. You can enable it in the GraphQL section on the Integration tab in the Configuration Panel. You can also enable GraphiQL in the WorkflowGen
web.config
file by setting the GraphqlGraphiqlEnabled
parameter to Y
.The maximum GraphQL query content length can be set by configuring the
maxAllowedContentLength
property in the WorkflowGen web.config
file. The following example shows how to configure this property as 1 MB (note that the value should always be specified in bytes, so the value in the example is 1,024,000 bytes). The default value is 30000000 bytes.<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="1024000" />
</requestFiltering>
</security>
</system.webServer>
You can configure the local or remote folder paths where files used by FILE type parameters are located using the Input file allowed folders setting In the GraphQL section on the Configuration Panel Integration tab. (Alternately, you can add the folder names separated by commas to the
GraphqlInputFileAllowedFolders
parameter in the WorkflowGen web.config
file.)When using file uploads, you don't need to include the original file folder.
To disallow input file allowed folders, leave this field empty. To allow certain folders only, enter comma-separated values according to the table below:
Value | Description |
Empty | No folders allowed |
* | All folders allowed |
c:\* | All folders on drive c: |
c:\Inetpub\* | All subfolders in a specific folder |
c:\Inetpub\folder* | All c:\Inetpub folders whose names start with folder
📌 Examples:
|
c:\Inetpub\folder2\abc\def | Specific folder only |
You can configure allowed HTTP URLs for input files using the Input file allowed HTTP URLs setting in the GraphQL section on the Configuration Panel Integration tab.
To disallow file uploads using HTTP and/or HTTPS URLs, leave the field empty. To allow certain URLs only, enter comma-separated values according to the table below:
Value | Description |
Empty | No HTTP or HTTPS URLs allowed |
* | All HTTP and HTTPS URLs allowed |
https://* | HTTPS URLs only |
http://* | HTTP URLs only |
http://mydomain/* | HTTP from a specific domain only |
http://mydomain/folder/* | HTTP from a specific folder only |
http://mydomain/folder* | All files and folders whose names start with folder
📌 Examples:
|
http://mydomain/folder/file.jpg | Specific file only |
In the GraphQL section on the Integration tab in the Administration Module Configuration Panel, enter the maximum input file size in kilobytes in the Maximum input file size (kB) field.
Alternately, you can set the maximum input file content size in kilobytes as the value of the
GraphqlMaxInputFileSize
parameter in the WorkflowGen web.config
file.When working with FILE type parameters content encoded in base64, you must enter the maximum input file content size in kilobytes in the Maximum input file content size (kB) field in the GraphQL section on the Integration tab in the Administration Module Configuration Panel.
Alternately, you can set the maximum input file content size in kilobytes as the value of the
GraphqlMaxInputFileContentSize
parameter in the WorkflowGen web.config
file.FILE type data content is only recommended for small files under 1 MB.
As of version 8.1.3, if your WorkflowGen and GraphQL API are secured using the WorkflowGen applicative or OpenID Connect authentications, you can alternatively access the GraphQL schema (i.e.
/wfgen/graphql/schema
) and the Introspection
queries using a GraphQL API key header (x-wfgen-graphql-api-key
) defined in the HTTP request, without an authentication header.Add or define the following configuration parameters to the main
\wfgen\web.config
:<add key="GraphqlApiKeyEnabled" value="N" />
: Set toY
in order to enable the GraphQL API key feature.<add key="GraphqlApiKey" value="[YOUR_API_KEY]" />
: The value sent in thex-wfgen-graphql-api-key
header must match this key. We recommend using a long and unique key string such as a GUID for example.
WorkflowGen is installed with the following default GraphQL settings (located under
iisnode
in \wfgen\graphql\web.config
):nodeProcessCountPerApplication="0"
maxConcurrentRequestsPerProcess="1024"
The value of the
nodeProcessCountPerApplication
setting is set to 0
by default for the best performance in Node.js applications. This creates one node process based on the number of virtual processors that are configured. You can change this value at any time to a custom number of node processes; for example, nodeProcessCountPerApplication=2
will create two node processes independently of the number of virtual processors.You can also optimize performance if needed by adjusting the
maxConcurrentRequestsPerProcess
value based on the number of potential concurrent users and requests.For more information, see the Best practices and troubleshooting guide for node applications on Azure Web Apps Microsoft article.
In order to allow a client application (such as front-end JavaScript code from an outside domain) to access and request data from the API, you must enable and configure the Cross-origin resource sharing (CORS) settings in the WorkflowGen GraphQL API module. To do this:
- 1.
- 2.Add the
cors
node with the list of external domains and their methods and headers (where HTTP requests will be allowed) to the WorkflowGen web configuration settings (located in\wfgen\web.config
). See some common examples below.
📌 Example 1: Allow all origins
<configuration>
<location path="graphql" inheritInChildApplications="false">
<system.webServer>
<cors enabled="true">
<add origin="*">
<allowMethods>
<add method="GET" />
<add method="POST" />
<add method="OPTIONS" />
<add method="HEAD" />
</allowMethods>
<allowHeaders>
<add header="Accept" />
<add header="Origin" />
<add header="Authorization" />
<add header="Content-Type" />
</allowHeaders>
</add>
</cors>
</system.webServer>
</location>
</configuration>
📌 Example 2: Allow specific origins
<configuration>
<location path="graphql" inheritInChildApplications="false">
<system.webServer>
<cors enabled="true">
<add origin="https://domain.b.com" allowCredentials="true">
<allowMethods>
<add method="GET" />
<add method="POST" />
<add method="OPTIONS" />
<add method="HEAD" />
</allowMethods>
<allowHeaders>
<add header="Accept" />
<add header="Origin" />
<add header="Authorization" />
<add header="Content-Type" />
</allowHeaders>
</add>
<add origin="https://domain.c.com" allowCredentials="true">
<allowMethods>
<add method="GET" />
<add method="POST" />
<add method="OPTIONS" />
<add method="HEAD" />
</allowMethods>
<allowHeaders>
<add header="Accept" />
<add header="Origin" />
<add header="Authorization" />
<add header="Content-Type" />
</allowHeaders>
</add>
</cors>
</system.webServer>
</location>
</configuration>
The following authentication methods are supported:
- IIS Basic
- WorkflowGen authentication
- Custom .NET authentication modules
- OpenID Connect
If your WorkflowGen site is configured with Integrated Windows or Basic authentication, you must configure GraphQL with Basic authentication.
HTTPS is required to secure credentials.
For OpenID Connect providers, you need to pass the access token as a bearer token in the Authorization header; for example,
Authorization: Bearer <ACCESS TOKEN>
(replace<ACCESS TOKEN>
with your access token).The GraphQL Node.js app code inside the
\wfgen\graphql
folder can also be customized to accommodate many other authentication methods (such as OAuth2, JWT, etc.) thanks to node libraries such as Passport.js.Some operations (such as
UpdateRequestDataset
) require users to have system access to perform the operations. This can be configured in the System operations allowed users field, under Security on the Integration tab in the Configuration Panel.User impersonation is supported but not recommended, and should be used only when no other technical solutions are possible. (For example, OpenID Connect-based authentication methods allow you to use access tokens to perform API operations on the client and server sides without impersonation.)
System operations allowed users can impersonate another WorkflowGen user account by setting this account's username as the value of the
x-wfgen-impersonate-username
HTTP request header.This request header can be renamed according to your naming convention. You can specify a new header name in the
GraphqlImpersonateUserNameHttpHeader
setting in the \wfgen\web.config
file (e.g. <add key="GraphqlImpersonateUserNameHttpHeader" value="my-custom-impersonate-username" />
).To give or withdraw system operations rights to or from specific users, refer to the System operations allowed users setting in the Security section on the General tab of the Configuration Panel; alternately, you can edit the
ProcessesRuntimeWebServiceAllowedUsers
setting in the \wfgen\web.config
file.Some GraphQL queries and operations can be executed on behalf of another user. This is possible when a user has created a delegation in WorkflowGen. The delegatee has to specify the user ID of his delegator in the
onBehalfOf
argument.List of actions to do on by the delegatee on behalf of the delegator with the user ID
VXNlcjoy
:{
viewer {
actions(filter: {as: ASSIGNEE, status: OPEN}, onBehalfOf:"VXNlcjoy"}) {
totalCount
hasNextPage
hasPreviousPage
items {
request {
number
description
}
number
name
description
limit
launchUrl
}
}
}
}
When the
onBehalfOf
argument is set, it is propagated implicitly to the all the sub-queries and fields until a User type is used.Each GraphQL type has an
id: ID!
field. This ID is global and is unique for all WorkflowGen objects.You can use the
node(id:ID!)
query to retrieve a WorkflowGen object by its ID.{
node(id: "UHJvY2VzczoxNQ==") {
id
... on Request {
number
requester {
lastName
}
}
... on Action {
limit
assignee {
id
company
}
}
... on User {
userName
email
}
}
}
You can copy/paste these queries directly in the GraphiQL IDE. See the Using GraphiQL IDE in a web browser section above for more information.
curl -X POST http://localhost/wfgen/graphql -H "Content-Type: application/x-www-form-urlencoded" -d "query={ viewer { userName lastName firstName email } }"
And the result is:
{
"data": {
"viewer": {
"userName": "johndoe",
"lastName": "Doe",
"firstName": "John",
"email": "[email protected]"
}
}
}
{
viewer {
userName
lastName
firstName
email
}
}
{
viewer {
actions(filter: {as: ASSIGNEE, status: OPEN}) {
totalCount
hasNextPage
hasPreviousPage
items {
request {
number
description
}
number
name
description
limit
launchUrl
}
}
}
}
{
request(number: 273) {
description
requester {
lastName
userName
company
}
process {
name
version
}
}
}
To create a new request from the GraphQL API, make sure that sub-process mode is enabled with public access on the target process. (See the Process form section in the WorkflowGen Administration Guide for more information.)
mutation {
createRequest(input: {
processName: "2_LEVELS_APPROVAL",
processVersion: 1
}) {
request {
id
name
number
}
}
}
{
"data": {
"createRequest": {
"request": {
"id": "UmVxdWVzdDoxNQ==",
"name": "2_LEVELS_APPROVAL #15",
"number": 15
}
}
}
}
A parameter's array can be included in the
createRequest
operation payload. Be aware that a data with the same name and data type must previously exist in the process for each parameter in the array to store the parameter's value. The following example shows how to send parameters corresponding to the four supported data types (TEXT, NUMERIC, DATETIME, and FILE).mutation {
createRequest(input: {processName: "SR", processVersion: 1, parameters: [{name: "TEXT", textValue: "My text parameter"}, {name: "NUMERIC", numericValue: 5}, {name: "DATE", dateTimeValue: "2017-02-23T20:46:00Z"}, {name: "FILE", fileValue: {name: "TestFile.txt", contentType: "text/plain", size: 616, url: "file:///c:/TestFile.txt", updatedAt: "2017-02-21T15:06:38Z"}}]}) {
request {
id
name
number
}
}
}
{
"data": {
"createRequest": {
"request": {
"id": "UmVxdWVzdDoxNg==",
"name": "2_LEVELS_APPROVAL #16",
"number": 16
}
}
}
}
For more information on FILE parameter manipulations when sent within GraphQL payloads, see the File upload section.
A request dataset context can be updated by adding a parameter array. In this case a request number or a request ID should be provided.
mutation {
updateRequestDataset(input: {
number: 22,
parameters: {
name: "TEXT",
textValue: "My text parameter"
}
}) {
dataset {
items {
name
textValue
}
}
}
}
{
"data": {
"updateRequestDataset": {
"dataset": {
"items": [
{
"name": "TEXT",
"textValue": "My text parameter"
}
]
}
}
}
}
You can cancel a request by using the request number or the request ID.
mutation {
cancelRequest(input: {
number: 15
}) {
request {
id
name
number
status
}
}
}
{
"data": {
"cancelRequest": {
"request": {
"id": "UmVxdWVzdDoxNQ==",
"name": "SR #15",
"number": 15,
"status": "CLOSED"
}
}
}
}
A request can be deleted by using the request number or the request ID.
mutation {
deleteRequest(input: {
number: 15
}) {
clientMutationId
}
}
mutation {
deleteRequest(input: {
id: "UmVxdWVzdDoxNQ=="
}) {
clientMutationId
}
}
{
"data": {
"deleteRequest": {
"clientMutationId": null
}
}
}
To complete an action, provide the request number and the action number, or the action ID.
mutation {
completeAction(input: {
requestNumber: 16,
number: 1
}) {
action {
id
status
}
}
}
{
"data": {
"completeAction": {
"action": {
"id": "QWN0aW9uOjE2LS0tMQ==",
"status": "CLOSED"
}
}
}
}
To complete an action, a parameter array can be included in the request payload arguments.
mutation {
completeAction(input: {
requestNumber: 20,
number: 1,
parameters: [{
name: "NEW_PARAMETER",
textValue: "My parameter"
}]
}) {
action {
id
status
}
}
}
{
"data": {
"completeAction": {
"action": {
"id": "QWN0aW9uOjIwLS0tMQ==",
"status": "CLOSED"
}
}
}
}
This mutation completes a form action (EFORMASPX action with ASP.NET web form) and also updates the form archive and form data files.
mutation {
completeFormAction(input: {
requestNumber: 20,
number: 1,
parameters: [{
name: "NEW_PARAMETER",
textValue: "My parameter"
}]
}) {
action {
id
status
}
}
}
{
"data": {
"completeFormAction": {
"action": {
"id": "QWN0aW9uOjIwLS0tMQ==",
"status": "CLOSED"
}
}
}
}
To cancel an action, provide the request number and the action number, or the action ID. The following conditions must be met:
- The viewer and the user (if they don't share the same identity, as in delegation mode) must have access to the request.
- The action must be open.
- The action must have a cancel or default exception defined in the transition.
- The viewer is:
- an administrator or process folder manager OR
- a supervisor with cancellation rights OR
- the action assignee.
mutation {
cancelAction(input: {
requestNumber: 21,
number: 1
}) {
action {
id
status
}
}
}
{
"data": {
"cancelAction": {
"action": {
"id": "QWN0aW9uOjIxLS0tMQ==",
"status": "CLOSED"
}
}
}
}
All of the actions with the same name in a request can be cancelled at the same time by using their name in this operation payload. The following conditions are met:
- The viewer and the user (if they do not share the same identity, as in delegation mode) must have access to the request.
- The action must be open.
- The action must have a cancel or default exception defined in the transition.
- The viewer is:
- an administrator or a process folder manager OR
- a supervisor with cancellation rights OR
- the action assignee.
mutation {
cancelRequestActionsByName(input: {
requestNumber: 21,
activityName: "INITIATES"
}) {
action {
id
status
}
}
}
{
"data": {
"cancelAction": {
"action": {
"id": "QWN0aW9uOjIxLS0tMQ==",
"status": "CLOSED"
}
}
}
}
To assign an action, provide the request number and the action number, or the action ID; you must also provide the
assigneeUserName
or the assigneeId
.mutation {
assignAction(input: {
requestNumber: 22,
number: 1,
assigneeId: "VXNlcjox"
}) {
action {
id
assignee {
id
}
}
}
}
{
"data": {
"assignAction": {
"action": {
"id": "QWN0aW9uOjIyLS0tMQ==",
"assignee": {
"id": "VXNlcjox"
}
}
}
}
}
To cancel an action assignment, you should provide the request number and the action number, or the action ID.
mutation {
cancelActionAssignment(input: {
requestNumber: 22,
number: 1
}) {
action {
id
assignee {
id
}
}
}
}
{
"data": {
"cancelActionAssignment": {
"action": {
"id": "QWN0aW9uOjIyLS0tMQ==",
"assignee": null
}
}
}
}
An interactive action (including data locks) can be started by using the
StartInteractiveAction
mutation. To perform this mutation, provide the either request number and the action number, the request number and the activity name, or the action ID. The following conditions must be met:
- The viewer and the user (if they don't share the same identity, as in delegation mode) must have access to the request.
- The action must be open.
- The action type must be human.
- The user must have the rights to to complete the action.
mutation {
startInteractiveAction(input: {requestNumber: 73, number: 2}) {
action {
status
subStatus
interactiveParameters {
totalCount
items {
name
direction
hasValue
type
textValue
numericValue
dateTimeValue
fileValue {
url
blobUrl
name
}
}
}
}
}
}
An array of interactive parameters is required to complete the action; it can be returned by using the
action.interactiveParameters
query.{
"data": {
"startInteractiveAction": {
"action": {
"status": "OPEN",
"subStatus": "RUNNING",
"interactiveParameters": {
"totalCount": 14,
"items": [
{
"name": "CURRENT_ACTION",
"direction": "IN",
"hasValue": true,
"type": "TEXT",
"textValue": "VALIDATES",
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "CURRENT_REQUEST",
"direction": "IN",
"hasValue": true,
"type": "NUMERIC",
"textValue": null,
"numericValue": 73,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "FORM_ARCHIVE",
"direction": "OUT",
"hasValue": false,
"type": "FILE",
"textValue": null,
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "FORM_DATA",
"direction": "INOUT",
"hasValue": false,
"type": "FILE",
"textValue": null,
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "FORM_DRAFT",
"direction": "OUT",
"hasValue": false,
"type": "TEXT",
"textValue": null,
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "FORM_FIELDS_HIDDEN",
"direction": "IN",
"hasValue": true,
"type": "TEXT",
"textValue": "LEVEL2",
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "FORM_FIELDS_READONLY",
"direction": "IN",
"hasValue": true,
"type": "TEXT",
"textValue": "REQUEST1_*,REQUEST_*",
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "FORM_FIELDS_REQUIRED",
"direction": "IN",
"hasValue": true,
"type": "TEXT",
"textValue": "LEVEL1_DECISION",
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "FORM_URL",
"direction": "IN",
"hasValue": true,
"type": "TEXT",
"textValue": "./wfapps/webforms/2_LEVELS_APPROVAL/V1/Default.aspx",
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "LEVEL1_DATE",
"direction": "IN",
"hasValue": true,
"type": "DATETIME",
"textValue": null,
"numericValue": null,
"dateTimeValue": "2021-02-08T19:21:51.033Z",
"fileValue": null
},
{
"name": "LEVEL1_DECISION",
"direction": "INOUT",
"hasValue": true,
"type": "TEXT",
"textValue": "default value",
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "LEVEL1_FIRSTNAME",
"direction": "IN",
"hasValue": true,
"type": "TEXT",
"textValue": "WorkflowGen",
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "LEVEL1_LASTNAME",
"direction": "IN",
"hasValue": true,
"type": "TEXT",
"textValue": "Administrator",
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
},
{
"name": "REQUEST_ATTACHMENT",
"direction": "INOUT",
"hasValue": false,
"type": "FILE",
"textValue": null,
"numericValue": null,
"dateTimeValue": null,
"fileValue": null
}
]
}
}
}
}
}
GraphQL lets users add processes and views to their favorites lists using the process or view IDs.
mutation {
createFavorite(input: {
itemId: "UHJvY2Vzczoy"
}) {
favorite {
id
type
}
}
}
{
"data": {
"createFavorite": {
"favorite": {
"id": "RmF2b3JpdGU6MQ==",
"type": "PROCESS"
}
}
}
}
This code will create a favorite using the process or view description, but adding a custom description is also possible, as shown in the following example: