diff --git a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyEventClient.java b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyEventClient.java index 3ddb19c..fedaa81 100644 --- a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyEventClient.java +++ b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyEventClient.java @@ -1,5 +1,6 @@ package systems.comodal.pagerduty.event.client; +import systems.comodal.pagerduty.event.data.PagerDutyChangeEventPayload; import systems.comodal.pagerduty.event.data.PagerDutyEventPayload; import systems.comodal.pagerduty.event.data.PagerDutyEventResponse; import systems.comodal.pagerduty.event.data.adapters.PagerDutyEventAdapter; @@ -44,6 +45,11 @@ CompletableFuture triggerEvent(final String clientName, final String routingKey, final PagerDutyEventPayload payload); + CompletableFuture changeEvent(final String clientName, + final String clientUrl, + final String routingKey, + final PagerDutyChangeEventPayload payload); + interface Builder { PagerDutyEventClient create(); diff --git a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyEventClientBuilder.java b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyEventClientBuilder.java index 0300ca4..64f297e 100644 --- a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyEventClientBuilder.java +++ b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyEventClientBuilder.java @@ -22,15 +22,16 @@ final class PagerDutyEventClientBuilder implements PagerDutyEventClient.Builder PagerDutyEventClientBuilder() { } - + @Override public PagerDutyEventClient create() { final var authHeader = "Token token=" + requireNonNull(authToken, "Auth token is required."); - final var pageDutyEventsUri = URI.create(pagerDutyUri).resolve("/v2/enqueue"); + final var eventUri = URI.create(pagerDutyUri).resolve("/v2/enqueue"); + final var changeEventsUri = URI.create(pagerDutyUri).resolve("/v2/change/enqueue"); return new PagerDutyHttpEventClient(defaultClientName, defaultClientUrl, defaultRoutingKey, authHeader, - pageDutyEventsUri, + eventUri, changeEventsUri, httpClient == null ? HttpClient.newBuilder().version(HTTP_2).build() : httpClient, responseAdapter == null ? ServiceLoader.load(PagerDutyEventAdapterFactory.class).findFirst().orElseThrow().create() diff --git a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyHttpEventClient.java b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyHttpEventClient.java index 5f0944a..ea3b954 100644 --- a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyHttpEventClient.java +++ b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/client/PagerDutyHttpEventClient.java @@ -1,9 +1,9 @@ package systems.comodal.pagerduty.event.client; +import systems.comodal.pagerduty.event.data.PagerDutyChangeEventPayload; import systems.comodal.pagerduty.event.data.PagerDutyEventPayload; import systems.comodal.pagerduty.event.data.PagerDutyEventResponse; import systems.comodal.pagerduty.event.data.PagerDutyImageRef; -import systems.comodal.pagerduty.event.data.PagerDutyLinkRef; import systems.comodal.pagerduty.event.data.adapters.PagerDutyEventAdapter; import java.net.URI; @@ -22,6 +22,7 @@ record PagerDutyHttpEventClient(String defaultClientName, String defaultRoutingKey, String authorizationHeader, URI eventUri, + URI changeEventUri, HttpClient httpClient, PagerDutyEventAdapter adapter) implements PagerDutyEventClient { @@ -40,6 +41,19 @@ public String getDefaultRoutingKey() { return defaultRoutingKey; } + private HttpRequest createRequest(final URI endpoint, final String jsonBody) { + return HttpRequest.newBuilder(endpoint).headers( + "Authorization", authorizationHeader, + "Accept", "application/json", + "Content-Type", "application/json") + .POST(ofString(jsonBody, UTF_8)).build(); + } + + private CompletableFuture createAndSendEventRequest(final URI uri, final String jsonBody) { + return httpClient.sendAsync(createRequest(uri, jsonBody), ofByteArray()) + .thenApplyAsync(adapter::adaptResponse); + } + @Override public CompletableFuture acknowledgeEvent(final String routingKey, final String dedupKey) { return eventAction(routingKey, dedupKey, "acknowledge"); @@ -58,7 +72,7 @@ private CompletableFuture eventAction(final String routi final var json = String.format(""" {"routing_key":"%s","dedup_key":"%s","event_action":"%s"}""", routingKey, dedupKey, eventAction); - return createAndSendRequest(routingKey, json); + return createAndSendEventRequest(eventUri, json); } @Override @@ -68,35 +82,31 @@ public CompletableFuture triggerEvent(final String clien final PagerDutyEventPayload payload) { Objects.requireNonNull(routingKey, "Routing key is a required field."); final var payloadJson = payload.getPayloadJson(); + final var linksJson = payload.getLinksJson(); final var imagesJson = payload.getImages().isEmpty() ? "" : payload.getImages().stream().map(PagerDutyImageRef::toJson) .collect(Collectors.joining(",", ",\"images\":[", "]")); - final var linksJson = payload.getLinks().isEmpty() - ? "" - : payload.getLinks().stream().map(PagerDutyLinkRef::toJson) - .collect(Collectors.joining(",", ",\"links\":[", "]")); final var json = String.format(""" {"event_action":"trigger","routing_key":"%s","dedup_key":"%s","payload":%s,"client":"%s"%s%s%s}""", routingKey, payload.getDedupKey(), payloadJson, clientName, (clientUrl == null ? "" : ",\"client_url\":\"" + clientUrl + '"'), imagesJson, linksJson); - return createAndSendRequest(routingKey, json); + return createAndSendEventRequest(eventUri, json); } - private HttpRequest createRequest(final String routingKey, final String jsonBody) { - return HttpRequest.newBuilder(eventUri) - .headers( - "Authorization", authorizationHeader, - "Accept", "application/vnd.pagerduty+json;version=2", - "Content-Type", "application/json", - "X-Routing-Key", routingKey) - .POST(ofString(jsonBody, UTF_8)).build(); - } - - private CompletableFuture createAndSendRequest(final String routingKey, final String jsonBody) { - return httpClient.sendAsync(createRequest(routingKey, jsonBody), ofByteArray()) - .thenApplyAsync(adapter::adaptResponse); + @Override + public CompletableFuture changeEvent(final String clientName, + final String clientUrl, + final String routingKey, + final PagerDutyChangeEventPayload payload) { + Objects.requireNonNull(routingKey, "Routing key is a required field."); + final var payloadJson = payload.getPayloadJson(); + final var linksJson = payload.getLinksJson(); + final var json = String.format(""" + {"routing_key":"%s","payload":%s%s}""", + routingKey, payloadJson, linksJson); + return createAndSendEventRequest(changeEventUri, json); } @Override @@ -104,6 +114,7 @@ public String toString() { return "PagerDutyHttpEventClient{defaultClientName='" + defaultClientName + '\'' + ", defaultClientUrl='" + defaultClientUrl + '\'' + ", defaultRoutingKey='" + defaultRoutingKey + '\'' + - ", eventUri=" + eventUri + '}'; + ", eventUri=" + eventUri + '\'' + + ", changeEventUri=" + changeEventUri + '}'; } } diff --git a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyChangeEventPayload.java b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyChangeEventPayload.java new file mode 100644 index 0000000..c7367bf --- /dev/null +++ b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyChangeEventPayload.java @@ -0,0 +1,57 @@ +package systems.comodal.pagerduty.event.data; + +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public interface PagerDutyChangeEventPayload { + + static PagerDutyChangeEventPayload.Builder build() { + return new PagerDutyChangeEventPayloadRecord.PagerDutyChangeEventPayloadBuilder(); + } + + static PagerDutyChangeEventPayload.Builder build(final PagerDutyChangeEventPayload prototype) { + return prototype == null ? build() : new PagerDutyChangeEventPayloadRecord.PagerDutyChangeEventPayloadBuilder(prototype); + } + + ZonedDateTime getTimestamp(); + + String getSummary(); + + String getSource(); + + Map getCustomDetails(); + + List getLinks(); + + default String getLinksJson() { + final var links = getLinks(); + return links.isEmpty() ? "" : links.stream().map(PagerDutyLinkRef::toJson) + .collect(Collectors.joining(",", ",\"links\":[", "]")); + } + + String getPayloadJson(); + + interface Builder extends PagerDutyChangeEventPayload { + + + PagerDutyChangeEventPayload create(); + + Builder summary(final String summary); + + Builder timestamp(final ZonedDateTime timestamp); + + Builder source(final String source); + + Builder customDetails(final String field, final String fieldValue); + + Builder customDetails(final String field, final Boolean fieldValue); + + Builder customDetails(final String field, final Number fieldValue); + + Builder customDetails(final String field, final Object fieldValue); + + Builder link(final PagerDutyLinkRef link); + } +} diff --git a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyChangeEventPayloadRecord.java b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyChangeEventPayloadRecord.java new file mode 100644 index 0000000..9be252a --- /dev/null +++ b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyChangeEventPayloadRecord.java @@ -0,0 +1,279 @@ +package systems.comodal.pagerduty.event.data; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.ZonedDateTime; +import java.util.*; +import java.util.stream.Collectors; + +import static java.time.ZoneOffset.UTC; + +record PagerDutyChangeEventPayloadRecord(String summary, + ZonedDateTime timestamp, + String source, + Map customDetails, + List links, + String json) implements PagerDutyChangeEventPayload { + + @Override + public ZonedDateTime getTimestamp() { + return timestamp; + } + + @Override + public String getSummary() { + return summary; + } + + @Override + public String getSource() { + return source; + } + + @Override + public Map getCustomDetails() { + return customDetails; + } + + @Override + public List getLinks() { + return links; + } + + @Override + public String getPayloadJson() { + return json; + } + + @Override + public String toString() { + return getPayloadJson(); + } + + static class PagerDutyChangeEventPayloadBuilder implements PagerDutyChangeEventPayload.Builder { + + private static final Map NO_CUSTOM_DETAILS = Map.of(); + private static final List NO_LINKS = List.of(); + + String summary; + String source; + ZonedDateTime timestamp; + Map customDetails; + List links; + + PagerDutyChangeEventPayloadBuilder() { + this.customDetails = NO_CUSTOM_DETAILS; + this.links = NO_LINKS; + } + + PagerDutyChangeEventPayloadBuilder(final PagerDutyChangeEventPayload prototype) { + this.summary(prototype.getSummary()); + this.source(prototype.getSource()); + this.timestamp(prototype.getTimestamp()); + final var customDetails = prototype.getCustomDetails(); + this.customDetails = customDetails == null || customDetails.isEmpty() + ? NO_CUSTOM_DETAILS + : customDetails.size() > 1 + ? new LinkedHashMap<>(customDetails) + : Map.copyOf(customDetails); + final var links = prototype.getLinks(); + this.links = links == null || links.isEmpty() + ? NO_LINKS + : links.size() > 1 + ? new ArrayList<>(links) + : List.copyOf(links); + } + + @Override + public PagerDutyChangeEventPayload create() { + Objects.requireNonNull(summary, "'Summary' is a required payload field."); + if (timestamp == null) { + timestamp = ZonedDateTime.now(UTC); + } + final var json = getPayloadJson(); + return new PagerDutyChangeEventPayloadRecord( + summary, + timestamp, + source, + customDetails.size() > 1 ? Collections.unmodifiableMap(customDetails) : customDetails, + links.size() > 1 ? Collections.unmodifiableList(links) : links, + json); + } + + @Override + public Builder summary(final String summary) { + this.summary = summary.length() > 1_024 ? summary.substring(0, 1_024) : summary; + return this; + } + + @Override + public Builder source(final String source) { + this.source = source; + return this; + } + + @Override + public Builder timestamp(final ZonedDateTime timestamp) { + this.timestamp = timestamp; + return this; + } + + final Builder customDetailsObject(final String field, final Object fieldValue) { + final var val = fieldValue == null ? "null" : fieldValue; + if (customDetails == null || customDetails.isEmpty()) { + customDetails = Map.of(field, val); + return this; + } else if (customDetails.size() == 1) { + customDetails = new LinkedHashMap<>(customDetails); + } + customDetails.put(field, val); + return this; + } + + @Override + public Builder customDetails(final String field, final String fieldValue) { + return customDetailsObject(field, fieldValue); + } + + @Override + public Builder customDetails(final String field, final Boolean fieldValue) { + return customDetailsObject(field, fieldValue); + } + + @Override + public Builder customDetails(final String field, final Number fieldValue) { + return customDetailsObject(field, fieldValue); + } + + @Override + public Builder customDetails(final String field, final Object fieldValue) { + return customDetailsObject(field, fieldValue == null ? null : fieldValue.toString()); + } + + @Override + public Builder link(final PagerDutyLinkRef link) { + if (links.isEmpty()) { + links = List.of(link); + return this; + } else if (links.size() == 1) { + links = new ArrayList<>(links); + } + links.add(link); + return this; + } + + @Override + public String getSummary() { + return summary; + } + + @Override + public final String getSource() { + return source; + } + + @Override + public final ZonedDateTime getTimestamp() { + return timestamp; + } + + @Override + public final Map getCustomDetails() { + return customDetails; + } + + @Override + public final List getLinks() { + return links; + } + + static void appendString(final StringBuilder jsonBuilder, final String field, final String str) { + if (str != null && !str.isBlank()) { + jsonBuilder.append(",\""); + jsonBuilder.append(field); + jsonBuilder.append("\":\""); + jsonBuilder.append(str); + jsonBuilder.append('"'); + } + } + + private static String escapeQuotes(final String str) { + final char[] chars = str.toCharArray(); + final char[] escaped = new char[chars.length << 1]; + char c; + for (int escapes = 0, from = 0, dest = 0, to = 0; ; to++) { + if (to == chars.length) { + if (from == 0) { + return str; + } else { + final int len = to - from; + System.arraycopy(chars, from, escaped, dest, len); + dest += len; + return new String(escaped, 0, dest); + } + } else { + c = chars[to]; + if (c == '\\') { + escapes++; + } else if (c == '"' && (escapes & 1) == 0) { + final int len = to - from; + System.arraycopy(chars, from, escaped, dest, len); + dest += len; + escaped[dest++] = '\\'; + from = to; + escapes = 0; + } else { + escapes = 0; + } + } + } + } + + static String toJson(final Map object) { + return object.entrySet().stream().map(entry -> { + final var key = entry.getKey(); + final var val = entry.getValue(); + return switch (val) { + case null -> String.format(""" + "%s":null""", key); + case BigDecimal bigDecimal -> String.format(""" + "%s":"%s\"""", key, bigDecimal.toPlainString()); + case BigInteger bigInteger -> String.format(""" + "%s":"%s\"""", key, bigInteger); + case Number number -> String.format(""" + "%s":%s""", key, number); + case Boolean bool -> String.format(""" + "%s":%s""", key, bool); + //noinspection DataFlowIssue + case Object obj -> { + final var str = obj.toString(); + yield String.format(""" + "%s":"%s\"""", + key, str.indexOf('"') < 0 ? str : escapeQuotes(str)); + } + }; + }).collect(Collectors.joining(",", "{", "}")); + } + + @Override + public String getPayloadJson() { + final var jsonBuilder = new StringBuilder(2_048); + jsonBuilder.append(String.format(""" + {"summary":"%s\"""", + summary)); + appendString(jsonBuilder, "source", source); + appendString(jsonBuilder, "timestamp", timestamp.toString()); + if (!customDetails.isEmpty()) { + jsonBuilder.append(""" + ,"custom_details":"""); + jsonBuilder.append(toJson(customDetails)); + } + return jsonBuilder.append('}').toString(); + } + + @Override + public final String toString() { + return getPayloadJson(); + } + } +} diff --git a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyEventPayload.java b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyEventPayload.java index 8533615..50adff7 100644 --- a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyEventPayload.java +++ b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyEventPayload.java @@ -2,9 +2,8 @@ import java.time.ZonedDateTime; import java.util.List; -import java.util.Map; -public interface PagerDutyEventPayload { +public interface PagerDutyEventPayload extends PagerDutyChangeEventPayload { static PagerDutyEventPayload.Builder build() { return new PagerDutyEventPayloadBuilder(); @@ -16,58 +15,46 @@ static PagerDutyEventPayload.Builder build(final PagerDutyEventPayload prototype String getDedupKey(); - String getSummary(); - - String getSource(); - PagerDutySeverity getSeverity(); - ZonedDateTime getTimestamp(); - String getComponent(); String getGroup(); String getType(); - Map getCustomDetails(); - - List getLinks(); - List getImages(); - String getPayloadJson(); - - interface Builder extends PagerDutyEventPayload { + interface Builder extends PagerDutyEventPayload, PagerDutyChangeEventPayload.Builder { PagerDutyEventPayload create(); - Builder dedupKey(final String dedupKey); + PagerDutyEventPayload.Builder dedupKey(final String dedupKey); - Builder summary(final String summary); + PagerDutyEventPayload.Builder summary(final String summary); - Builder source(final String source); + PagerDutyEventPayload.Builder source(final String source); - Builder severity(final PagerDutySeverity severity); + PagerDutyEventPayload.Builder severity(final PagerDutySeverity severity); - Builder timestamp(final ZonedDateTime timestamp); + PagerDutyEventPayload.Builder timestamp(final ZonedDateTime timestamp); - Builder component(final String component); + PagerDutyEventPayload.Builder component(final String component); - Builder group(final String group); + PagerDutyEventPayload.Builder group(final String group); - Builder type(final String type); + PagerDutyEventPayload.Builder type(final String type); - Builder customDetails(final String field, final String fieldValue); + PagerDutyEventPayload.Builder customDetails(final String field, final String fieldValue); - Builder customDetails(final String field, final Boolean fieldValue); + PagerDutyEventPayload.Builder customDetails(final String field, final Boolean fieldValue); - Builder customDetails(final String field, final Number fieldValue); + PagerDutyEventPayload.Builder customDetails(final String field, final Number fieldValue); - Builder customDetails(final String field, final Object fieldValue); + PagerDutyEventPayload.Builder customDetails(final String field, final Object fieldValue); - Builder link(final PagerDutyLinkRef link); + PagerDutyEventPayload.Builder link(final PagerDutyLinkRef link); - Builder image(final PagerDutyImageRef image); + PagerDutyEventPayload.Builder image(final PagerDutyImageRef image); } } diff --git a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyEventPayloadBuilder.java b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyEventPayloadBuilder.java index 56c5180..68fcf13 100644 --- a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyEventPayloadBuilder.java +++ b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyEventPayloadBuilder.java @@ -1,58 +1,35 @@ package systems.comodal.pagerduty.event.data; -import java.math.BigDecimal; -import java.math.BigInteger; import java.time.ZonedDateTime; import java.util.*; -import java.util.stream.Collectors; import static java.time.ZoneOffset.UTC; -final class PagerDutyEventPayloadBuilder implements PagerDutyEventPayload.Builder { +final class PagerDutyEventPayloadBuilder + extends PagerDutyChangeEventPayloadRecord.PagerDutyChangeEventPayloadBuilder + implements PagerDutyEventPayload.Builder { - private static final Map NO_CUSTOM_DETAILS = Map.of(); - private static final List NO_LINKS = List.of(); private static final List NO_IMAGES = List.of(); private String dedupKey; - private String summary; - private String source; private PagerDutySeverity severity; - private ZonedDateTime timestamp; private String component; private String group; private String type; - private Map customDetails; - private List links; private List images; PagerDutyEventPayloadBuilder() { - this.customDetails = NO_CUSTOM_DETAILS; - this.links = NO_LINKS; + super(); this.images = NO_IMAGES; } PagerDutyEventPayloadBuilder(final PagerDutyEventPayload prototype) { + super(prototype); this.dedupKey(prototype.getDedupKey()); - this.summary(prototype.getSummary()); - this.source(prototype.getSource()); this.severity(prototype.getSeverity()); - this.timestamp(prototype.getTimestamp()); this.component(prototype.getComponent()); this.group(prototype.getGroup()); this.type(prototype.getType()); - final var customDetails = prototype.getCustomDetails(); - this.customDetails = customDetails == null || customDetails.isEmpty() - ? NO_CUSTOM_DETAILS - : customDetails.size() > 1 - ? new LinkedHashMap<>(customDetails) - : Map.copyOf(customDetails); - final var links = prototype.getLinks(); - this.links = links == null || links.isEmpty() - ? NO_LINKS - : links.size() > 1 - ? new ArrayList<>(links) - : List.copyOf(links); final var images = prototype.getImages(); this.images = images == null || images.isEmpty() ? NO_IMAGES @@ -87,7 +64,7 @@ public PagerDutyEventPayload create() { } @Override - public Builder dedupKey(final String dedupKey) { + public PagerDutyEventPayload.Builder dedupKey(final String dedupKey) { if (dedupKey != null && dedupKey.length() > 255) { throw new IllegalArgumentException("Max length for 'dedup_key' is 255"); } @@ -96,93 +73,79 @@ public Builder dedupKey(final String dedupKey) { } @Override - public Builder summary(final String summary) { - this.summary = summary.length() > 1_024 ? summary.substring(0, 1_024) : summary; + public PagerDutyEventPayload.Builder summary(final String summary) { + super.summary(summary); return this; } @Override - public Builder source(final String source) { - this.source = source; + public PagerDutyEventPayload.Builder source(final String source) { + super.source(source); return this; } @Override - public Builder severity(final PagerDutySeverity severity) { + public PagerDutyEventPayload.Builder severity(final PagerDutySeverity severity) { this.severity = severity; return this; } @Override - public Builder timestamp(final ZonedDateTime timestamp) { - this.timestamp = timestamp; + public PagerDutyEventPayload.Builder timestamp(final ZonedDateTime timestamp) { + super.timestamp(timestamp); return this; } @Override - public Builder component(final String component) { + public PagerDutyEventPayload.Builder component(final String component) { this.component = component; return this; } @Override - public Builder group(final String group) { + public PagerDutyEventPayload.Builder group(final String group) { this.group = group; return this; } @Override - public Builder type(final String type) { + public PagerDutyEventPayload.Builder type(final String type) { this.type = type; return this; } - private Builder customDetailsObject(final String field, final Object fieldValue) { - final var val = fieldValue == null ? "null" : fieldValue; - if (customDetails == null || customDetails.isEmpty()) { - customDetails = Map.of(field, val); - return this; - } else if (customDetails.size() == 1) { - customDetails = new LinkedHashMap<>(customDetails); - } - customDetails.put(field, val); - return this; - } - @Override - public Builder customDetails(final String field, final String fieldValue) { - return customDetailsObject(field, fieldValue); + public PagerDutyEventPayload.Builder customDetails(final String field, final String fieldValue) { + super.customDetailsObject(field, fieldValue); + return this; } @Override - public Builder customDetails(final String field, final Boolean fieldValue) { - return customDetailsObject(field, fieldValue); + public PagerDutyEventPayload.Builder customDetails(final String field, final Boolean fieldValue) { + super.customDetailsObject(field, fieldValue); + return this; } @Override - public Builder customDetails(final String field, final Number fieldValue) { - return customDetailsObject(field, fieldValue); + public PagerDutyEventPayload.Builder customDetails(final String field, final Number fieldValue) { + super.customDetailsObject(field, fieldValue); + return this; } @Override - public Builder customDetails(final String field, final Object fieldValue) { - return customDetailsObject(field, fieldValue == null ? null : fieldValue.toString()); + public PagerDutyEventPayload.Builder customDetails(final String field, final Object fieldValue) { + super.customDetailsObject(field, fieldValue); + return this; } @Override - public Builder link(final PagerDutyLinkRef link) { - if (links.isEmpty()) { - links = List.of(link); - return this; - } else if (links.size() == 1) { - links = new ArrayList<>(links); - } - links.add(link); + public PagerDutyEventPayload.Builder link(final PagerDutyLinkRef link) { + super.link(link); return this; } @Override - public Builder image(final PagerDutyImageRef image) { + public PagerDutyEventPayload.Builder image(final PagerDutyImageRef image) { if (images.isEmpty()) { images = List.of(image); return this; @@ -198,26 +161,11 @@ public String getDedupKey() { return dedupKey; } - @Override - public String getSummary() { - return summary; - } - - @Override - public String getSource() { - return source; - } - @Override public PagerDutySeverity getSeverity() { return severity; } - @Override - public ZonedDateTime getTimestamp() { - return timestamp; - } - @Override public String getComponent() { return component; @@ -233,88 +181,11 @@ public String getType() { return type; } - @Override - public Map getCustomDetails() { - return customDetails; - } - - @Override - public List getLinks() { - return links; - } - @Override public List getImages() { return images; } - private static void appendString(final StringBuilder jsonBuilder, final String field, final String str) { - if (str != null && !str.isBlank()) { - jsonBuilder.append(",\""); - jsonBuilder.append(field); - jsonBuilder.append("\":\""); - jsonBuilder.append(str); - jsonBuilder.append('"'); - } - } - - private static String escapeQuotes(final String str) { - final char[] chars = str.toCharArray(); - final char[] escaped = new char[chars.length << 1]; - char c; - for (int escapes = 0, from = 0, dest = 0, to = 0; ; to++) { - if (to == chars.length) { - if (from == 0) { - return str; - } else { - final int len = to - from; - System.arraycopy(chars, from, escaped, dest, len); - dest += len; - return new String(escaped, 0, dest); - } - } else { - c = chars[to]; - if (c == '\\') { - escapes++; - } else if (c == '"' && (escapes & 1) == 0) { - final int len = to - from; - System.arraycopy(chars, from, escaped, dest, len); - dest += len; - escaped[dest++] = '\\'; - from = to; - escapes = 0; - } else { - escapes = 0; - } - } - } - } - - private static String toJson(final Map object) { - return object.entrySet().stream().map(entry -> { - final var key = entry.getKey(); - final var val = entry.getValue(); - return switch (val) { - case null -> String.format(""" - "%s":null""", key); - case BigDecimal bigDecimal -> String.format(""" - "%s":"%s\"""", key, bigDecimal.toPlainString()); - case BigInteger bigInteger -> String.format(""" - "%s":"%s\"""", key, bigInteger); - case Number number -> String.format(""" - "%s":%s""", key, number); - case Boolean bool -> String.format(""" - "%s":%s""", key, bool); - //noinspection DataFlowIssue - case Object obj -> { - final var str = obj.toString(); - yield String.format(""" - "%s":"%s\"""", - key, str.indexOf('"') < 0 ? str : escapeQuotes(str)); - } - }; - }).collect(Collectors.joining(",", "{", "}")); - } @Override public String getPayloadJson() { @@ -332,9 +203,4 @@ public String getPayloadJson() { } return jsonBuilder.append('}').toString(); } - - @Override - public String toString() { - return getPayloadJson(); - } } diff --git a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyImageRef.java b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyImageRef.java index ed6ecbd..e7e17c0 100644 --- a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyImageRef.java +++ b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyImageRef.java @@ -13,8 +13,23 @@ static PagerDutyImageRef.Builder build() { String getAlt(); default String toJson() { - return String.format(""" - {"src":"%s","href":"%s","alt":"%s"}""", getSrc(), getHref(), getAlt()); + final var href = getHref(); + final var alt = getAlt(); + if (href == null || href.isBlank()) { + if (alt == null || alt.isBlank()) { + return String.format(""" + {"src":"%s"}""", getSrc()); + } else { + return String.format(""" + {"src":"%s","alt":"%s"}""", getSrc(), alt); + } + } else if (alt == null || alt.isBlank()) { + return String.format(""" + {"src":"%s","href":"%s"}""", getSrc(), href); + } else { + return String.format(""" + {"src":"%s","href":"%s","alt":"%s"}""", getSrc(), href, alt); + } } interface Builder extends PagerDutyImageRef { diff --git a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyLinkRef.java b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyLinkRef.java index 90b0c55..9755bb4 100644 --- a/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyLinkRef.java +++ b/systems.comodal.pagerduty_event_client/src/main/java/systems/comodal/pagerduty/event/data/PagerDutyLinkRef.java @@ -11,8 +11,14 @@ static PagerDutyLinkRef.Builder build() { String getText(); default String toJson() { - return String.format(""" - {"href":"%s","text":"%s"}""", getHref(), getText()); + final var text = getText(); + if (text == null || text.isBlank()) { + return String.format(""" + {"href":"%s"}""", getHref()); + } else { + return String.format(""" + {"href":"%s","text":"%s"}""", getHref(), text); + } } interface Builder extends PagerDutyLinkRef { diff --git a/systems.comodal.pagerduty_event_client_test/src/main/java/systems/comodal/test/pagerduty/EventClientTests.java b/systems.comodal.pagerduty_event_client_test/src/main/java/systems/comodal/test/pagerduty/EventClientTests.java index b28ebf6..bf5a27e 100644 --- a/systems.comodal.pagerduty_event_client_test/src/main/java/systems/comodal/test/pagerduty/EventClientTests.java +++ b/systems.comodal.pagerduty_event_client_test/src/main/java/systems/comodal/test/pagerduty/EventClientTests.java @@ -32,10 +32,10 @@ public void createContext(final HttpServer httpServer, assertNull(httpExchange.getRequestURI().getQuery()); final var headers = httpExchange.getRequestHeaders(); - assertEquals("application/json", headers.getFirst("Content-Type")); assertEquals("Token token=" + authToken, headers.getFirst("Authorization")); - assertEquals("application/vnd.pagerduty+json;version=2", headers.getFirst("Accept")); - assertEquals(routingKey, headers.getFirst("X-Routing-Key")); + assertEquals("application/json", headers.getFirst("Content-Type")); + assertEquals("application/json", headers.getFirst("Accept")); + assertNull(headers.getFirst("X-Routing-Key")); final var body = new String(httpExchange.getRequestBody().readAllBytes());