com.ibm.cp4waiops.connectors.sdk.TicketAction Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of connectors-sdk Show documentation
Show all versions of connectors-sdk Show documentation
A developer SDK for creating connectors for CP4WAIOps.
package com.ibm.cp4waiops.connectors.sdk;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.OffsetDateTime;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Map.Entry;
import org.json.JSONObject;
import com.ibm.cp4waiops.connectors.sdk.models.Ticket;
import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
public class TicketAction {
protected ConnectorBase connector = null;
protected String sourceName = null;
protected Map mapping;
protected String partition = null;
protected String instance = null;
protected String connMode = null;
public static final String TOPIC_OUTPUT_ACTION_SNOW = "cp4waiops-cartridge.connector-snow-actions";
public static final String TOPIC_OUTPUT_INCIDENT = "cp4waiops-cartridge.incident";
public static final String TOPIC_OUTPUT_CHANGE_REQUEST = "cp4waiops-cartridge.changerequest";
public static final String TOPIC_INPUT_REQUESTS = "cp4waiops-cartridge.lifecycle.output.connector-requests";
public static final String TOPIC_OUTPUT_RESPONSES = "cp4waiops-cartridge.lifecycle.input.connector-responses";
public static final String TOPIC_LIFECYCLE_INPUT_EVENTS = "cp4waiops-cartridge.lifecycle.input.events";
static final String CE_CONNECTION_MODE = "connectionmode";
static final String CONNECTION_ID = "connection_id";
static final String INSTANCE = "instance";
final static String CHANGE_REQUEST_DISC = "com.ibm.sdlc.snow.changerequest.discovered";
final static String INCIDENT_DISC = "com.ibm.sdlc.snow.incident.discovered";
final static String SOURCE_NAME = "source_name";
static final String RESPONSE_TIME_CE_EXT = "responsetime";
static final URI SELF_SOURCE = URI.create("template.connectors.aiops.watson.ibm.com/connectorsnow");
static final String COMPONENT_NAME_CE_EXTENSION_NAME = "componentname";
static final String CONNECTION_ID_CE_EXTENSION_NAME = "connectionid";
static final String SYSTEM_NAME_CE_EXTENSION_NAME = "systemname";
static final String TOOL_TYPE = "tooltype";
public final static String TOOL_TYPE_SNOW = "com.ibm.sdlc.type.snow";
static final String CE_EXT_STRUCTURED_CONTENT_MODE = "structuredcontentmode";
static final String CE_EXT_STRUCTURED_CONTENT_MODE_VALUE = "true";
static final String CONNECTION_MODE_HISTORICAL = "historical";
static final String CONNECTION_MODE_LIVE = "live";
ElasticHelper elasticHelper = null;
static final Logger logger = Logger.getLogger(TicketAction.class.getName());
/**
* Perform actions with a ticket type
*
* @param connector
* the connector used for querying ticket information. The connector contains key information required
* for AI training and inference
* @param mapping
* if mapping is provided, call the applyMapping function in this class to allow custom fields to be used
* in place of expected ones
* @param sourceName
* the name of the source where the data comes from
* @param instance
* the instance name of the connector
* @param connMode
* the connection mode, can be live or historic
*/
public TicketAction(ConnectorBase connector, Map mapping, String sourceName, String instance,
String connMode) {
this.connector = connector;
this.mapping = mapping;
this.sourceName = sourceName;
this.partition = getPartition();
this.instance = instance;
this.connMode = connMode;
try {
elasticHelper = new ElasticHelper();
} catch (PropertyReadException e) {
logger.log(Level.WARNING,
"Failed to setup elastic helper due to missing elastic variables: " + e.getMessage());
}
}
/**
* Create Kafka message containing incident data, to be used by AI training or inference.
*
* @param ticket
* data to be put onto Kafka
* @param source
* the source of this data, typically an identifiable URI
*/
public void emitIncident(Ticket ticket, String source) {
try {
connector.emitCloudEvent(TOPIC_OUTPUT_INCIDENT, partition, getIncidentCE(ticket, source));
} catch (Exception e) {
logger.log(Level.WARNING, "Failed to emit incident to Kafka due to error: " + e.getMessage(), e);
}
}
/**
* Create Kafka message containing change request data, to be used by AI training or inference.
*
* @param ticket
* data to be put onto Kafka
* @param source
* the source of this data, typically an identifiable URI
*/
public void emitChangeRequest(Ticket ticket, String source) {
try {
connector.emitCloudEvent(TOPIC_OUTPUT_CHANGE_REQUEST, partition, getChangeRequestCE(ticket, source));
} catch (Exception e) {
logger.log(Level.WARNING, "Failed to emit change request to Kafka due to error: " + e.getMessage(), e);
}
}
public CloudEvent getIncidentCE(Ticket ticket, String source) throws JsonProcessingException {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(ticket);
return getIncidentCE(new JSONObject(json), source);
}
public CloudEvent getChangeRequestCE(Ticket ticket, String source) throws JsonProcessingException {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(ticket);
return getChangeRequestCE(new JSONObject(json), source);
}
/**
* Takes an incident JSON and adds the expected Cloud Event properties
*
* @param incident
* incident data in JSON format
* @param source
* the source of the data for identifying where the event came from
*
* @return
*/
protected CloudEvent getIncidentCE(JSONObject json, String source) {
return getCE(INCIDENT_DISC, json, source);
}
/**
* Takes an change request JSON and adds the expected Cloud Event properties
*
* @param incident
* incident data in JSON format
* @param source
* the source of the data for identifying where the event came from
*
* @return
*/
protected CloudEvent getChangeRequestCE(JSONObject json, String source) {
return getCE(CHANGE_REQUEST_DISC, json, source);
}
/**
* Get the Cloud Event
*
* @param cloudEventType
* the type for differentiating between change request and incidents
* @param json
* @param source
*
* @return
*/
protected CloudEvent getCE(String cloudEventType, JSONObject json, String source) {
json.put(SOURCE_NAME, sourceName);
json.put(CE_CONNECTION_MODE, connMode);
json.put(CONNECTION_ID, connector.getConnectorID());
json.put(INSTANCE, instance);
return createEvent(0, cloudEventType, json.toString(), getSourceURI(source));
}
protected static String getValue(JSONObject snowResource, String key) {
Object value = snowResource.get(key);
if (value == null)
value = "";
return value.toString();
}
/**
* Apply mapping is called on the JSON resource before being mapped to a Ticket
*
* @param snowResource
* the original json
* @param json
* the json to be returned
* @param mappings
* the mapping
*/
public void applyMappings(JSONObject snowResource, JSONObject json, Map mappings) {
if (snowResource == null || json == null || mappings == null) {
return;
}
for (Entry mapping : mappings.entrySet()) {
String key = mapping.getKey();
if (!json.has(key))
continue;
String value = getValue(snowResource, mapping.getValue());
if (!value.isBlank())
json.put(key, value);
}
}
// Gets the URI for the source or returns null if it is an invalid URI
protected static URI getSourceURI(String sourceURI) {
try {
if (sourceURI != null) {
URI uri = new URI(sourceURI);
return uri;
}
} catch (URISyntaxException e) {
// Do nothing
}
return null;
}
protected CloudEvent createEvent(long responseTime, String ce_type, String jsonMessage, URI source) {
// Default source in case none is set
if (source == null) {
source = SELF_SOURCE;
}
// The cloud event being returned needs to be in a structured format
return CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withSource(source)
.withTime(OffsetDateTime.now()).withType(ce_type).withExtension(RESPONSE_TIME_CE_EXT, responseTime)
.withExtension(CONNECTION_ID_CE_EXTENSION_NAME, connector.getConnectorID())
.withExtension(COMPONENT_NAME_CE_EXTENSION_NAME, connector.getComponentName())
.withExtension(TOOL_TYPE, TOOL_TYPE_SNOW)
.withExtension(CE_EXT_STRUCTURED_CONTENT_MODE, CE_EXT_STRUCTURED_CONTENT_MODE_VALUE)
.withData("application/json", jsonMessage.getBytes()).build();
}
protected String getPartition() {
// Generate the partition
// ConnectorConfiguration currentConfig = getConnectorConfiguration();
if (connector != null) {
String connectionID = connector.getConnectorID();
if (connectionID != null && !connectionID.isEmpty()) {
return "{\"ce-partitionkey\":\"" + connectionID + "\"}";
}
}
// If a partition cannot be created, return null
// Null is a valid partition and will not throw errors, but
// can run into unintended consequences from consumerss
return null;
}
/**
* Insert incident data directly into Elastic for AI training
*
* @param ticket
* data to be inserted into Elastic
*
* @throws IOException
* Error inserting into Elastic
*/
public void insertIncidentIntoElastic(Ticket ticket) throws IOException {
if (ticket != null) {
elasticHelper.insertIntoElastic(ticket.getHashMap(), Constant.ELASTIC_INCIDENT);
} else {
logger.log(Level.WARNING, "Ticket is null, data will not be inserted into elastic");
}
}
/**
* Insert change request data directly into Elastic for AI training
*
* @param ticket
* data to be inserted into Elastic
*
* @throws IOException
* Error inserting into Elastic
*/
public void insertChangeRequestIntoElastic(Ticket ticket) throws IOException {
if (ticket != null) {
elasticHelper.insertIntoElastic(ticket.getHashMap(), Constant.ELASTIC_CHANGE_REQUEST);
} else {
logger.log(Level.WARNING, "Ticket is null, data will not be inserted into elastic");
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy