= REST API extensions
:description: Create REST API extensions to use third party systems (databases, web services, Bonita Engine, etc) data in forms and pages.

Create REST API extensions to use third party systems (databases, web services, Bonita Engine, etc) data in forms and pages.

REST API extensions can be used to query xref:define-and-deploy-the-bdm.adoc[business data], Bonita Engine APIs, or an external information system (such as a database, web service, LDAP directory...). They also help to keep a clean separation between the front-end (forms, pages, and interfaces visible to users) and the back-end (processes).

== Prerequisites

Prerequisites for developing a REST API extension are:

* Java/Groovy development expertise.

== Example description

The following sections show how to create a REST API extension. As an example, we create a REST API extension that uses the Bonita BPM Engine to provide user information (first name, last name, email address).

There is some additional prerequisites when creating a REST API extension:

* Basic knowledge of Maven
* *Access to http://central.maven.org/maven2[Maven central repository]*.
* More information on maven configuration xref:configure-maven.adoc[here]

=== Generate a new REST API extension skeleton

. In the *Development* menu, choose *REST API Extension* then *New...*.
. Enter a *Name*, for example _User information REST API Extension_.
. Enter a *Description*, for example _Query Bonita Engine to retrieve user information_.
. Enter a package name, use to set the artifact *Group id*, for example: _com.company.rest.api_
. Enter a *Project name*, for example _userInformationRestAPIExtension_
. Click *Next*.
. Enter the *pathTemplate* for this REST API extension, for example _userInformation_. This will be the access point of the API, and follows this pattern: `+{bonita_portal_context}/API/extension/userInformation+`.
. As this REST API extension does not access business data you can safely uncheck "Add BDM dependencies" check box.
. Define a *Permission* name for the extension (replace the default one), for example _read_user_information_. This is the name of the permission the users should have to be granted access to the extension (see xref:api-extensions.adoc#usage[REST API extensions usage]
. Click *Next*
. This screen defines *URL parameters* that will be passed to the API. By default, _p_ and _c_ parameters are defined to enables paged result, it applies well in our examples as we want to return a list of users.
. Click *Create*.

=== Write the code

==== Main source code

First step would be to remove files and code related to REST API configuration as we don't need to define configuration parameters for our REST API:

. Delete `configuration.properties` from `src/main/resources` folder
. Delete `testConfiguration.properties` from `src/test/resources`
. Remove the setup of configuration file mock. Edit `IndexTest.groovy`, go to `setup()` method and remove the line starting with `+resourceProvider...+`.
. Remove the example of configuration usage in `Index.groovy` file (see comment starting with: "Here is an example of you can...").

Now we can add our business logic. In `Index.groovy`, in `doHandle` method, locate the "Your code goes here" comment and add your code below (removing the existing `result` and `return` statement):

[source,groovy]
----
// Convert parameters from string to int
p = p as int
c = c as int

// Initialize the list to store users information
def usersInformation = []

// Get the list of user
List<User> users = context.apiClient.identityAPI.getUsers(p*c, c, UserCriterion.FIRST_NAME_ASC)

// Iterate over each user
for (user in users) {
	// Get user extra information (including email address)
	ContactData contactData = context.apiClient.identityAPI.getUserContactData(user.id, false)

	// Create a map with current user first name, last name and email address
	def userInformation = [firstName: user.firstName, lastName: user.lastName, email: contactData.email]

	// Add current user information to the global list
	usersInformation << userInformation
}

// Prepare the result
def result = [p: p, c: c, userInformation: usersInformation]

int startIndex = p*c
int endIndex = p*c + users.size() - 1

// Send the result as a JSON representation
return buildPagedResponse(responseBuilder, new JsonBuilder(result).toString(), startIndex, endIndex, context.apiClient.identityAPI.numberOfUsers)
----

Make sure you are adding all missing imports (default shortcut CTRL+SHIFT+o).

==== Test source code

Now we need to update the test to verify the behavior of our REST API extension by editing `IndexTest.groovy`.

First step is to define some mocks for our externals dependencies such as Engine Identity API. Add the following mocks declaration after the existing ones:

[source,groovy]
----
def apiClient = Mock(APIClient)
def identityAPI = Mock(IdentityAPI)
def april = Mock(User)
def william = Mock(User)
def walter = Mock(User)
def contactData = Mock(ContactData)
----

Now we need to define the generic behavior of our mocks. `setup()` method should have the following content:

[source,groovy]
----
context.apiClient >> apiClient
apiClient.identityAPI >> identityAPI

identityAPI.getUsers(0, 2, _) >> [april, william]
identityAPI.getUsers(1, 2, _) >> [william, walter]
identityAPI.getUsers(2, 2, _) >> [walter]

april.firstName >> "April"
april.lastName >> "Sanchez"
william.firstName >> "William"
william.lastName >> "Jobs"
walter.firstName >> "Walter"
walter.lastName >> "Bates"

identityAPI.getUserContactData(*_) >> contactData
contactData.email >> "test@email"
----

Now you can define a test method. Replace existing test `should_return_a_json_representation_as_result` method with the following one:

[source,groovy]
----
def should_return_a_json_representation_as_result() {
  given: "a RestAPIController"
  def index = new Index()
  // Simulate a request with a value for each parameter
  httpRequest.getParameter("p") >> "0"
  httpRequest.getParameter("c") >> "2"

  when: "Invoking the REST API"
  def apiResponse = index.doHandle(httpRequest, new RestApiResponseBuilder(), context)

  then: "A JSON representation is returned in response body"
  def jsonResponse = new JsonSlurper().parseText(apiResponse.response)
  // Validate returned response
  apiResponse.httpStatus == 200
  jsonResponse.p == 0
  jsonResponse.c == 2
  jsonResponse.userInformation.equals([
    [firstName:"April", lastName: "Sanchez", email: "test@email"],
    [firstName:"William", lastName: "Jobs", email: "test@email"]
  ]);
}
----

Make sure you are adding all missing imports (default shortcut CTRL+SHIFT+o).

You should now be able to run your unit test. Right click the `IndexTest.groovy` file and click on *REST API Extension* > *Run JUnit Test*. The JUnit view displays the test results. All tests should pass.

=== Build, deploy and test the REST API extension

Studio let you build and deploy the REST API extension in the embedded test environment.

First step is to configure security mapping for your extension in Studio embedded test environment:

. In the *Development* menu, choose *REST API Extension* then *Edit permissions mapping*.
. Append this line at the end of the file:
`profile|User=[read_user_information]` This means that anyone logged in with the user profile is granted this permission.
. Save and close the file.

Now you can actually build and deploy the extension:

. In the *Development* menu, choose *REST API Extension* > *Deploy...*
. Select the userInformationRestAPIExtension REST API extension.
. Click on *Deploy* button.
. In the coolbar, click the *Portal* icon. This opens the Bonita Portal in your browser.
. In the Portal, change to the *Administrator* profile.
. Go to the *Resources* tab, and check that the User information REST API extension is in the list of REST API extension resources.

Now you can finally test your REST API extension:

. Open a new tab in the web browser
. Enter the following URL: `+http://localhost:8080/bonita/API/extension/userInformation?p=0&c=10+`.
. The JSON response body should be displayed.

The REST API extension can be used in forms and pages in the *UI Designer* using an `External API` variable.

== Example ready to use

You can checkout the Bonita Studio repository that include this extension and a process that use it directly from the Studio by provinding the Git repository URL: https://github.com/bonitasoft/rest-api-extension-user-information-example

== BDM and Performance matters

Two maven artifacts are generated from the Business Data Model : *bdm-dao* and *bdm-client*. +
The version of those artifacts is fixed to 1.0.
You have the possibility to edit the group id of those artifacts from the BDM edition wizard. +
Those maven artifacts are meant to be used from REST API extensions, using the following dependencies:

[source,xml]
----
<dependency>
	<groupId>[YOUR GROUP ID]</groupId>
	<artifactId>bdm-client</artifactId>
	<version>1.0.0</version>
	<scope>provided</scope>
</dependency>

<dependency>
	<groupId>[YOUR GROUP ID]</groupId>
	<artifactId>bdm-dao</artifactId>
	<version>1.0.0</version>
	<scope>provided</scope>
</dependency>
----

Those dependencies are automatically added when a REST API Extension is created from the Bonita Studio. It allows to manipulate Business Objects from a REST API Extension. +
ℹ️ Only *read operations*  can be performed on business objects from a REST API Extension, even with the dao. Write operations are done through processes.

Be aware that a poor implementation of a custom REST API accessing BDM objects can lead to poor performance results. See the xref:bdm-in-rest-api.adoc[best practice] on this matter.

[.troubleshooting-title]
== Troubleshooting

[.troubleshooting-section]
--
[.symptom]
I get the following stacktrace when using Java 8 Date types (LocalDate, LocalDateTime...) in my Rest API Extension

[source,log]
----
java.lang.StackOverflowError
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1806)
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1735)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:149)
	at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:144)
	at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:253)
	at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:285)
	at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:295)
	at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:261)
	at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:871)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.getMetaPropertyValues(DefaultGroovyMethods.java:364)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.getProperties(DefaultGroovyMethods.java:383)
	at groovy.json.JsonOutput.writeObject(JsonOutput.java:290)
	at groovy.json.JsonOutput.writeIterator(JsonOutput.java:445)
	at groovy.json.JsonOutput.writeObject(JsonOutput.java:269)
	at groovy.json.JsonOutput.writeMap(JsonOutput.java:424)
	at groovy.json.JsonOutput.writeObject(JsonOutput.java:294)
	at groovy.json.JsonOutput.writeIterator(JsonOutput.java:441)
	at groovy.json.JsonOutput.writeObject(JsonOutput.java:269)
	at groovy.json.JsonOutput.writeMap(JsonOutput.java:424)
	at groovy.json.JsonOutput.writeObject(JsonOutput.java:294)
----

[.cause]#Cause#
The http://docs.groovy-lang.org/2.4.4/html/gapi/groovy/json/JsonBuilder.html[`groovy.json.JSONBuilder`] does not support Java 8 Date types serialization for the groovy version currently used by Bonita.

[.solution]#Solution#
As a workaround you have to format dates in a new data structure before using the JSONBuilder.

Example:

[source,groovy]
----
def employee = //A given employee object
def result = [
    name:employee.name,
    birthDate:employee.birthDate.format(DateTimeFormatter.ISO_LOCAL_DATE)
]
return buildResponse(responseBuilder, HttpServletResponse.SC_OK,new JsonBuilder(result).toPrettyString())
----

[NOTE]
====

We do not recommend to manage time zone at the Rest API level, as the local of the Rest API server, the Bonita Engine server, and the End User machine could be different.
So we encourage you to manipulate UTC dates only server-side.
You can see how we xref:datetimes-management-tutorial.adoc#_date_and_time_displayed_in_the_user_time_zone[manage the time zone using the date time picker]. This time zone should only be managed in the end user interface.
====
--
