diff --git a/gradient-labs-client/src/main/java/ai/gradientlabs/client/model/CustomerSupportPlatformIdentifier.java b/gradient-labs-client/src/main/java/ai/gradientlabs/client/model/CustomerSupportPlatformIdentifier.java new file mode 100644 index 0000000..90e30e4 --- /dev/null +++ b/gradient-labs-client/src/main/java/ai/gradientlabs/client/model/CustomerSupportPlatformIdentifier.java @@ -0,0 +1,87 @@ +package ai.gradientlabs.client.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Links a customer to their record in a third-party customer support platform. + *

+ * {@code type} is only meaningful (and only validated) for platforms that have more than one + * kind of identifier: intercom, zendesk, and salesforce. Omit it entirely for freshchat and + * freshdesk, which don't have subtypes. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CustomerSupportPlatformIdentifier { + + @JsonProperty("support_platform") + private SupportPlatform supportPlatform; + + @JsonProperty("type") + private CustomerSupportPlatformIdentifierType type; + + @JsonProperty("value") + private String value; + + /** + * Default constructor for Jackson. + */ + public CustomerSupportPlatformIdentifier() { + } + + /** + * Creates a new identifier for a platform that doesn't have subtypes (e.g. freshchat, freshdesk). + * + * @param supportPlatform the support platform + * @param value the external ID in that platform + */ + public CustomerSupportPlatformIdentifier(SupportPlatform supportPlatform, String value) { + this.supportPlatform = supportPlatform; + this.value = value; + } + + /** + * Creates a new identifier with a subtype (required for intercom, zendesk, and salesforce). + * + * @param supportPlatform the support platform + * @param type the kind of identifier + * @param value the external ID in that platform + */ + public CustomerSupportPlatformIdentifier(SupportPlatform supportPlatform, CustomerSupportPlatformIdentifierType type, String value) { + this.supportPlatform = supportPlatform; + this.type = type; + this.value = value; + } + + public SupportPlatform getSupportPlatform() { + return supportPlatform; + } + + public void setSupportPlatform(SupportPlatform supportPlatform) { + this.supportPlatform = supportPlatform; + } + + public CustomerSupportPlatformIdentifierType getType() { + return type; + } + + public void setType(CustomerSupportPlatformIdentifierType type) { + this.type = type; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "CustomerSupportPlatformIdentifier{" + + "supportPlatform=" + supportPlatform + + ", type=" + type + + ", value='" + value + '\'' + + '}'; + } +} diff --git a/gradient-labs-client/src/main/java/ai/gradientlabs/client/model/CustomerSupportPlatformIdentifierType.java b/gradient-labs-client/src/main/java/ai/gradientlabs/client/model/CustomerSupportPlatformIdentifierType.java new file mode 100644 index 0000000..436fa3d --- /dev/null +++ b/gradient-labs-client/src/main/java/ai/gradientlabs/client/model/CustomerSupportPlatformIdentifierType.java @@ -0,0 +1,55 @@ +package ai.gradientlabs.client.model; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * The kind of identifier a {@link CustomerSupportPlatformIdentifier} represents, for + * platforms that have more than one kind of customer identifier. + */ +public enum CustomerSupportPlatformIdentifierType { + /** + * An Intercom lead ID. + */ + INTERCOM_LEAD("intercom_lead"), + + /** + * An Intercom user ID. + */ + INTERCOM_USER("intercom_user"), + + /** + * A Zendesk end-user ID from a messaging conversation. + */ + ZENDESK_CONVERSATION_USER("zendesk_conversation_user"), + + /** + * A Zendesk support (ticketing) user ID. + */ + ZENDESK_SUPPORT_USER("zendesk_support_user"), + + /** + * A Salesforce Contact ID. + */ + SALESFORCE_CONTACT_ID("salesforce_contact_id"), + + /** + * A Salesforce Account ID. + */ + SALESFORCE_ACCOUNT_ID("salesforce_account_id"); + + private final String value; + + CustomerSupportPlatformIdentifierType(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/gradient-labs-client/src/main/java/ai/gradientlabs/client/model/SupportPlatform.java b/gradient-labs-client/src/main/java/ai/gradientlabs/client/model/SupportPlatform.java new file mode 100644 index 0000000..a704c54 --- /dev/null +++ b/gradient-labs-client/src/main/java/ai/gradientlabs/client/model/SupportPlatform.java @@ -0,0 +1,49 @@ +package ai.gradientlabs.client.model; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * A third-party customer support platform. + */ +public enum SupportPlatform { + /** + * Intercom. + */ + INTERCOM("intercom"), + + /** + * Zendesk. + */ + ZENDESK("zendesk"), + + /** + * Salesforce. + */ + SALESFORCE("salesforce"), + + /** + * Freshchat. + */ + FRESHCHAT("freshchat"), + + /** + * Freshdesk. + */ + FRESHDESK("freshdesk"); + + private final String value; + + SupportPlatform(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/gradient-labs-client/src/main/java/ai/gradientlabs/client/request/StartConversationRequest.java b/gradient-labs-client/src/main/java/ai/gradientlabs/client/request/StartConversationRequest.java index ebe5e1d..60f6351 100644 --- a/gradient-labs-client/src/main/java/ai/gradientlabs/client/request/StartConversationRequest.java +++ b/gradient-labs-client/src/main/java/ai/gradientlabs/client/request/StartConversationRequest.java @@ -1,12 +1,15 @@ package ai.gradientlabs.client.request; import ai.gradientlabs.client.model.Channel; +import ai.gradientlabs.client.model.CustomerSupportPlatformIdentifier; import ai.gradientlabs.client.model.ParticipantType; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.time.Instant; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -45,6 +48,9 @@ public class StartConversationRequest { @JsonProperty("traffic_group_id") private final String trafficGroupId; + @JsonProperty("customer_support_platform_identifiers") + private final List customerSupportPlatformIdentifiers; + private StartConversationRequest(Builder builder) { this.id = builder.id; this.customerId = builder.customerId; @@ -56,6 +62,7 @@ private StartConversationRequest(Builder builder) { this.resources = builder.resources; this.conversationToken = builder.conversationToken; this.trafficGroupId = builder.trafficGroupId; + this.customerSupportPlatformIdentifiers = builder.customerSupportPlatformIdentifiers; } public static Builder builder() { @@ -107,6 +114,15 @@ public String getTrafficGroupId() { return trafficGroupId; } + /** + * Gets the customer's identifiers in third-party customer support platforms. + * + * @return the customer support platform identifiers, or null if none were set + */ + public List getCustomerSupportPlatformIdentifiers() { + return customerSupportPlatformIdentifiers; + } + public static class Builder { private String id; private String customerId; @@ -118,6 +134,7 @@ public static class Builder { private Map resources; private String conversationToken; private String trafficGroupId; + private List customerSupportPlatformIdentifiers; private Builder() { } @@ -285,6 +302,35 @@ public Builder trafficGroupId(String trafficGroupId) { return this; } + /** + * Sets the customer's identifiers in third-party customer support platforms (optional). + *

+ * Links the customer being created to their record(s) in platforms such as Intercom + * or Zendesk, alongside {@code customerId}. Each identifier's {@code type} is only + * required (and only validated) for intercom, zendesk, and salesforce. + * + * @param customerSupportPlatformIdentifiers the customer support platform identifiers + * @return this builder + */ + public Builder customerSupportPlatformIdentifiers(List customerSupportPlatformIdentifiers) { + this.customerSupportPlatformIdentifiers = customerSupportPlatformIdentifiers; + return this; + } + + /** + * Adds a single customer support platform identifier. + * + * @param customerSupportPlatformIdentifier the identifier to add + * @return this builder + */ + public Builder addCustomerSupportPlatformIdentifier(CustomerSupportPlatformIdentifier customerSupportPlatformIdentifier) { + if (this.customerSupportPlatformIdentifiers == null) { + this.customerSupportPlatformIdentifiers = new ArrayList<>(); + } + this.customerSupportPlatformIdentifiers.add(customerSupportPlatformIdentifier); + return this; + } + /** * Builds the request. * diff --git a/gradient-labs-client/src/test/java/ai/gradientlabs/client/request/StartConversationRequestTest.java b/gradient-labs-client/src/test/java/ai/gradientlabs/client/request/StartConversationRequestTest.java new file mode 100644 index 0000000..32f382a --- /dev/null +++ b/gradient-labs-client/src/test/java/ai/gradientlabs/client/request/StartConversationRequestTest.java @@ -0,0 +1,89 @@ +package ai.gradientlabs.client.request; + +import ai.gradientlabs.client.model.Channel; +import ai.gradientlabs.client.model.CustomerSupportPlatformIdentifier; +import ai.gradientlabs.client.model.CustomerSupportPlatformIdentifierType; +import ai.gradientlabs.client.model.SupportPlatform; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +class StartConversationRequestTest { + + private final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); + + @Test + void omitsCustomerSupportPlatformIdentifiersWhenNotSet() throws Exception { + StartConversationRequest request = StartConversationRequest.builder() + .id("conv-1") + .customerId("user-1") + .channel(Channel.CHAT) + .build(); + + JsonNode json = objectMapper.valueToTree(request); + + assertFalse(json.has("customer_support_platform_identifiers")); + } + + @Test + void serializesCustomerSupportPlatformIdentifiersWithAndWithoutType() throws Exception { + StartConversationRequest request = StartConversationRequest.builder() + .id("conv-1") + .customerId("user-1") + .channel(Channel.CHAT) + .addCustomerSupportPlatformIdentifier(new CustomerSupportPlatformIdentifier( + SupportPlatform.INTERCOM, + CustomerSupportPlatformIdentifierType.INTERCOM_USER, + "6953e162a988d9ef0f73ef9b")) + .addCustomerSupportPlatformIdentifier(new CustomerSupportPlatformIdentifier( + SupportPlatform.FRESHDESK, + "12345")) + .build(); + + JsonNode json = objectMapper.valueToTree(request); + JsonNode identifiers = json.get("customer_support_platform_identifiers"); + + assertEquals(2, identifiers.size()); + + JsonNode intercomIdentifier = identifiers.get(0); + assertEquals("intercom", intercomIdentifier.get("support_platform").asText()); + assertEquals("intercom_user", intercomIdentifier.get("type").asText()); + assertEquals("6953e162a988d9ef0f73ef9b", intercomIdentifier.get("value").asText()); + + JsonNode freshdeskIdentifier = identifiers.get(1); + assertEquals("freshdesk", freshdeskIdentifier.get("support_platform").asText()); + assertEquals("12345", freshdeskIdentifier.get("value").asText()); + assertFalse(freshdeskIdentifier.has("type")); + } + + @Test + void roundTripsThroughDeserialization() throws Exception { + StartConversationRequest request = StartConversationRequest.builder() + .id("conv-1") + .customerId("user-1") + .channel(Channel.CHAT) + .addCustomerSupportPlatformIdentifier(new CustomerSupportPlatformIdentifier( + SupportPlatform.ZENDESK, + CustomerSupportPlatformIdentifierType.ZENDESK_SUPPORT_USER, + "42")) + .build(); + + String json = objectMapper.writeValueAsString(request); + JsonNode tree = objectMapper.readTree(json); + List identifiers = objectMapper.convertValue( + tree.get("customer_support_platform_identifiers"), + objectMapper.getTypeFactory().constructCollectionType(List.class, CustomerSupportPlatformIdentifier.class)); + + assertEquals(1, identifiers.size()); + assertEquals(SupportPlatform.ZENDESK, identifiers.get(0).getSupportPlatform()); + assertEquals(CustomerSupportPlatformIdentifierType.ZENDESK_SUPPORT_USER, identifiers.get(0).getType()); + assertEquals("42", identifiers.get(0).getValue()); + assertEquals(1, request.getCustomerSupportPlatformIdentifiers().size()); + } +} diff --git a/pom.xml b/pom.xml index 582174e..0744945 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,16 @@ ${project.version} + + + com.fasterxml.jackson + jackson-bom + ${jackson.version} + pom + import + + org.springframework.boot @@ -69,18 +79,6 @@ import - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson.version} - - jakarta.servlet