Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -78,7 +77,7 @@ public class AgentRegistrationRequest {
public static final String JSON_PROPERTY_DISCOVERY_PROFILES = "discoveryProfiles";

@Nullable
private Set<DiscoveryProfile> discoveryProfiles = new LinkedHashSet<>(Arrays.asList(DiscoveryProfile.ANS_DNSAID));
private Set<DiscoveryProfile> discoveryProfiles = new LinkedHashSet<>();

public AgentRegistrationRequest() {
}
Expand Down Expand Up @@ -301,7 +300,7 @@ public AgentRegistrationRequest discoveryProfiles(@Nullable Set<DiscoveryProfile

public AgentRegistrationRequest addDiscoveryProfilesItem(DiscoveryProfile discoveryProfilesItem) {
if (this.discoveryProfiles == null) {
this.discoveryProfiles = new LinkedHashSet<>(Arrays.asList(DiscoveryProfile.ANS_DNSAID));
this.discoveryProfiles = new LinkedHashSet<>();
}
this.discoveryProfiles.add(discoveryProfilesItem);
return this;
Expand All @@ -324,7 +323,7 @@ public AgentRegistrationRequest addDiscoveryProfilesItem(DiscoveryProfile discov
*/
@Nullable
@JsonProperty(value = JSON_PROPERTY_DISCOVERY_PROFILES, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
@JsonInclude(value = JsonInclude.Include.NON_EMPTY)
public Set<DiscoveryProfile> getDiscoveryProfiles() {
return discoveryProfiles;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.godaddy.ans.sdk.auth.AnsCredentials;
import com.godaddy.ans.sdk.config.AnsConfiguration;
Expand Down Expand Up @@ -168,31 +166,6 @@ String serializeToJson(Object object) {
}
}

/**
* Serializes an object to JSON with the named top-level field removed.
*
* <p>Used to strip v2-only fields (e.g. {@code discoveryProfiles}) from the
* wire body when targeting the v1 lane, without mutating the caller's object.
* Operates on the JSON tree and reuses this client's {@link ObjectMapper}.</p>
*
* @param object the object to serialize
* @param fieldName the top-level field to remove
* @return the JSON string without the named field
* @throws AnsServerException if serialization fails
*/
String serializeToJsonWithoutField(Object object, String fieldName) {
try {
JsonNode tree = objectMapper.valueToTree(object);
if (tree instanceof ObjectNode objectNode) {
objectNode.remove(fieldName);
}

return objectMapper.writeValueAsString(tree);
} catch (IOException e) {
throw new AnsServerException("Failed to serialize request: " + e.getMessage(), 0, e, null);
}
}

ApiVersion getApiVersion() {
return configuration.getApiVersion();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import com.godaddy.ans.sdk.model.AgentRevocationRequest;
import com.godaddy.ans.sdk.model.AgentRevocationResponse;
import com.godaddy.ans.sdk.model.AgentStatus;
import com.godaddy.ans.sdk.model.DiscoveryProfile;
import com.godaddy.ans.sdk.model.Link;
import com.godaddy.ans.sdk.model.RegistrationPending;

import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Set;
import java.util.UUID;

/**
Expand All @@ -35,10 +37,12 @@ class RegistrationService {
* the 'self' link is followed instead (HATEOAS).</p>
*/
AgentDetails register(AgentRegistrationRequest request) {
// discoveryProfiles is a v2-only field; strip it from the wire body on v1.
String requestBody = (apiVersion == ApiVersion.V1)
? httpClient.serializeToJsonWithoutField(request, "discoveryProfiles")
: httpClient.serializeToJson(request);
// discoveryProfiles is a v2-only field. The v1 lane ignores it server-side, so an
// explicit selection on v1 is a client misconfiguration: reject it rather than drop
// it silently. An empty set (the default) carries no selection and is omitted on the wire.
rejectDiscoveryProfilesOnV1(request);

String requestBody = httpClient.serializeToJson(request);

HttpRequest httpRequest = httpClient.createRequestBuilder(AgentPaths.registerPath(apiVersion))
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
Expand All @@ -50,6 +54,25 @@ AgentDetails register(AgentRegistrationRequest request) {
return getAgentDetails(resolveAgentDetailsPath(pending));
}

/**
* Rejects a discoveryProfiles selection on the v1 lane.
*
* <p>discoveryProfiles is a v2-only field. The v1 lane ignores it server-side, so an
* explicit selection is a client misconfiguration. The default (empty) set carries no
* selection and is allowed on either lane.</p>
*/
private void rejectDiscoveryProfilesOnV1(AgentRegistrationRequest request) {
if (apiVersion != ApiVersion.V1) {
return;
}
Set<DiscoveryProfile> profiles = request.getDiscoveryProfiles();
if (profiles != null && !profiles.isEmpty()) {
throw new IllegalArgumentException(
"discoveryProfiles requires ApiVersion.V2; the v1 lane ignores the field. "
+ "Remove the profile selection or build the client with ApiVersion.V2.");
}
}

Comment thread
bchen-godaddy marked this conversation as resolved.
/**
* Resolves the path to fetch full agent details after registration.
*
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
import com.godaddy.ans.sdk.model.AgentEndpoint;
import com.godaddy.ans.sdk.model.AgentRegistrationRequest;
import com.godaddy.ans.sdk.model.AgentRevocationRequest;
import com.godaddy.ans.sdk.model.DiscoveryProfile;
import com.godaddy.ans.sdk.model.Protocol;
import com.godaddy.ans.sdk.model.RevocationReason;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.net.URI;
import java.util.Set;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.absent;
Expand Down Expand Up @@ -88,8 +90,8 @@ void v1RegisterFollowsSelfLink(WireMockRuntimeInfo wm) {
}

@Test
@DisplayName("v1 register omits the v2-only discoveryProfiles field from the wire body")
void v1RegisterStripsDiscoveryProfiles(WireMockRuntimeInfo wm) {
@DisplayName("v1 register omits the empty discoveryProfiles default from the wire body")
void v1RegisterOmitsDefaultDiscoveryProfiles(WireMockRuntimeInfo wm) {
stubFor(post(urlEqualTo("/v1/agents/register"))
.willReturn(aResponse()
.withStatus(202)
Expand All @@ -109,6 +111,42 @@ void v1RegisterStripsDiscoveryProfiles(WireMockRuntimeInfo wm) {
.withRequestBody(matchingJsonPath("$.discoveryProfiles", absent())));
}

@Test
@DisplayName("v1 register rejects an explicit discoveryProfiles selection")
void v1RegisterRejectsExplicitDiscoveryProfiles(WireMockRuntimeInfo wm) {
AgentRegistrationRequest request = sampleRequest()
.discoveryProfiles(Set.of(DiscoveryProfile.ANS_TXT));

assertThatThrownBy(() -> v1Client(wm).registerAgent(request))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("ApiVersion.V2");

// The request must fail before any HTTP call reaches the register endpoint.
verify(0, postRequestedFor(urlEqualTo("/v1/agents/register")));
}

@Test
@DisplayName("v1 register allows null discoveryProfiles")
void v1RegisterAllowsNullDiscoveryProfiles(WireMockRuntimeInfo wm) {
stubFor(post(urlEqualTo("/v1/agents/register"))
.willReturn(aResponse()
.withStatus(202)
.withHeader("Content-Type", "application/json")
.withBody(v1PendingWithSelfLink())));

stubFor(get(urlEqualTo("/v1/agents/" + TEST_AGENT_ID))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody(agentDetails())));

v1Client(wm).registerAgent(sampleRequest().discoveryProfiles(null));

verify(postRequestedFor(urlEqualTo("/v1/agents/register"))
.withRequestBody(containing("\"agentDisplayName\":\"Test Agent\""))
.withRequestBody(matchingJsonPath("$.discoveryProfiles", absent())));
}

@Test
@DisplayName("v1 register throws when self link href is null")
void shouldThrowWhenSelfLinkHrefIsNull(WireMockRuntimeInfo wm) {
Expand Down
Loading
Loading