Skip to content

Commit

Permalink
Domain sync
Browse files Browse the repository at this point in the history
  • Loading branch information
robotdan committed Sep 5, 2024
1 parent f764371 commit 5cc6a0c
Show file tree
Hide file tree
Showing 15 changed files with 845 additions and 0 deletions.
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 src/main/java/io/fusionauth/domain/WebhookAttemptLog.java
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 src/main/java/io/fusionauth/domain/WebhookAttemptResult.java
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 src/main/java/io/fusionauth/domain/WebhookCallResponse.java
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 src/main/java/io/fusionauth/domain/WebhookEventLog.java
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);
}
}
Loading

0 comments on commit 5cc6a0c

Please sign in to comment.