Sending HTTP requests
Introduction
Noumena Cloud supports integration with external HTTP services through a dedicated HTTP bridge. This guide will help you set up the HTTP bridge and integrate it with your Noumena Cloud application.
Getting Started
To get started with the HTTP bridge, you need to enable it in your Noumena Cloud application. Once enabled, you can configure the Authentication methods to be added to the HTTP bridge and implement the necessary logic in NPL to send HTTP requests and handle responses.
Dependencies
npl-connectors-library
needs to be added as a dependency to the NPL project.
Here is an example of how to add the dependency and unpack the library using Maven:
<!-- pom.xml -->
<!-- add dependency -->
<dependencies>
<!--...-->
<dependency>
<groupId>com.noumenadigital.contrib</groupId>
<artifactId>npl-connectors-library</artifactId>
<version>1.0.19</version>
</dependency>
</dependencies>
<!-- unpack library -->
<build>
<plugins>
<!--...-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-npl-contrib</id>
<goals>
<goal>copy-dependencies</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<outputDirectory>${project.build.directory}/contrib</outputDirectory>
<includeGroupIds>com.noumenadigital.contrib</includeGroupIds>
<includeArtifactIds>npl-connectors-library</includeArtifactIds>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
<execution>
<id>unzip-contrib-libs</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<includeArtifactIds>npl-connectors-library</includeArtifactIds>
<fileMappers>
<fileMapper implementation="org.codehaus.plexus.components.io.filemappers.RegExpFileMapper">
<pattern>npl-connectors-library-.*/connector</pattern>
<replacement>connector</replacement>
</fileMapper>
</fileMappers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Configuring the HTTP Bridge
HTTP bridge can be enabled in your Noumena Cloud application by navigating to the services section in the Noumena Cloud portal and toggling the HTTP bridge service on. This will allow you to use the HTTP bridge for interacting with HTTP APIs directly from your NPL applications.
Once the HTTP bridge is enabled, it can be configured to connect to different APIs. At the moment, the bridge only supports APIs with the JSON request and response format.
Authentication configuration can be set for each API directly through the Noumena Cloud portal.
Example of the configuration file:
nyala.de:
auth_type: api_key_with_hmac
api_key: xxx
api_secret: xxx
# This is to support the token-fetching auth flow, as an example.
# This can be setup in https://developer.spotify.com/dashboard.
spotify.com:
auth_type: bearer_token
token_url: https://accounts.spotify.com/api/token
# Request headers.
headers:
Content-Type: application/x-www-form-urlencoded
body:
client_id: xxxx
client_secret: xxxx
grant_type: client_credentials
Using the HTTP Bridge in NPL
To use the HTTP bridge in your NPL applications, you need to implement the necessary logic to send HTTP requests and handle responses. You will then be able to read responses from the HTTP bridge and process them in your NPL applications.
To facilitate the integration with external HTTP services, NPL data types and functions can be generated from the OpenAPI specification of the HTTP service you want to integrate with.
HTTP requests and responses
Once the HTTP bridge is configured, you can use it in your NPL applications. The HTTP bridge provides a set of APIs that can be used to interact with external HTTP services using the standard NPL notify-resume pattern. It provides the following types of messages:
HttpRequestExecutionMessage
: Used to perform a HTTP request to an external HTTP service.
struct HttpRequest {
method: HttpMethod,
url: Text,
data: HttpData
};
struct HttpResponse {
executionStatus: ExecutionStatus,
statusCode: Number,
data: HttpData
};
notification HttpRequestExecutionMessage(httpRequest: HttpRequest) returns HttpResponse;
HTTP request execution
permission[worker] executeWithData(request: HttpRequest) {
notify HttpRequestExecutionMessage(request) resume responseCallback;
}
@api
permission[worker] responseCallback(res: NotifyResult<HttpResponse>) | pending {
match (res) {
is NotifySuccess<HttpResponse> -> match (res.result.executionStatus) {
ExecutionStatus.Success -> this.httpRequestExecuted[worker](res.result)
ExecutionStatus.Failure -> this.httpRequestFailed[worker](res.result)
}
// This will never be called -- the HTTP service response, including errors, is passed via NotifySuccess.
is NotifyFailure -> {}
};
};
NPL code Generation
The HTTP bridge supports code generation for NPL applications. You can generate NPL code from the
OpenAPI specification of the HTTP service you want to integrate with using the openapi-npl-codegen
Maven plugin. This plugin will generate NPL data types based on the OpenAPI specification that can be
used in the NPL applications.
Example of the Maven plugin configuration for generating NPL code:
<plugin>
<groupId>com.noumenadigital.contrib</groupId>
<artifactId>openapi-npl-codegen</artifactId>
<version>1.0.20</version>
<executions>
<execution>
<id>npl-datatypes-generation</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<openApiSpecPath>${project.build.sourceDirectory}/integrations/nyala/openapi.json</openApiSpecPath>
<outputDataTypesPath>${project.build.sourceDirectory}/integrations/nyala/generated/datatypes.npl</outputDataTypesPath>
<nplPackageName>integrations.nyala.generated</nplPackageName>
</configuration>
</execution>
</executions>
</plugin>
Example of the generated NPL code:
struct CountryDto {
id: Optional<Text>,
iso: Optional<Text>,
isoCode3: Optional<Text>,
name: Optional<Text>
}
function serializeCountryDto(r: CountryDto) returns HttpData -> {
var ret = listOf<Pair<Text, HttpData>>();
if (r.id.isPresent()) {
ret = ret.with(Pair("id", serialize(r.id.getOrFail())));
};
if (r.iso.isPresent()) {
ret = ret.with(Pair("iso", serialize(r.iso.getOrFail())));
};
if (r.isoCode3.isPresent()) {
ret = ret.with(Pair("isoCode3", serialize(r.isoCode3.getOrFail())));
};
if (r.name.isPresent()) {
ret = ret.with(Pair("name", serialize(r.name.getOrFail())));
};
return ret;
}
function deserializeCountryDto(data: HttpData) returns Optional<CountryDto> -> {
return match (data) {
is List<Pair<Text, HttpData>> -> optionalOf(CountryDto(
id = if (getter(data, "id").isPresent()) { deserializeText(getter(data, "id").getOrFail()); } else { None(); },
iso = if (getter(data, "iso").isPresent()) { deserializeText(getter(data, "iso").getOrFail()); } else { None(); },
isoCode3 = if (getter(data, "isoCode3").isPresent()) { deserializeText(getter(data, "isoCode3").getOrFail()); } else { None(); },
name = if (getter(data, "name").isPresent()) { deserializeText(getter(data, "name").getOrFail()); } else { None(); }
))
else -> None()
};
}
function deserializeListOfCountryDto(data: HttpData) returns List<CountryDto> -> {
return match (data) {
is List<HttpData> -> data.map(function(v: HttpData) -> deserializeCountryDto(v).getOrFail())
else -> listOf<CountryDto>()
};
}
Response can be parsed using the generated NPL code dedicated for the API response.
For example, if you have a response of the CustomerAccountDtoListApiResponse
type,
you can deserialize it as follows:
permission[http_connector] getCustomerListSucceeded(response: HttpResponse) {
var parsedResponse: CustomerAccountDtoListApiResponse = deserializeCustomerAccountDtoListApiResponse(response.data).getOrFail();
if (parsedResponse.data.isPresent()) {
var customerData = parsedResponse.data.getOrFail();
// Process customer data.
} else {
error(
"Nyala API: failed to fetch a list of customers. Error codes: " + parsedResponse.errorMessageCodes.toText()
);
// Process API error.
};
}