Example REST client configurations
This page presents examples of REST client definitions that leverage the possibilities of version 2 of the Magnolia REST Client module.
The examples are derived from the configurations in the declarative-rest-demo light module, a subfolder in the Magnolia rest-client repository.
As this page focuses mainly on REST client configurations, below we deal with template definitions and template scripts only marginally.
See also the Hello Magnolia - with content via REST tutorial page containing an additional and easy to configure scenario. |
Prerequisite: Magnolia
To test the REST client configurations, you need:
-
A running Magnolia instance.
If you don’t have Magnolia installed and running, go to the page called Installing Magnolia through npm CLI and complete the steps described there. -
The
declarative-rest-demo
light module.You can get a copy of the light module by cloning the https://git.magnolia-cms.com/projects/MODULES/repos/rest-client/ repository and extracting the declarative-rest-demo
directory to the folder defined by themagnolia.resources.dir
property. If you installed Magnolia using CLI, it is thelight-modules
folder, located in the root of your Magnolia installation:<your-Magnolia-installation-folder> ├── apache-tomcat/ ├── downloads/ └── light-modules/ └── declarative-rest-demo/
Case 1: Simple configuration
In this case, a REST client is configured to send a request to the /countries
path of the https://placesapi.dev/api/v1/
endpoint to get a list of countries:
Client configuration
The client is called countries
, its name is derived from the configuration file’s name. The REST call is named allCountries
in this definition.
baseUrl: https://restcountries.com/v3.1
restCalls:
allCountries:
class: info.magnolia.rest.client.call.GetRestCall
entityClass: com.fasterxml.jackson.databind.JsonNode
path: /all
Component template script
Example component script that renders the list of countries.
[#assign response = restfn.call("countries", "allCountries")]
<h2>Countries</h2>
<ul>
[#list response.elements() as country]
<li>${country.get("name").official.textValue()}</li>
[/#list]
</ul>
Case 2: Configuration with a security scheme
If the REST API service needs an authentication of a REST call, you can configure a security (authentication) scheme for it under the securitySchemes
node. You can use scheme types basic
and bearer
, which are available out of the box in the REST client module.
For an example with the basic
scheme, see basic security scheme.
Below are more example configurations.
Bearer token with expiry duration
Example configuration of a client called bearer-security
utilizing the bearer security scheme and an expiration limit. The scheme’s configuration is defined under the bearerTokenWithExpiryDuration
node, the authentication call is configured under authenticateWithDurationAsLong
.
baseUrl: http://localhost:${wiremockPort}
securitySchemes:
bearerTokenWithExpiryDuration:
$type: bearer
restClientName: bearer-security
restCallName: authenticateWithDurationAsLong
tokenJsonPath: $.access_token
expiry:
$type: duration
expiryJsonPath: $.expires_in
expiryTimeUnit: SECONDS
restCalls:
getDataWithBearer:
method: GET
entityClass: com.fasterxml.jackson.databind.JsonNode
path: /getDataWithBearer
securityScheme: bearerTokenWithExpiryDuration
authenticateWithDurationAsLong:
method: POST
entityClass: com.fasterxml.jackson.databind.JsonNode
path: /oauth2/token
headers:
Content-Type: application/x-www-form-urlencoded
X_Test_Expiry_Type: DURATION_AS_LONG
OAuth2 bearer security scheme with expiry duration
Example configuration of a client called knowledgeSource
also utilizing the bearer security scheme and an expiration limit. The scheme’s configuration is defined under the oauth2
node. The authentication call is configured under authenticate
in a separate knowledgeSourceAuth.yaml
configuration file.
This example showcases a situation in which you are using a third-party authentication service for your REST calls. In other words, the authentication URL is different from the service URL. In this particular example configuration, the domain where the token is served from is |
baseUrl: https://api.example.com/systems/client/v1
securitySchemes:
oauth2:
$type: bearer
restClientName: knowledgeSourceAuth
restCallName: authenticate
tokenJsonPath: $.access_token
expiry:
$type: duration
expiryJsonPath: $.expires_in
expiryTimeUnit: SECONDS
restCalls:
getUser:
method: get
entityClass: com.fasterxml.jackson.databind.JsonNode
path: /users/123
securityScheme: oauth2
getUsersInOrg:
method: get
entityClass: com.fasterxml.jackson.databind.JsonNode
path: /organisations/abc-123/users
securityScheme: oauth2
baseUrl: https://login.example.net/089a6470-d3a7-46a4-8852-74c0c698c729
restCalls:
authenticate:
headers:
Content-Type: "application/x-www-form-urlencoded"
path: /oauth2/v2.0/token
method: post
entityClass: com.fasterxml.jackson.databind.JsonNode
body: 'client_id=62336501-fbe2-4fbe-9bba-1b0b4a8c8573&client_secret=Sih8Q~tW-Sdnxc4tdvECHfwYGIJ6jaP4TeKfzcVr&grant_type=client_credentials&scope=https%3A%2F%2Fapi.app.example.com%2Fsgc%2F.default'
Case 3: Configuration with a JavaScript model
This case makes use of a REST client called books
and a JavaScript model called search
. The model invokes the configured searchByIsbn
call that queries the https://openlibrary.org/dev/docs/api/read API with a request for a record of a book identified by the ISBN 13 number.
The component template script then renders the book’s title, number of pages and its cover image (if available).
Client configuration
baseUrl: http://openlibrary.org/api
restCalls:
searchByIsbn:
method: get
entityClass: java.lang.String
path: /volumes/brief/isbn/{isbn}.json
defaultValues:
isbn: 978-3-16-148410-0
The path property uses the {isbn} template, where the JavaScript model injects the value submitted by the user in the search field.
|
Component template script
[#assign isbn = ctx.getParameter("isbn")!]
[#assign isSubmitted = ctx.getParameter("submit")?has_content?then(true, false)]
[#if isbn?has_content && isSubmitted]
[#assign result = model.search(isbn)]
[/#if]
<h2>Search books by isbn13</h2>
<p>For example: 9780261103252, 9780582517349</p>
<div style="margin:10px">
<form method="get">
<p>
<label for="isbn">Isbn</label>
<input type="text" name="isbn" />
</p>
<input type="submit" name="submit" value="Search" />
</form>
[#if result?has_content]
<p>Title: ${result.title}</p>
<p>Number of pages: ${result.number_of_pages}</p>
<img src="${result.cover.large}" alt="Cover"/>
[/#if]
</div>
JavaScript
var BookModel = function () {
var restClient = restClientFactory.createClientIfAbsent(restClientRegistry.getProvider("books").get());
this.search = function (isbn) {
var result = restClient.invoke("searchByIsbn", { isbn: isbn});
var data = JSON.parse(result.getEntity());
if (data && data.records) {
var keys = Object.keys(data.records);
return data.records[keys[0]].data;
}
return null;
}
};
new BookModel();
Case 4: Configuration using an OpenAPI schema
The benefit of a configuration using an OpenAPI schema is that all that is required to configure a REST client is reduced just to two properties, class
and schemaUrl
.
-
For the
class
, you useinfo.magnolia.openapi.client.OpenApiRestClientDefinition
. -
For the
schemaUrl
, you provide a link to an existing definition (file or URL) complying with the OpenAPi v3 specification.
Client configuration
class: info.magnolia.openapi.client.OpenApiRestClientDefinition
schemaUrl: https://en.wikipedia.org/api/rest_v1/?spec
Component template script
[#assign aDateTime = .now]
[#assign results = restfn.call("wikipedia", "onThisDay", {"type":"all", "mm":aDateTime?string["MM"], "dd":aDateTime?string["dd"]}) ] (1)
<h2>What happened on ${aDateTime?string["MMMM"]} ${aDateTime?string["dd"]} in the past?</h2>
[#list results.get("selected").elements() as event]
<p>
${event.get("text").textValue()} (${event.get("year").intValue()?c}) (2)
</p>
[/#list]
<p>(Source: en.wikipedia.org)</p>
1 | The alias onThisDay is used to target the endpoint path since the alias is defined in the schema. If this weren’t the case, you can always refer to a call name using the following pattern: method:/path/pathAction . |
2 | At the end of the directive, the c FreeMarker builtin is used to render the intValue() for the year without formatting for human audience, for example as 2019 rather than 2,019 . |
After passing the template values for type, month and date, the component displays a list of notable events that happened on the given day at some point in the past, for example: