HTTP endpoint database
HttpEndpointDatabase allows you to integrate with a third-party service that uses an HTTP API. A subclass of HttpEndpointDatabase can convert Data queries to HTTP API calls and return the results as if they were retrieved from the Dari underlying database. The search experience is the same for the end user, whether a query is on the underlying database or converted to an HTTP request to a third-party API.
HttpEndpointDatabase includes methods for extracting Dari query predicates and values for constructing HTTP API calls. It also includes methods that you can override to open and close connections to a third-party service, and to save objects to the underlying Dari database.
An HttpEndpointDatabase subclass must implement the doInitialize and readPartial methods inherited from the Dari AbstractDatabase and Database classes, respectively.
The following example shows a simple implementation of HttpEndpointDatabase that leverages the Getty REST Search API to retrieve images from that service. The GettyDatabase class gets the predicate and associated values from a Dari query, constructs an HTTP request that is sent to the Getty API, and returns the Getty response as a Dari PaginatedResult object.
public class GettyDatabase extends HttpEndpointDatabase {
private static final Logger LOGGER = LoggerFactory.getLogger(GettyDatabase.class);
public static final String API_KEY_SUB_SETTING = "apiKey";
public static final String API_URL_SUB_SETTING = "apiUrl";
public static final String DEFAULT_API_URL = "https://api.gettyimages.com/v3";
private String apiUrl;
private String apiKey;
public String getApiUrl() {
return apiUrl;
}
public void setApiUrl(String apiUrl) {
this.apiUrl = apiUrl;
}
public String getApiKey() {
return apiKey;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
@Override
protected void doInitialize(String settingsKey, Map<String, Object> settings) {
setApiUrl(ObjectUtils.to(String.class, settings.get(API_URL_SUB_SETTING)));
setApiKey(ObjectUtils.to(String.class, settings.get(API_KEY_SUB_SETTING)));
}
@Override /* Must be implemented */
public <T> PaginatedResult<T> readPartial(Query<T> query, long offset, int limit) {
String apiKey = getApiKey();
if (apiKey == null) {
return PaginatedResult.empty();
}
try {
String apiUrl = ObjectUtils.firstNonBlank(getApiUrl(), DEFAULT_API_URL);
Predicate predicate = query.getPredicate();
final String fields = "id,title,display_set";
URL url = new URL(StringUtils.addQueryParameters(apiUrl + "/search/images",
"phrase", findQueryStringPredicateValue(predicate),
"fields", fields,
"page", offset + 1,
"page_size", limit));
HttpClient httpClient = HttpClientBuilder.create().build();
HttpGet get = new HttpGet(url.toString());
get.addHeader("Api-Key", apiKey);
HttpResponse response = httpClient.execute(get);
if (response == null) return PaginatedResult.empty();
Map<String, Object> responseMap = (Map<String, Object>) ObjectUtils
.fromJson(EntityUtils.toString(response.getEntity()));
List<Object> images = ((List<Object>) responseMap.get("images"));
if (images != null) {
List<Object> items = images.stream()
.map(image -> (Map<String, Object>) image)
.filter(Objects::nonNull)
.collect(Collectors.toList());
return Optional.ofNullable((Long) responseMap.get("result_count"))
.map(count -> new PaginatedResult(offset, limit, count, items))
.orElse(new PaginatedResult(offset, limit, items));
}
else return PaginatedResult.empty();
} catch (IOException error) {
LOGGER.error("Unable to perform Getty request!", error);
}
return PaginatedResult.empty();
}
}
-
Final variables that refer to the URL and security key for the Getty REST API. The API_URL_SUB_SETTING specifies the host for the API, and the API_KEY_SUB_SETTING specifies the security key issued by the service. These values are set in the client. A default host site is set on DEFAULT_API_URL in the event that the Getty API URL is not set in the client.
-
Implementation for the doInitialize method, which sets the Getty URL and security key. Instead of calling doInitialize, a client can set the Getty URL and security key using the public setter methods setApiUrl and setApiKey.
-
Gets the Getty security key.
-
Gets the URL for the Getty REST API.
-
Gets the predicate from the Dari query passed to the method.
-
Initializes a fields variable that specifies the data to be retrieved from the Getty service.
-
Creates a URL object to be passed in the HTTP request to the Getty service. The URL specifies the HTTP endpoint (/search/images) and the fields variable. Also included in the URL is phrase, the value of the Dari predicate. This value is retrieved with the HttpEndpointDatabase#findQueryStringPredicateValue method.
-
Creates HTTP-related objects and submits the request to the Getty service. JSON data returned from the service is set on response.
-
Creates a list of objects from the JSON image data.
-
Creates a list of objects to construct a PaginatedResult object returned by the method.
Objects retrieved from a third-party service are represented as ExternalItem objects in Brightspot. External items are associated with a source database provider, which specifies the Database object that retrieves data from a third-party service.
For GettyDatabase that integrates with the Getty third-party service, the GettyDatabaseProvider is the source database provider. It provides the glue between the ExternalItem objects that represent retrieved Getty images and GettyDatabase. GettyDatabaseProvider implements SourceDatabaseProvider#get, which creates the GettyDatabase instance and sets the Getty API key on the instance. The key is retrieved from the Brightspot UI.
public class GettyDatabaseProvider implements SourceDatabaseProvider {
@Override
public Database get(ObjectType type) {
GettyDatabase db = new GettyDatabase();
db.setEnvironment(Database.Static.getDefault().getEnvironment());
db.setApiKey(Application.Static.getInstance(CmsTool.class).as(GettySettings.class).getApiKey());
return db;
}
}