Develop custom opportunity publishers and integrate them with POB.
Last Updated: 27 May 2022 • Page Author: Jillur Quddus
Overview
This page is intended for Java software engineers who wish to develop a custom publisher to publish procurement opportunities to any downstream application or system (in addition to the native publishers provided by POB).
Development
POB can be easily extended to publish procurement opportunities to any downstream application or system. To create a custom publisher, simply extend the OpportunityPublisher abstract class found in the $POB_BASE/pob-data/pob-publishers Maven module and implement the publish(Opportunity opportunity) abstract method as demonstrated in the following code snippet:
import ai.hyperlearning.pob.data.publishers.OpportunityPublisher
public class TestPublisher extends OpportunityPublisher {
public TestPublisher(Map<String, Object> properties) {
super(properties);
}
@Override
public void publish(Opportunity opportunity) {
... custom publisher code goes here ...
}
}
To review POB's Opportunity entity model class, please open the Opportunity class found in the $POB_BASE/pob-model Maven module.
Examples
POB's native opportunity publishers may be found in the $POB_BASE/pob-data/pob-publishers Maven module. For example, POB's native Slack publisher SlackPublisher is provided below - it works by simply creating a HTTP client and executing a HTTP POST request to the configured Slack Webhook URL where the HTTP request body contains the Opportunity object as a JSON object conforming to the required Slack webhook message format.
package ai.hyperlearning.pob.data.publishers.slack;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ai.hyperlearning.pob.data.publishers.CommonPublisherProperties;
import ai.hyperlearning.pob.data.publishers.OpportunityPublisher;
import ai.hyperlearning.pob.data.publishers.exceptions.OpportunityPublishingException;
import ai.hyperlearning.pob.model.Opportunity;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* Slack Publisher
*
* @author jillurquddus
* @since 0.0.1
*/
public class SlackPublisher extends OpportunityPublisher {
private static final Logger LOGGER =
LoggerFactory.getLogger(SlackPublisher.class);
// Webhook properties
private static final String CHANNEL_PROPERTY_KEY = "channel";
private static final String WEBHOOK_PROPERTY_KEY = "webhook";
// Request client
private OkHttpClient client;
// Request properties
private static final String MEDIA_TYPE =
org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
// Message content and formatting
private static final String JSON_PLACEHOLDER_SLACK_CHANNEL =
"[SLACK_CHANNEL]";
private static final String REQUEST_BODY_JSON_TEMPLATE =
"{" +
"\"channel\": \"" + JSON_PLACEHOLDER_SLACK_CHANNEL + "\", " +
"\"text\": \"" +
CommonPublisherProperties.MESSAGE_CONTENT_TEMPLATE +
"\", " +
"\"unfurl_links\": true" +
"}";
public SlackPublisher(Map<String, Object> properties) {
super(properties);
client = new OkHttpClient();
}
@Override
public void publish(Opportunity opportunity) {
LOGGER.info("Started the Slack publisher.");
Response response = null;
try {
// Get the Slack publisher properties
String channel = (String) getProperties().get(CHANNEL_PROPERTY_KEY);
String webhook = (String) getProperties().get(WEBHOOK_PROPERTY_KEY);
// Create the Slack message as a JSON object
String json = buildMessage(REQUEST_BODY_JSON_TEMPLATE, opportunity)
.replace(JSON_PLACEHOLDER_SLACK_CHANNEL, channel);
// Create the HTTP POST request
if (client == null)
client = new OkHttpClient();
RequestBody body = RequestBody.create(
json, MediaType.parse(MEDIA_TYPE));
Request request = new Request.Builder()
.url(webhook)
.post(body)
.build();
// Submit the POST request and get the Slack webhook response
LOGGER.debug("POSTing opportunity to Slack channel: \n{}", json);
Call call = client.newCall(request);
response = call.execute();
} catch (Exception e) {
String message = "An error was encountered whilst attempting to "
+ "publish the opportunity to Slack.";
throw new OpportunityPublishingException(message, e.getMessage());
} finally {
// Close the response object to avoid connection leaks
if (response != null) {
try {
response.close();
} catch (Exception e) {
LOGGER.warn("An error was encountered whilst attempting to "
+ "close the OkHttpClient response object.", e);
}
}
}
}
}
Integration
To integrate your custom publisher into the main POB data pipeline, simply append your custom publisher to the publishers list in the POB's Application Configuration found in the application.yml file in the $POB_BASE/pob-configuration Maven module, as follows:
Once configured, the main POB data pipeline will instantiate and execute all the enabled publisher classes that have been registered in application.yml, including any newly added custom publishers. Note that all custom publisher properties defined in application.yml will be injected and made available to your custom publisher class by the main POB data pipeline via the properties hash map in the Publisher object.