In Part One, you saw how to create an Alfresco Share dashlet that uses out-of-the-box Aikau widgets to chart data. In that example, the data came from a repository tier web script.
In this blog post, Part Two, you’ll see how to fetch the data from an external API called from the Share tier. The external API could be anything, but for this example we’ll implement it with Spring Boot. We’ll add a Docker module to the project so that the Spring Boot application will run in a container alongside the Alfresco and Share containers that are part of all Alfresco SDK projects. Finally, we’ll secure the external API so that only authenticated Alfresco users can invoke it.
Steps
This code is available in GitHub. Just like Part One, each of the steps below is tagged. After you clone the repository you can switch between steps by doing git checkout step4
or git checkout step5
etc. I’m starting with “Step 4” because Part One covered steps 1 – 3.
If you are going to follow along, and you haven’t already, clone the project:
git clone https://github.com/jpotts/alfresco-share-chart-example.git
Step 4: Add an external REST API with Spring Boot
If you are following along:
git checkout step4
The existing code invokes a web script in the repository tier that returns sales data. A more likely case is that the sales data comes from a REST API running external to Alfresco. In this step we’ll build a small Spring Boot application that simulates the external sales data API. To make things simple, we’ll add the new application as an additional module to the alfresco-share-chart-example project.
Switch to the root of the alfresco-share-chart-example project and create a new directory named alfresco-chart-example-api.
Within that, add a new pom.xml file that pulls in the Spring Boot dependency:
There are many ways to organize a Spring Boot app. I like to create separate files for my application, model, controllers, and services:
- src/main/java/com/emcarchitect/alfresco/chart/example/ChartExampleApiApplication.java
- src/main/java/com/emcarchitect/alfresco/chart/example/model/SalesData.java: A POJO that models our sales data.
- src/main/java/com/emcarchitect/alfresco/chart/example/services/SalesDataService.java: The service class fetches data from its source. In this example it is hardcoded.
- src/main/java/com/emcarchitect/alfresco/chart/example/web/SalesDataController.java: The controller implements the REST API.
We’ll set the application port to 8000 in the application.properties file:
Test by switching into alfresco-chart-example-api and running mvn clean install
. Then, run java -jar target/alfresco-chart-example-api-1.0-SNAPSHOT.jar
. Once the app comes up you should be able to invoke:
http://localhost:8000/api/salesData
And see the JSON response.
Use ctl+c to stop the application.
Step 5: Create a Docker module for the example API
If you are following along:
git checkout step5
The Alfresco SDK uses Docker to run Alfresco and Alfresco Share, so we’ll do the same. To do that, create a new module directory called alfresco-chart-example-api-docker.
Within that, add a new pom.xml file. This pom.xml pulls in the application created in the previous step as a dependency so that it can be easily copied to the Docker image.
Now add a Dockerfile that builds the image. There are a lot of ways you can deploy Spring Boot apps with Docker. In this example, we’ll just use an Alpine Linux image with the JDK, and launch the Spring Boot app directly from the JAR, just like you did when you tested the app in the previous step.
When we start Alfresco with the run.sh script that is included with the SDK it builds the Alfresco containers, starts them up, and networks them together using Docker Compose. Our container should be included in that, so we’ll update the Docker Compose file that already exists off the root of the project source code and add corresponding tasks to the run scripts:
Finally, make sure to add the two new module directories to the root pom.xml:
Test by changing to the alfresco-chart-example-api-docker directory and running mvn clean install
. Then, switch to the root of the project source and run ./run.sh reload_api
or run.bat reload_api
. This should cause a Docker container to start up with the Spring Boot application running. Again, you should be able to invoke:
http://localhost:8000/api/salesData
And see the JSON response.
Run ./run.sh stop
or run.bat stop
to stop the container.
At this point you should be able to do ./run.sh build_start
or run.bat build_start
to start up Alfresco, Alfresco Share, and the Spring Boot Example API, all at once.
Step 6: Update Share to fetch data from the API
If you are following along:
git checkout step6
Now Alfresco, Share, and the API are running, but the Share chart dashlet is still pulling its data from the repository tier web script. It’s time to update the dashlet to instead pull its data from the Spring Boot example API.
First, create a web script that the chart dashlet will call to get its data. This is similar to the one we created in the repository tier in Part One, but this one is going into the Share tier. You could run this in the repository tier, but putting it in the Share tier saves us one hop.
- alfresco-share-chart-example-share/src/main/resources/alfresco/web-extension/site-webscripts/com/ecmarchitect/data/data.get.desc.xml: Web script descriptor.
- alfresco-share-chart-example-share/src/main/resources/alfresco/web-extension/site-webscripts/com/ecmarchitect/data/data.get.js: Web script controller that invokes the sales data API.
- alfresco-share-chart-example-share/src/main/resources/alfresco/web-extension/site-webscripts/com/ecmarchitect/data/data.get.json.ftl: Web script view template that formats the sales data in the JSON structure the dashlet expects.
The web script calls the external API and returns the data in the format that the dashlet is expecting. To make that call, we’re using a built-in root-scoped object called “remote” that is able to call external REST APIs:
var connector = remote.connect("example-api");
var dataString = connector.get("/api/salesData?ticket=" + ticket);
That connector ID, “example-api”, refers to Share configuration that includes the actual host that is being connected to:
In this example, the API is running as a Docker container, so the endpoint-url refers to the Docker Compose service name:
<endpoint-url>http://alfresco-chart-example-api:8000</endpoint-url>
Finally, the CustomReportService we created in Part One is still referring to the repository tier web script. It needs to be updated to instead call the new Share tier web script:
You’ve made changes to Alfresco Share, so do ./run.sh reload_share
or run.bat reload_share
to rebuild and run the Share container. When it comes back up, go to Share at http://localhost:8180/share, log in, and go to the test site that has your chart dashlet.
The chart dashlet should now display data coming from the Spring Boot API.
Step 7: Secure the API to only allow requests from authenticated Alfresco users
If you are following along:
git checkout step7
The Alfresco Share sales summary dashlet is now pulling data from an external REST API. If you don’t mind that anyone can fetch that data from the REST API at any time, there is no need to go any further. In this example, let’s assume that only authenticated Alfresco users should be allowed to retrieve data from the REST API. How can we modify the API and the web script that calls it to meet that requirement?
First, let’s add a new service to alfresco-chart-example-api that knows how to validate Alfresco tickets:
- alfresco-chart-example-api/pom.xml: Adds the Apache HTTP library as a dependency.
- alfresco-chart-example-api/src/main/java/com/emcarchitect/alfresco/chart/example/services/AlfrescoService.java: Uses the Alfresco Public REST API to validate the ticket.
- alfresco-chart-example-api/src/main/resources/application.properties: Updated to configure the Alfresco server URL.
- alfresco-chart-example-api/src/main/java/com/emcarchitect/alfresco/chart/example/exceptions/AlfrescoServiceException.java
- alfresco-chart-example-api/src/main/java/com/emcarchitect/alfresco/chart/example/exceptions/NotAuthorizedException.java
The service uses the Alfresco 5.2 public REST API to ask Alfresco if the ticket is valid.
With that in place, the SalesDataController can be updated to validate the ticket before returning a response:
If you do a run.sh reload_api
or run.bat reload_api
at this point and try to invoke http://localhost:8000/api/salesData you should see a 401 returned, which is exactly what we want–if you don’t have a valid ticket you cannot invoke the sales data API.
To test a successful call, use curl to grab a ticket:
jpotts@Metaversant alfresco-share-chart-example % curl -X POST -H 'content-type: application/json' http://localhost:8080/alfresco/s/api/login -d'{"username":"admin", "password":"admin"}'
{
"data":
{
"ticket":"TICKET_01b44089022f98740c3eb27009f49e787f9fb213"
}
}
And then pass the ticket to the sales data API and the response should be successful:
jpotts@Metaversant alfresco-share-chart-example % curl "http://localhost:8000/api/salesData?ticket=TICKET_01b44089022f98740c3eb27009f49e787f9fb213"
[{"region":"North","amount":125},{"region":"South","amount":125},{"region":"East","amount":125},{"region":"West","amount":125}]
So far, so good.
The last step is to modify the Share tier web script that fetches the sales data from the API to pass along the ticket. One gotcha here is that Share does not have direct access to the ticket, so we’ll first add a tiny repository tier web script that returns the ticket:
- alfresco-share-chart-example-platform/src/main/resources/alfresco/extension/templates/webscripts/custom/ticket.get.desc.xml: Web script descriptor for the ticket web script.
- alfresco-share-chart-example-platform/src/main/resources/alfresco/extension/templates/webscripts/custom/ticket.get.js: Web script controller that puts the user’s current ticket on the model.
- alfresco-share-chart-example-platform/src/main/resources/alfresco/extension/templates/webscripts/custom/ticket.get.json.ftl: Web script view template that produces a JSON response.
And then we can modify the Share tier web script to fetch the ticket from the repository tier and pass it along to the sales data API:
The controller uses the same “remote” root-scoped object it used previously, but it relies on the default connector, which is the Alfresco repository web script connector, to fetch the ticket:
// Need to get the user's ticket from the repo
var ticketString = remote.call("/custom/ticket");
var ticketResponse = jsonUtils.toObject(ticketString);
var ticket = ticketResponse.ticket;
// Get the sales data from the API
var connector = remote.connect("example-api");
var dataString = connector.get("/api/salesData?ticket=" + ticket);
You’ve now made changes to both the repository tier and the share tier. Either reload each one with ./run.sh reload_acs
and ./run.sh reload_share
or do a ./run.sh stop
to stop all the running containers followed by a ./run.sh build_start
to start everything back up.
That’s it. You’ve now got an external API that returns data only to authenticated Alfresco users and a Share dashlet that displays that data in a pie chart.