-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
845 additions
and
0 deletions.
There are no files selected for viewing
58 changes: 58 additions & 0 deletions
58
src/main/java/io/fusionauth/client/json/WebhookEventDeserializer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Copyright (c) 2024, FusionAuth, All Rights Reserved | ||
*/ | ||
package io.fusionauth.client.json; | ||
|
||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.stream.Collectors; | ||
|
||
import com.fasterxml.jackson.core.JsonParser; | ||
import com.fasterxml.jackson.databind.DeserializationContext; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer; | ||
import io.fusionauth.domain.WebhookEventLog; | ||
import io.fusionauth.domain.event.BaseEvent; | ||
import io.fusionauth.domain.event.EventType; | ||
|
||
/** | ||
* Custom JSON de-serializer for BaseEvent. | ||
* | ||
* @author Spencer Witt | ||
*/ | ||
public class WebhookEventDeserializer extends StdDeserializer<BaseEvent> { | ||
public WebhookEventDeserializer() { | ||
super(WebhookEventLog.class); | ||
} | ||
|
||
@Override | ||
public BaseEvent deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { | ||
JsonNode node = p.getCodec().readTree(p); | ||
|
||
BaseEvent newEvent = extractEventType(ctxt, p, node); | ||
return ((ObjectMapper) p.getCodec()).readerForUpdating(newEvent).readValue(node); | ||
} | ||
|
||
private BaseEvent extractEventType(DeserializationContext ctxt, JsonParser p, JsonNode eventNode) | ||
throws IOException { | ||
JsonNode node = eventNode.at("/type"); | ||
String type = node.asText(); | ||
|
||
EventType eventType = EventType.forValue(type); | ||
if (eventType == null) { | ||
// Handle an unexpected EventType and provide a useful error | ||
String sorted = Arrays.stream(EventType.values()).map(Enum::name).sorted().collect(Collectors.joining(", ")); | ||
return (BaseEvent) ctxt.handleUnexpectedToken(BaseEvent.class, node.asToken(), p, | ||
"Expected the type field to be one of [" + sorted + "], but found [" + node.asText() + "]"); | ||
} | ||
|
||
// Assuming all of our events following this naming schema '{EventType}Event' | ||
String className = BaseEvent.class.getPackage().getName() + "." + eventType.name() + "Event"; | ||
try { | ||
return (BaseEvent) Class.forName(className).getConstructor().newInstance(); | ||
} catch (Exception e) { | ||
throw new IllegalStateException("Unexpected type [" + eventType + "]. This is a FusionAuth bug, could not instantiate class [" + className + "]."); | ||
} | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
src/main/java/io/fusionauth/domain/WebhookAttemptLog.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/* | ||
* Copyright (c) 2024, FusionAuth, All Rights Reserved | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, | ||
* either express or implied. See the License for the specific | ||
* language governing permissions and limitations under the License. | ||
*/ | ||
package io.fusionauth.domain; | ||
|
||
import java.time.Duration; | ||
import java.time.ZonedDateTime; | ||
import java.util.LinkedHashMap; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.UUID; | ||
|
||
import com.fasterxml.jackson.annotation.JsonIgnore; | ||
import com.inversoft.json.ToString; | ||
|
||
/** | ||
* A webhook call attempt log. | ||
* | ||
* @author Spencer Witt | ||
*/ | ||
public class WebhookAttemptLog implements Buildable<WebhookAttemptLog>, Comparable<WebhookAttemptLog> { | ||
public Map<String, Object> data = new LinkedHashMap<>(); | ||
|
||
public ZonedDateTime endInstant; | ||
|
||
public UUID id; | ||
|
||
public ZonedDateTime startInstant; | ||
|
||
public WebhookCallResponse webhookCallResponse; | ||
|
||
public UUID webhookEventLogId; | ||
|
||
/** | ||
* The webhook Id for this attempt or {@code null} if it was sent to a Kafka topic. | ||
*/ | ||
public UUID webhookId; | ||
|
||
@Override | ||
public int compareTo(WebhookAttemptLog o) { | ||
// Sort by startInstant and then Id | ||
return startInstant.compareTo(o.startInstant) != 0 ? startInstant.compareTo(o.startInstant) : id.compareTo(o.id); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
WebhookAttemptLog that = (WebhookAttemptLog) o; | ||
return Objects.equals(data, that.data) && | ||
Objects.equals(endInstant, that.endInstant) && | ||
Objects.equals(id, that.id) && | ||
Objects.equals(startInstant, that.startInstant) && | ||
Objects.equals(webhookCallResponse, that.webhookCallResponse) && | ||
Objects.equals(webhookEventLogId, that.webhookEventLogId) && | ||
Objects.equals(webhookId, that.webhookId); | ||
} | ||
|
||
public WebhookAttemptResult getAttemptResult() { | ||
if (webhookCallResponse != null) { | ||
return webhookCallResponse.statusCode >= 200 && webhookCallResponse.statusCode <= 299 ? WebhookAttemptResult.Success : WebhookAttemptResult.Failure; | ||
} | ||
return WebhookAttemptResult.Unknown; | ||
} | ||
|
||
@JsonIgnore | ||
public long getDuration() { | ||
return Duration.between(startInstant, endInstant).toMillis(); | ||
} | ||
|
||
@JsonIgnore | ||
// using Url instead of URL so that we can access this as a property named .url | ||
public String getUrl() { | ||
return webhookCallResponse != null && webhookCallResponse.url != null ? webhookCallResponse.url.toString() : null; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(data, endInstant, id, startInstant, webhookCallResponse, webhookEventLogId, webhookId); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return ToString.toString(this); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/main/java/io/fusionauth/domain/WebhookAttemptResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* Copyright (c) 2024, FusionAuth, All Rights Reserved | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, | ||
* either express or implied. See the License for the specific | ||
* language governing permissions and limitations under the License. | ||
*/ | ||
package io.fusionauth.domain; | ||
|
||
/** | ||
* The possible states of an individual webhook attempt to a single endpoint. | ||
* | ||
* @author Spencer Witt | ||
*/ | ||
public enum WebhookAttemptResult { | ||
Success, | ||
Failure, | ||
Unknown | ||
} |
60 changes: 60 additions & 0 deletions
60
src/main/java/io/fusionauth/domain/WebhookCallResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright (c) 2024, FusionAuth, All Rights Reserved | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, | ||
* either express or implied. See the License for the specific | ||
* language governing permissions and limitations under the License. | ||
*/ | ||
package io.fusionauth.domain; | ||
|
||
import java.net.URI; | ||
import java.util.Objects; | ||
|
||
import com.inversoft.json.ToString; | ||
|
||
/** | ||
* A webhook call response. | ||
* | ||
* @author Spencer Witt | ||
*/ | ||
public class WebhookCallResponse implements Buildable<WebhookCallResponse> { | ||
public String exception; | ||
|
||
public int statusCode; | ||
|
||
/** | ||
* The URI for the webhook endpoint or {@code null} if the event was sent to a Kafka topic. | ||
*/ | ||
public URI url; | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
WebhookCallResponse that = (WebhookCallResponse) o; | ||
return statusCode == that.statusCode && | ||
Objects.equals(exception, that.exception) && | ||
Objects.equals(url, that.url); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(exception, statusCode, url); | ||
} | ||
|
||
public String toString() { | ||
return ToString.toString(this); | ||
} | ||
} |
105 changes: 105 additions & 0 deletions
105
src/main/java/io/fusionauth/domain/WebhookEventLog.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* Copyright (c) 2024, FusionAuth, All Rights Reserved | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, | ||
* either express or implied. See the License for the specific | ||
* language governing permissions and limitations under the License. | ||
*/ | ||
package io.fusionauth.domain; | ||
|
||
import java.time.ZonedDateTime; | ||
import java.util.ArrayList; | ||
import java.util.LinkedHashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.UUID; | ||
|
||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
import com.inversoft.json.ToString; | ||
import io.fusionauth.domain.event.EventRequest; | ||
import io.fusionauth.domain.event.EventType; | ||
|
||
/** | ||
* An instance of a webhook event log. | ||
* | ||
* @author Spencer Witt | ||
*/ | ||
|
||
@JsonIgnoreProperties(value = {"successfulAttempts", "failedAttempts"}, allowGetters = true) | ||
public class WebhookEventLog implements Buildable<WebhookEventLog> { | ||
// Do not include the webhook event log Id for individual attempts when returning as part of the full event | ||
@JsonIgnoreProperties("webhookEventLogId") | ||
public List<WebhookAttemptLog> attempts = new ArrayList<>(); | ||
|
||
public Map<String, Object> data = new LinkedHashMap<>(); | ||
|
||
public EventRequest event; | ||
|
||
public WebhookEventResult eventResult = WebhookEventResult.Running; | ||
|
||
public EventType eventType; | ||
|
||
public UUID id; | ||
|
||
public ZonedDateTime insertInstant; | ||
|
||
public ZonedDateTime lastAttemptInstant; | ||
|
||
public ZonedDateTime lastUpdateInstant; | ||
|
||
public UUID linkedObjectId; | ||
|
||
public Long sequence; | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
WebhookEventLog that = (WebhookEventLog) o; | ||
return Objects.equals(attempts, that.attempts) && | ||
Objects.equals(data, that.data) && | ||
Objects.equals(event, that.event) && | ||
eventResult == that.eventResult && | ||
eventType == that.eventType && | ||
Objects.equals(id, that.id) && | ||
Objects.equals(insertInstant, that.insertInstant) && | ||
Objects.equals(lastAttemptInstant, that.lastAttemptInstant) && | ||
Objects.equals(lastUpdateInstant, that.lastUpdateInstant) && | ||
Objects.equals(linkedObjectId, that.linkedObjectId) && | ||
Objects.equals(sequence, that.sequence); | ||
} | ||
|
||
public Integer getFailedAttempts() { | ||
return Math.toIntExact(attempts.stream() | ||
.filter(attempt -> attempt.getAttemptResult().equals(WebhookAttemptResult.Failure)) | ||
.count()); | ||
} | ||
|
||
public Integer getSuccessfulAttempts() { | ||
return Math.toIntExact(attempts.stream() | ||
.filter(attempt -> attempt.getAttemptResult().equals(WebhookAttemptResult.Success)) | ||
.count()); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(attempts, data, event, eventResult, eventType, id, insertInstant, lastAttemptInstant, lastUpdateInstant, linkedObjectId, sequence); | ||
} | ||
|
||
public String toString() { | ||
return ToString.toString(this); | ||
} | ||
} |
Oops, something went wrong.