Skip to content

Commit 5cc6a0c

Browse files
committed
Domain sync
1 parent f764371 commit 5cc6a0c

15 files changed

+845
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (c) 2024, FusionAuth, All Rights Reserved
3+
*/
4+
package io.fusionauth.client.json;
5+
6+
import java.io.IOException;
7+
import java.util.Arrays;
8+
import java.util.stream.Collectors;
9+
10+
import com.fasterxml.jackson.core.JsonParser;
11+
import com.fasterxml.jackson.databind.DeserializationContext;
12+
import com.fasterxml.jackson.databind.JsonNode;
13+
import com.fasterxml.jackson.databind.ObjectMapper;
14+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
15+
import io.fusionauth.domain.WebhookEventLog;
16+
import io.fusionauth.domain.event.BaseEvent;
17+
import io.fusionauth.domain.event.EventType;
18+
19+
/**
20+
* Custom JSON de-serializer for BaseEvent.
21+
*
22+
* @author Spencer Witt
23+
*/
24+
public class WebhookEventDeserializer extends StdDeserializer<BaseEvent> {
25+
public WebhookEventDeserializer() {
26+
super(WebhookEventLog.class);
27+
}
28+
29+
@Override
30+
public BaseEvent deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
31+
JsonNode node = p.getCodec().readTree(p);
32+
33+
BaseEvent newEvent = extractEventType(ctxt, p, node);
34+
return ((ObjectMapper) p.getCodec()).readerForUpdating(newEvent).readValue(node);
35+
}
36+
37+
private BaseEvent extractEventType(DeserializationContext ctxt, JsonParser p, JsonNode eventNode)
38+
throws IOException {
39+
JsonNode node = eventNode.at("/type");
40+
String type = node.asText();
41+
42+
EventType eventType = EventType.forValue(type);
43+
if (eventType == null) {
44+
// Handle an unexpected EventType and provide a useful error
45+
String sorted = Arrays.stream(EventType.values()).map(Enum::name).sorted().collect(Collectors.joining(", "));
46+
return (BaseEvent) ctxt.handleUnexpectedToken(BaseEvent.class, node.asToken(), p,
47+
"Expected the type field to be one of [" + sorted + "], but found [" + node.asText() + "]");
48+
}
49+
50+
// Assuming all of our events following this naming schema '{EventType}Event'
51+
String className = BaseEvent.class.getPackage().getName() + "." + eventType.name() + "Event";
52+
try {
53+
return (BaseEvent) Class.forName(className).getConstructor().newInstance();
54+
} catch (Exception e) {
55+
throw new IllegalStateException("Unexpected type [" + eventType + "]. This is a FusionAuth bug, could not instantiate class [" + className + "].");
56+
}
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2024, FusionAuth, All Rights Reserved
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the License.
15+
*/
16+
package io.fusionauth.domain;
17+
18+
import java.time.Duration;
19+
import java.time.ZonedDateTime;
20+
import java.util.LinkedHashMap;
21+
import java.util.Map;
22+
import java.util.Objects;
23+
import java.util.UUID;
24+
25+
import com.fasterxml.jackson.annotation.JsonIgnore;
26+
import com.inversoft.json.ToString;
27+
28+
/**
29+
* A webhook call attempt log.
30+
*
31+
* @author Spencer Witt
32+
*/
33+
public class WebhookAttemptLog implements Buildable<WebhookAttemptLog>, Comparable<WebhookAttemptLog> {
34+
public Map<String, Object> data = new LinkedHashMap<>();
35+
36+
public ZonedDateTime endInstant;
37+
38+
public UUID id;
39+
40+
public ZonedDateTime startInstant;
41+
42+
public WebhookCallResponse webhookCallResponse;
43+
44+
public UUID webhookEventLogId;
45+
46+
/**
47+
* The webhook Id for this attempt or {@code null} if it was sent to a Kafka topic.
48+
*/
49+
public UUID webhookId;
50+
51+
@Override
52+
public int compareTo(WebhookAttemptLog o) {
53+
// Sort by startInstant and then Id
54+
return startInstant.compareTo(o.startInstant) != 0 ? startInstant.compareTo(o.startInstant) : id.compareTo(o.id);
55+
}
56+
57+
@Override
58+
public boolean equals(Object o) {
59+
if (this == o) {
60+
return true;
61+
}
62+
if (o == null || getClass() != o.getClass()) {
63+
return false;
64+
}
65+
WebhookAttemptLog that = (WebhookAttemptLog) o;
66+
return Objects.equals(data, that.data) &&
67+
Objects.equals(endInstant, that.endInstant) &&
68+
Objects.equals(id, that.id) &&
69+
Objects.equals(startInstant, that.startInstant) &&
70+
Objects.equals(webhookCallResponse, that.webhookCallResponse) &&
71+
Objects.equals(webhookEventLogId, that.webhookEventLogId) &&
72+
Objects.equals(webhookId, that.webhookId);
73+
}
74+
75+
public WebhookAttemptResult getAttemptResult() {
76+
if (webhookCallResponse != null) {
77+
return webhookCallResponse.statusCode >= 200 && webhookCallResponse.statusCode <= 299 ? WebhookAttemptResult.Success : WebhookAttemptResult.Failure;
78+
}
79+
return WebhookAttemptResult.Unknown;
80+
}
81+
82+
@JsonIgnore
83+
public long getDuration() {
84+
return Duration.between(startInstant, endInstant).toMillis();
85+
}
86+
87+
@JsonIgnore
88+
// using Url instead of URL so that we can access this as a property named .url
89+
public String getUrl() {
90+
return webhookCallResponse != null && webhookCallResponse.url != null ? webhookCallResponse.url.toString() : null;
91+
}
92+
93+
@Override
94+
public int hashCode() {
95+
return Objects.hash(data, endInstant, id, startInstant, webhookCallResponse, webhookEventLogId, webhookId);
96+
}
97+
98+
@Override
99+
public String toString() {
100+
return ToString.toString(this);
101+
}
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2024, FusionAuth, All Rights Reserved
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the License.
15+
*/
16+
package io.fusionauth.domain;
17+
18+
/**
19+
* The possible states of an individual webhook attempt to a single endpoint.
20+
*
21+
* @author Spencer Witt
22+
*/
23+
public enum WebhookAttemptResult {
24+
Success,
25+
Failure,
26+
Unknown
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2024, FusionAuth, All Rights Reserved
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the License.
15+
*/
16+
package io.fusionauth.domain;
17+
18+
import java.net.URI;
19+
import java.util.Objects;
20+
21+
import com.inversoft.json.ToString;
22+
23+
/**
24+
* A webhook call response.
25+
*
26+
* @author Spencer Witt
27+
*/
28+
public class WebhookCallResponse implements Buildable<WebhookCallResponse> {
29+
public String exception;
30+
31+
public int statusCode;
32+
33+
/**
34+
* The URI for the webhook endpoint or {@code null} if the event was sent to a Kafka topic.
35+
*/
36+
public URI url;
37+
38+
@Override
39+
public boolean equals(Object o) {
40+
if (this == o) {
41+
return true;
42+
}
43+
if (o == null || getClass() != o.getClass()) {
44+
return false;
45+
}
46+
WebhookCallResponse that = (WebhookCallResponse) o;
47+
return statusCode == that.statusCode &&
48+
Objects.equals(exception, that.exception) &&
49+
Objects.equals(url, that.url);
50+
}
51+
52+
@Override
53+
public int hashCode() {
54+
return Objects.hash(exception, statusCode, url);
55+
}
56+
57+
public String toString() {
58+
return ToString.toString(this);
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (c) 2024, FusionAuth, All Rights Reserved
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the License.
15+
*/
16+
package io.fusionauth.domain;
17+
18+
import java.time.ZonedDateTime;
19+
import java.util.ArrayList;
20+
import java.util.LinkedHashMap;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.Objects;
24+
import java.util.UUID;
25+
26+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
27+
import com.inversoft.json.ToString;
28+
import io.fusionauth.domain.event.EventRequest;
29+
import io.fusionauth.domain.event.EventType;
30+
31+
/**
32+
* An instance of a webhook event log.
33+
*
34+
* @author Spencer Witt
35+
*/
36+
37+
@JsonIgnoreProperties(value = {"successfulAttempts", "failedAttempts"}, allowGetters = true)
38+
public class WebhookEventLog implements Buildable<WebhookEventLog> {
39+
// Do not include the webhook event log Id for individual attempts when returning as part of the full event
40+
@JsonIgnoreProperties("webhookEventLogId")
41+
public List<WebhookAttemptLog> attempts = new ArrayList<>();
42+
43+
public Map<String, Object> data = new LinkedHashMap<>();
44+
45+
public EventRequest event;
46+
47+
public WebhookEventResult eventResult = WebhookEventResult.Running;
48+
49+
public EventType eventType;
50+
51+
public UUID id;
52+
53+
public ZonedDateTime insertInstant;
54+
55+
public ZonedDateTime lastAttemptInstant;
56+
57+
public ZonedDateTime lastUpdateInstant;
58+
59+
public UUID linkedObjectId;
60+
61+
public Long sequence;
62+
63+
@Override
64+
public boolean equals(Object o) {
65+
if (this == o) {
66+
return true;
67+
}
68+
if (o == null || getClass() != o.getClass()) {
69+
return false;
70+
}
71+
WebhookEventLog that = (WebhookEventLog) o;
72+
return Objects.equals(attempts, that.attempts) &&
73+
Objects.equals(data, that.data) &&
74+
Objects.equals(event, that.event) &&
75+
eventResult == that.eventResult &&
76+
eventType == that.eventType &&
77+
Objects.equals(id, that.id) &&
78+
Objects.equals(insertInstant, that.insertInstant) &&
79+
Objects.equals(lastAttemptInstant, that.lastAttemptInstant) &&
80+
Objects.equals(lastUpdateInstant, that.lastUpdateInstant) &&
81+
Objects.equals(linkedObjectId, that.linkedObjectId) &&
82+
Objects.equals(sequence, that.sequence);
83+
}
84+
85+
public Integer getFailedAttempts() {
86+
return Math.toIntExact(attempts.stream()
87+
.filter(attempt -> attempt.getAttemptResult().equals(WebhookAttemptResult.Failure))
88+
.count());
89+
}
90+
91+
public Integer getSuccessfulAttempts() {
92+
return Math.toIntExact(attempts.stream()
93+
.filter(attempt -> attempt.getAttemptResult().equals(WebhookAttemptResult.Success))
94+
.count());
95+
}
96+
97+
@Override
98+
public int hashCode() {
99+
return Objects.hash(attempts, data, event, eventResult, eventType, id, insertInstant, lastAttemptInstant, lastUpdateInstant, linkedObjectId, sequence);
100+
}
101+
102+
public String toString() {
103+
return ToString.toString(this);
104+
}
105+
}

0 commit comments

Comments
 (0)