Integrating with 3rd party services (ADF 106)

cancel
Showing results for 
Search instead for 
Did you mean: 

Integrating with 3rd party services (ADF 106)

alfresco
Alfresco Employee
3 0 3,865

After a detailed overview on how to create custom pages and components using the Alfresco Application Development Framework, in this tutorial we are going to see how to integrate different services from Alfresco ECM and Activiti BPM, into an existing application.

We would like here to remember that Alfresco Application Development Framework (from now on ADF) is provided by Alfresco as Open Source, to build custom applications on top of the Activiti BPM Engine and Alfresco ECM Repository.

We won’t introduce here the basis of the Alfresco ADF, because we would like to focus the content on customising an existing ADF application. To better describe the tasks, we are going to use “a step by step“ approach, more practical and easy to follow for our goal.  

For further details about the Alfresco ADF, please refer to the alfresco-ng2-components GitHub Project and take a look to the Alfresco Catalog, with a friendly (and technical) overview of all the available components. For requests of support, questions and feedback, don’t forget to participate to the Community of Developers dedicated to the ADF or to the Gitter space dedicated to the Alfresco Angular 2 components.

About integrating 3rd party services

As described in the introduction of the Alfresco ADF architecture, the ADF application is a front-end layer, completely decoupled from the back-end layer. The back-end layer is defined by a collection of RESTFul based services, defined into the ECM System and the BPM System.

This standard configuration is not mandatory and there are no restrictions on defining one (or more) 3rd party entities into the architecture. Below a picture showing how the Alfresco ADF architecture look like, in case of other entities are included into the back-end layer.

Alfresco ADF architecture with focus on the service layer including 3rd party services.

The requirements around the 3rd party services are not strict or challenging. The most relevant topics are about: exposing the services of the 3rd party application over the HTTP/HTTPS protocol and, hopefully, being real RESTFul based services (not a mandatory requirement by the way).

After this brief introduction about the architecture, in the next paragraph we will see a couple of possible integrations as an example. The goal is to share some practical scenarios, as a guidance for your specific integrations.

Introducing examples of 3rd party integrations

In this section we would like to share some possible integrations between the Alfresco Content Services, the Alfresco Process Services and a 3rd party services. All of this, using an Alfresco ADF application on top of them. More in detail, we are going to describe two possible integrations with the goal to share some working examples, hopefully close to your business needs and requirements.

Assuming you have an Alfresco ADF application, created in the same way it is described in the ADF 101 tutorial, two possible integrations are:

  • Google Mail reader. In this integration we imagine you could be interested to develop a view into the Alfresco ADF application, showing the previews of the emails into your GMail inbox. For each email, the user could request for two different actions: uploading the email into the ECM repository as new content, starting a process of review/feedback of the content using the BMP system.

  • Community Portal reader. In this integration we imagine you could be interested to develop a view into the Alfresco ADF application, showing the previews of all the new content into the Alfresco Community Portal (but could be another portal, if you will want). For each new content, the user could request for two different actions: uploading the email into the ECM repository as new content, starting a process of review/feedback of the content using the BMP system.

As you have seen above, the examples are quite similar but involves two different 3rd party services: in the first case Google Mail and in the second case Jive-x Customer Community Software developing the Alfresco Community Portal.

Talking about the Google Mail reader, Google exposes the GMail content using a great (and simple) GMail API. For further details about how the GMail API works, we suggest to take a look to the JavaScript QuickStart, very straightforward to make it work in practice. The collection of available RESTFul based services will let you able to interact with your inbox, having a full control. Unfortunately, the process of creation of valid credentials is quite complex for a tutorial like this, so we prefer to leave this example to your initiative and show here about the Jive integration. In any case, the solution discussed below is valid for both the solutions (and more similar) and you can try by yourself to integrate your GMail inbox as an exercise.

The Jive REST API

Talking about the Jive-x Customer Community Software, it is one of the most known Community Software and it is the platform used from Alfresco to develop its Alfresco Community Portal. As described in the official documentation, Jive comes with a very nice REST API. The good news for our purpose is that some of the services are publicly available, without mandatory authentication, authorisation or security restrictions. Of course, the integration of the Jive-x Customer Community Software is only an example and you could be interested to integrate your preferred portal or software in general.

Because of this “open” approach, this sample in particular is more attractive than others. More in detail, we are interested to use the content services, defined as: web service endpoints for functionality that is common to all content objects. As well described from the Jive documentation, using those services you can manage the content as preferred, using a pure RESTFul based approach.

Looking at the various available services, the one we would like to use is available at the endpoint /contents, it is reachable using the GET method and returns a paginated list of content objects that match the specified filter criteria (you can look at the official documentation for further details). For our purpose, it is interesting because we can get all the most recent community content, with a simple call like the one described below as an example.

https://community.alfresco.com/api/core/v3/contents?filter=type(document,post)&includeBlogs=true&abr...

To see how the result of the REST service look like, try to execute it using your preferred browser. Below a picture showing the result as an example.

Jive call to the /content endpoint to retrieve the latest community content.

As you can see the result is a pure JSON representation and can be easily managed by a JavaScript source code.

Developing the Alfresco ADF view showing the latest contributions in Jive

Now that we know how to use the Jive-x REST API, in particular how to retrieve the most recent community content into the Alfresco Community Portal, it’s time to start developing and customising the Alfresco ADF application.

As usual, we are going to have a “step by step” approach, detailing the tasks to reach the final goal. For a better understanding, we are going to discuss the solution in three different tasks:

  • Creating the new view, services and components into the Alfresco ADF application. In this task we are going to create a new view with all the requested components, to make the integrations working properly.

  • Description of the solution. Here we are going to describe the details of the solution in terms of components, strategies and integrations.

  • The integration in action. Last but not least, we are going to see how the integration work in practice with some screenshots for a better understanding.

Below a paragraph detailing each task with further details.

Creating the new view, services and components

Assuming you have an Alfresco ADF application up and running, called my-adf application and created in the same way it is described in the ADF 101 tutorial, let’s create a new view called integration, following the tutorial about creating custom pages and components into an Alfresco ADF application. The new view will be defined from the beginning with all it is required for the integration to work properly. Only in the next paragraph we will share a detailed explanation of the various solutions and components.

Starting from <my-adf>/app/components, let’s create a new folder named integration. Inside the new folder, let’s create three files: integration.component.css, integration.component.html and integration.component.ts. All the three files together, define the Angular 2 component. Below you can find the content you should copy (or digit) into each file.

integration.component.css

.container {
 margin: 10px;
}

@media only screen and (max-width: 640px) {
 .container {
   margin: 0;
 }
}

integration.component.html

<div class="mdl-grid">

<div class="mdl-cell mdl-cell--12-col task-column mdl-shadow--2dp">

 <table class="mdl-data-table mdl-js-data-table">

  <thead>
   <tr>
    <th class="mdl-data-table__cell--non-numeric">Author</th>
    <th class="mdl-data-table__cell--non-numeric">Views</th>
    <th class="mdl-data-table__cell--non-numeric">Community content</th>
    <th class="mdl-data-table__cell--non-numeric">Actions</th>
   </tr>
  </thead>

  <tbody>

   <tr *ngFor="let content of jiveContent; let i = index;" #elem>

    <!-- Cell: Author. -->
    <td class="mdl-data-table__cell--non-numeric">
     <img src="{{content.author.thumbnailUrl}}" width="50px;"/>
    </td>

    <!-- Cell: Views. -->
    <td class="mdl-data-table__cell--non-numeric">
     <span class="mdl-chip"><span class="mdl-chip__text">{{content.viewCount}} views</span></span>
    </td>

    <!-- Cell: Content preview. -->
    <td class="mdl-data-table__cell--non-numeric adf__cell_long_text">
     <h5>{{content.subject}}</h5>
     <p>{{content.content.text}}</p>
    </td>

    <!-- Cell: Actions. -->
    <td class="mdl-data-table__cell--non-numeric">
     <button [id]="content.id" class="mdl-button mdl-js-button mdl-button--icon">
      <i class="material-icons">more_vert</i>
     </button>
     <ul [attr.for]="content.id" class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect">
      <li class="mdl-menu__item" (click)="UploadContentOn(i,content.id)">Upload as content</li>
      <li class="mdl-menu__item" (click)="StartProcessOn(i,content.id)">Start a review process</li>
     </ul>
    </td>

   </tr>

  </tbody>

 </table>

</div>
</div>

integration.component.ts

import { Component, Injectable, OnInit } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { AlfrescoApiService, AlfrescoAuthenticationService } from 'ng2-alfresco-core';

@Injectable()
export class JiveContentService {

 private url = "/api/core/v3/contents?filter=type(document,post)&includeBlogs=true&abridged=true&sort=dateCreatedDesc&count=5";

 constructor(private http: Http) {
 }

 getContent() : Observable<string[]> {
   return this.http
     .get(this.url)
     .map((res:Response) => res.json().list)
     .catch((error:any) => Observable.throw(error.json().error || 'Request error'));
 }
}

@Component({
 selector: 'integration-component',
 templateUrl: './integration.component.html',
 styleUrls: ['./integration.component.css'],
 providers: [JiveContentService]
})
export class IntegrationComponent implements OnInit {

 public jiveContent = [];

 constructor(
   public auth: AlfrescoAuthenticationService,
   private apiService: AlfrescoApiService,
   private jiveService : JiveContentService) {
 }

 // Initialisation of the component.
 ngOnInit() {

   // Retrieve the content from the Jive REST service.
   this.jiveService
     .getContent()
     .subscribe(
       communityContents => { this.jiveContent = communityContents; },
       err => { console.log(err); });
 }

 // Upload the content into the ECM.
 UploadContentOn( pos: number, id: string ) {

   if (this.auth.isEcmLoggedIn()) {

     // Creating a blob content.
     var blob = new Blob([this.jiveContent[pos].content.text], {type: 'plain/text'});

     // Creating options for uploading the content.
     var opts = {
       autoRename: true,
       renditions: 'doclib',
       name: this.jiveContent[pos].subject
     };

     // Uploading the content into Alfresco.
     this.apiService.getInstance().upload.uploadFile(blob, null, null, null, opts).then(
       function () {
         alert("The content has been uploaded into the content repository with success!");
       },
       function (error) {
         console.log('Error during the upload ' + error);
       }
     );
   }
   else {
     alert("The content repository seems to be not connected!");
   }
 }

 // Start the process into the BPM.
 StartProcessOn( pos: number, id: string ) {

   if (this.auth.isBpmLoggedIn()) {

     // Alfresco JS API.
     var alfrescoJsApi = this.apiService.getInstance();

     // Defining the new task.
     var taskRepresentation = new alfrescoJsApi.activiti.TaskRepresentation();
     taskRepresentation.name = "Please review the content.";

     // Creating the task on Activiti.
     alfrescoJsApi.activiti.taskApi.createNewTask(taskRepresentation).then(

       (taskRepresentation) => {

         // Defining the new comment.
         var commentRequest = new alfrescoJsApi.activiti.CommentRepresentation();
         commentRequest.message = this.jiveContent[pos].content.text;

         // Creating the comment on Activiti.
         alfrescoJsApi.activiti.commentsApi.addTaskComment(commentRequest, taskRepresentation.id).then(
           function () {
             alert("The task has been defined into the process repository with success!");
           },
           function (error) {
             console.log('Error during the comment creation ' + error);
           }
         );
       }
     );
   }
   else {
     alert("The process engine seems to be not connected!");
   }
 }
}

As you can see from the source code, the integration.component.ts file contains two declared components: the IntegrationComponent defining the view and the JiveContentService defining the Jive services. As discussed in a previous tutorial, a single file containing several components and modules is not a best practice, but we use this approach as an example, to have a source code more compact and readable.

Now that the IntegrationComponent is created as a new view of the Alfresco ADF application, it’s time to include it in the index.ts file containing the links between the alias of each component and the source code. For this purpose, let’s edit the index.ts file stored into the <my-adf>/app/components folder and modify it, according to the description below.

// Add this at the end of the ‘index.ts’ file.
export { IntegrationComponent } from './integration/integration.component';

At this stage, the IntegrationComponent is correctly setup but not visible to the my-adf application. To make the IntegrationComponent visible to the my-adf application, let’s act on the root module. Edit the app.module.ts file stored into the <my-adf>/app folder and change the content according to the description below.

import {
 ...
 IntegrationComponent, // Add this import.
 ...
} from './components/index';

@NgModule({
   ...
   declarations: [
       ...
       IntegrationComponent, // Add this declaration.
       ...
   ],
   ...
})
export class AppModule {}

Once the IntegrationComponent is visible to the my-adf application, the next step is to define the Angular 2 routing to enable the navigation from and to the view. To develop the routing, let’s act on the app.routes.ts file stored into the <my-adf>/app folder. Below the details of the changes to apply to the content.

import {
 ...
 IntegrationComponent, // Add this import.
 ...
} from './components/index';

export const appRoutes: Routes = [
 ...
 // Add the content below here.
 {
   path: 'integration',
   component: IntegrationComponent,
   canActivate: [AuthGuard]
 },
 ...
];

With the changes above, the IntegrationComponent component is routed with the http://localhost:3000/integration URL (assuming you run the my-adf application on localhost at port 3000).

Please remember that after each saving of one or more files, the Angular 2 Webpack will update the my-adf application, trying to refresh the view with the latest updates. Of course, before completing this task, the my-adf application is not ready to be used and some errors could appear in the log.

Now that all the changes are completed and the integration is available as a view, we need to proxy the requests to the Alfresco Community Portal to avoid the Cross-origin resource sharing (CORS) issue. To solve it, edit the webpack.dev.js file into the <my-adf>/config folder and change the content according with what is described below.

...
devServer: {
 host: '0.0.0.0',
 port: 3000,
 inline: true,
 historyApiFallback: true,
 stats: 'minimal',
 proxy: {
   '/api/core/v3/**': {
     target: 'https://community.alfresco.com',
     secure: false, changeOrigin: true
 }
}
...

Now that all the changes are completed and the integration is available as a view, let’s add the new page to the two available menus (one on the top bar and the other to the hidden left panel). To reach the goal, let’s edit the app.component.html file stored into the <my-adf>/app folder and change the content according with what is described below.

...

<!-- Navigation. We hide it in small screens. -->
<nav class="mdl-navigation mdl-layout--large-screen-only">
...
<a class="mdl-navigation__link" data-automation-id="activiti" href="" routerLink="/activiti">Activiti</a>
<!-- Add the content below -->
<a class="mdl-navigation__link" data-automation-id="integration" href="" routerLink="/integration">Integration</a>
...
</nav>

...

<span class="mdl-layout-title">Components</span>
<nav class="mdl-navigation">
<a class="mdl-navigation__link" href="" routerLink="/" (click)="hideDrawer()">Home</a>
<!-- Add the content below -->
<a class="mdl-navigation__link" href="" routerLink="/integration" (click)="hideDrawer()">Integration</a>
...
</nav>

Saving the app.component.html file, you will see that the application will be automatically updated and the new items will be visible into the menus. Of course, clicking on the links, both will show the integration view. In the picture below you can see how the home look like, with highlights on both the menu items.

The home page of my-adf application with the new menu items highlighted.

Now that the new view with all the components is up and running into the Alfresco ADF application, let’s see something more about the role of the components and how they work for the final goal of the integration.

Description of the solution

In this paragraph we are going to describe the details of the solution in terms of components, strategy and integrations. We won’t detail here the development around building a new view and adding the new view to the menu (take a look at ADF 105 for further details). We won’t detail neither how to proxy the requests and the CORS issue (look at the various resources on the web about CORS for further details). Instead of this, we would like to concentrate the discussion on the integration.component.html and ts file. More in detail about how to face the integration with an external (and 3rd party) service.

One relevant part of the solution is represented by the JiveContentService component. This Angular 2 component manages all the interactions with the 3rd party services (in our case the Alfresco Community Portal) and has one mission only: raise an HTTP request to the Jive Portal and receive the response into a JSON structure as a result. This task in particular is developed into the getContent method and is developed as a standard HTTP request in Angular 2. If you will need to add more that one service to interact with, this is the right place where to manage all the settings and the exchange of information like: the protocol, the expected result, the error management, etc.

Looking at the source code, the JiveContentService component is decorated with an Injectable mark. This is the standard way for Angular 2 to mark a class as available to an injector for instantiation. This feature in particular is useful because the component is used (throughout the injection) into the IntegrationComponent (look the IntegrationComponent’s constructor for further details). If you need more information about Angular 2 and the dependency injection, please check the official documentation about this topic.

Now that we know the role of the JiveContentService component, let’s see the IntegrationComponent and its definition. The first relevant thing about IntegrationComponent is the inheritance. As you can see, IntegrationComponent extends the OnInit interface. The OnInit interface is used because of the ngOnInit method is called right after the directive's data-bound properties have been checked for the first time, and before any of its children have been checked. As you can see from the ngOnInit definition, at run time the JiveContentService is used to extract the list of the latest community content and fill in the JiveContent property with an array of data (in JSON format).

Once the jiveContent property is properly filled during the component init, the integration.component.html file renders the content. Looking at the HTML source code, the core of rendering is developed with the command below.

<tr *ngFor="let content of jiveContent; let i = index;" #elem>

As you can see, the commands iterate on the jiveContent property and for each item generate the row of the table with some information visible (author, number of views, content preview) and the actions related to the content.

The actions provided in a vertical menu are two: uploading the content into the Content Platform (using the method UploadContentOn) and starting a review task into the Process Services (using the method StartProcessOn). Both the two methods are defined and part of the IntegrationComponent.

Talking about the UploadContentOn method, it takes as parameter the position of the selected content in the JiveContent property and develops the upload of the content into the ECM system. The very first thing that the UploadContentOn method do, is about checking the login into the ECM system (this.auth.isEcmLoggedIn()). If the user is logged in, the content is created starting from the JiveContent item and uploaded into the ECM system using the Alfresco JavaScript API.

Talking about the StartProcessOn method, it takes as parameter the position of the selected content in the JiveContent property and starts the review task into the BPM system. The very first thing that the StartProcessOn method do, is about checking the login into the BPM system (this.auth.isBpmLoggedIn()). If the user is logged in, a new generic task is created starting from the JiveContent item and raised into the BPM system using the Alfresco JavaScript API.

In both cases, a very simple message is shown to the user if the operation succeed or fails. Exactly in the UploadContentOn method and StartProcessOn method is where the integration happens with the ECM and BPM systems respectively.

The integration in action

Now that we know how (and why) the integration with the Alfresco Community Portal works, let’s see it in action into the my-adf application with the new integration view.

Starting from the home page as shown in the picture above, let’s click on the integration item and we will land into a page similar to the one presented below.

The integration view of the my-adf application.

As you can see, a preview of the community content is shown in an ADF table. Some useful information about the author, the number of views, the title and a portion of the content is visible. On the right a vertical menu indicates some possible action on the content, as described below in the picture.

The integration view with highlight on the action menu.

Clicking on the Upload as content item, the IntegrationComponent uploads the preview as new content into the ECM system, directly in the root folder. Below a picture showing the result in case of success.

Uploading a content with success.

To see the result from the ECM perspective, below a picture showing the ECM content with the new document stored into it.

ECM view with the new content stored.

Now that we know how the integration with the ECM system works, let’s see the same thing for the BPM integration. Starting from the integration view, let’s click on the Start a new review process item in the vertical menu. Requesting this action, the IntegrationComponent raises a new generic task into the BPM system. Below a picture showing the result in case of success.

Starting a review task with success.

To see the result from the BPM perspective, below a picture showing the task with the portion of the content as comment, stored into it.

BPM view with the new task of review.

Disclaimer

All the content available in this tutorial has been developed and tested using Alfresco ADF v1.1.0 LA release. Below the full list of platforms, languages, frameworks and tools used here.

  • Linux Ubuntu 16.04.01 LTS as Operating System.
  • Alfresco Community Edition 201612 GA release for Linux (running on port 8080).
  • Activiti Enterprise Edition 1.5.2.1 release for Linux (running on port 9999).
  • Jive SBS 2016.3.6.0 available at the URL https://community.alfresco.com.
  • Node.js version 6.9.4.

Each variation to the listed versions and tools could affect the success of the tutorial, even if the involved technologies and tasks have been defined to be as more general as possible, not related to any specific context or platform. Please let us know for any issue or problem, requesting for support into the Community Portal dedicated to the Alfresco ADF or to the Gitter space dedicated to the Alfresco Angular 2 components.

Conclusion

In this tutorial we saw hot to integrate different services from Alfresco ECM and Activiti BPM, into an existing application built using the Alfresco Application Development Framework (named also Alfresco ADF). The Alfresco ADF is provided by Alfresco as Open Source, to build custom applications on top of the Activiti BPM Engine and Alfresco ECM Repository. For further details about the Alfresco ADF, please refer to the alfresco-ng2-components GitHub Project and take a look to the Alfresco Catalog, with a friendly (and technical) overview of all the available components. For requests of support, questions and feedback, don’t forget to participate to the Community of Developers dedicated to the ADF or to the Gitter space dedicated to the Alfresco Angular 2 components.