|
24 | 24 | import java.io.IOException;
|
25 | 25 | import java.util.ArrayList;
|
26 | 26 | import java.util.Arrays;
|
| 27 | +import java.util.Collections; |
27 | 28 | import java.util.List;
|
28 | 29 | import java.util.Map;
|
29 | 30 | import java.util.Objects;
|
| 31 | +import java.util.stream.Collectors; |
30 | 32 |
|
31 | 33 | /**
|
32 | 34 | * Official documentation for this can be found at
|
33 | 35 | *
|
34 |
| - * https://developer.pagerduty.com/documentation/howto/manually-trigger-an-incident/ |
35 |
| - * https://developer.pagerduty.com/documentation/integration/events/trigger |
36 |
| - * https://developer.pagerduty.com/documentation/integration/events/acknowledge |
37 |
| - * https://developer.pagerduty.com/documentation/integration/events/resolve |
| 36 | + * https://v2.developer.pagerduty.com/docs/send-an-event-events-api-v2 |
38 | 37 | */
|
39 | 38 | public class IncidentEvent implements ToXContentObject {
|
40 | 39 |
|
41 | 40 | static final String HOST = "events.pagerduty.com";
|
42 |
| - static final String PATH = "/generic/2010-04-15/create_event.json"; |
| 41 | + static final String PATH = "/v2/enqueue"; |
| 42 | + static final String ACCEPT_HEADER = "application/vnd.pagerduty+json;version=2"; |
43 | 43 |
|
44 | 44 | final String description;
|
45 | 45 | @Nullable final HttpProxy proxy;
|
@@ -93,46 +93,81 @@ public int hashCode() {
|
93 | 93 | return result;
|
94 | 94 | }
|
95 | 95 |
|
96 |
| - public HttpRequest createRequest(final String serviceKey, final Payload payload) throws IOException { |
| 96 | + HttpRequest createRequest(final String serviceKey, final Payload payload, final String watchId) throws IOException { |
97 | 97 | return HttpRequest.builder(HOST, -1)
|
98 | 98 | .method(HttpMethod.POST)
|
99 | 99 | .scheme(Scheme.HTTPS)
|
100 | 100 | .path(PATH)
|
101 | 101 | .proxy(proxy)
|
102 |
| - .jsonBody(new ToXContent() { |
103 |
| - @Override |
104 |
| - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { |
105 |
| - builder.field(Fields.SERVICE_KEY.getPreferredName(), serviceKey); |
106 |
| - builder.field(Fields.EVENT_TYPE.getPreferredName(), eventType); |
107 |
| - builder.field(Fields.DESCRIPTION.getPreferredName(), description); |
108 |
| - if (incidentKey != null) { |
109 |
| - builder.field(Fields.INCIDENT_KEY.getPreferredName(), incidentKey); |
110 |
| - } |
111 |
| - if (client != null) { |
112 |
| - builder.field(Fields.CLIENT.getPreferredName(), client); |
113 |
| - } |
114 |
| - if (clientUrl != null) { |
115 |
| - builder.field(Fields.CLIENT_URL.getPreferredName(), clientUrl); |
116 |
| - } |
117 |
| - if (attachPayload) { |
118 |
| - builder.startObject(Fields.DETAILS.getPreferredName()); |
119 |
| - builder.field(Fields.PAYLOAD.getPreferredName()); |
120 |
| - payload.toXContent(builder, params); |
121 |
| - builder.endObject(); |
122 |
| - } |
123 |
| - if (contexts != null && contexts.length > 0) { |
124 |
| - builder.startArray(Fields.CONTEXTS.getPreferredName()); |
125 |
| - for (IncidentEventContext context : contexts) { |
126 |
| - context.toXContent(builder, params); |
127 |
| - } |
128 |
| - builder.endArray(); |
129 |
| - } |
130 |
| - return builder; |
131 |
| - } |
132 |
| - }) |
| 102 | + .setHeader("Accept", ACCEPT_HEADER) |
| 103 | + .jsonBody((b, p) -> buildAPIXContent(b, p, serviceKey, payload, watchId)) |
133 | 104 | .build();
|
134 | 105 | }
|
135 | 106 |
|
| 107 | + XContentBuilder buildAPIXContent(XContentBuilder builder, Params params, String serviceKey, |
| 108 | + Payload payload, String watchId) throws IOException { |
| 109 | + builder.field(Fields.ROUTING_KEY.getPreferredName(), serviceKey); |
| 110 | + builder.field(Fields.EVENT_ACTION.getPreferredName(), eventType); |
| 111 | + if (incidentKey != null) { |
| 112 | + builder.field(Fields.DEDUP_KEY.getPreferredName(), incidentKey); |
| 113 | + } |
| 114 | + |
| 115 | + builder.startObject(Fields.PAYLOAD.getPreferredName()); |
| 116 | + { |
| 117 | + builder.field(Fields.SUMMARY.getPreferredName(), description); |
| 118 | + |
| 119 | + if (attachPayload && payload != null) { |
| 120 | + builder.startObject(Fields.CUSTOM_DETAILS.getPreferredName()); |
| 121 | + { |
| 122 | + builder.field(Fields.PAYLOAD.getPreferredName(), payload, params); |
| 123 | + } |
| 124 | + builder.endObject(); |
| 125 | + } |
| 126 | + |
| 127 | + if (watchId != null) { |
| 128 | + builder.field(Fields.SOURCE.getPreferredName(), watchId); |
| 129 | + } else { |
| 130 | + builder.field(Fields.SOURCE.getPreferredName(), "watcher"); |
| 131 | + } |
| 132 | + // TODO externalize this into something user editable |
| 133 | + builder.field(Fields.SEVERITY.getPreferredName(), "critical"); |
| 134 | + } |
| 135 | + builder.endObject(); |
| 136 | + |
| 137 | + if (client != null) { |
| 138 | + builder.field(Fields.CLIENT.getPreferredName(), client); |
| 139 | + } |
| 140 | + if (clientUrl != null) { |
| 141 | + builder.field(Fields.CLIENT_URL.getPreferredName(), clientUrl); |
| 142 | + } |
| 143 | + |
| 144 | + if (contexts != null && contexts.length > 0) { |
| 145 | + toXContentV2Contexts(builder, params, contexts); |
| 146 | + } |
| 147 | + |
| 148 | + return builder; |
| 149 | + } |
| 150 | + |
| 151 | + /** |
| 152 | + * Turns the V1 API contexts into 2 distinct lists, images and links. The V2 API has separated these out into 2 top level fields. |
| 153 | + */ |
| 154 | + private void toXContentV2Contexts(XContentBuilder builder, ToXContent.Params params, |
| 155 | + IncidentEventContext[] contexts) throws IOException { |
| 156 | + // contexts can be either links or images, and the v2 api needs them separate |
| 157 | + Map<IncidentEventContext.Type, List<IncidentEventContext>> groups = Arrays.stream(contexts) |
| 158 | + .collect(Collectors.groupingBy(iec -> iec.type)); |
| 159 | + |
| 160 | + List<IncidentEventContext> links = groups.getOrDefault(IncidentEventContext.Type.LINK, Collections.emptyList()); |
| 161 | + if (links.isEmpty() == false) { |
| 162 | + builder.array(Fields.LINKS.getPreferredName(), links.toArray()); |
| 163 | + } |
| 164 | + |
| 165 | + List<IncidentEventContext> images = groups.getOrDefault(IncidentEventContext.Type.IMAGE, Collections.emptyList()); |
| 166 | + if (images.isEmpty() == false) { |
| 167 | + builder.array(Fields.IMAGES.getPreferredName(), images.toArray()); |
| 168 | + } |
| 169 | + } |
| 170 | + |
136 | 171 | @Override
|
137 | 172 | public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
|
138 | 173 | builder.startObject();
|
@@ -445,8 +480,15 @@ interface Fields {
|
445 | 480 | // we need to keep this for BWC
|
446 | 481 | ParseField CONTEXT_DEPRECATED = new ParseField("context");
|
447 | 482 |
|
448 |
| - ParseField SERVICE_KEY = new ParseField("service_key"); |
449 | 483 | ParseField PAYLOAD = new ParseField("payload");
|
450 |
| - ParseField DETAILS = new ParseField("details"); |
| 484 | + ParseField ROUTING_KEY = new ParseField("routing_key"); |
| 485 | + ParseField EVENT_ACTION = new ParseField("event_action"); |
| 486 | + ParseField DEDUP_KEY = new ParseField("dedup_key"); |
| 487 | + ParseField SUMMARY = new ParseField("summary"); |
| 488 | + ParseField SOURCE = new ParseField("source"); |
| 489 | + ParseField SEVERITY = new ParseField("severity"); |
| 490 | + ParseField LINKS = new ParseField("links"); |
| 491 | + ParseField IMAGES = new ParseField("images"); |
| 492 | + ParseField CUSTOM_DETAILS = new ParseField("custom_details"); |
451 | 493 | }
|
452 | 494 | }
|
0 commit comments