mirror of
https://github.com/datahub-project/datahub.git
synced 2025-10-26 00:14:53 +00:00
feat(oss): add clientId to ctaLink (#15071)
This commit is contained in:
parent
eed191c72e
commit
f5d0efcc73
@ -1007,7 +1007,8 @@ public class GmsGraphQLEngine {
|
|||||||
this.s3Util != null))
|
this.s3Util != null))
|
||||||
.dataFetcher(
|
.dataFetcher(
|
||||||
"latestProductUpdate",
|
"latestProductUpdate",
|
||||||
new ProductUpdateResolver(this.productUpdateService, this.featureFlags))
|
new ProductUpdateResolver(
|
||||||
|
this.productUpdateService, this.featureFlags, this.entityService))
|
||||||
.dataFetcher("me", new MeResolver(this.entityClient, featureFlags))
|
.dataFetcher("me", new MeResolver(this.entityClient, featureFlags))
|
||||||
.dataFetcher("search", new SearchResolver(this.entityClient))
|
.dataFetcher("search", new SearchResolver(this.entityClient))
|
||||||
.dataFetcher(
|
.dataFetcher(
|
||||||
|
|||||||
@ -2,6 +2,9 @@ package com.linkedin.datahub.graphql.resolvers.config;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.linkedin.datahub.graphql.generated.ProductUpdate;
|
import com.linkedin.datahub.graphql.generated.ProductUpdate;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -20,13 +23,26 @@ public class ProductUpdateParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse JSON into a ProductUpdate object.
|
* Parse JSON into a ProductUpdate object without clientId decoration.
|
||||||
*
|
*
|
||||||
* @param jsonOpt Optional JSON node containing product update data
|
* @param jsonOpt Optional JSON node containing product update data
|
||||||
* @return ProductUpdate object if parsing succeeds and update is enabled, null otherwise
|
* @return ProductUpdate object if parsing succeeds and update is enabled, null otherwise
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static ProductUpdate parseProductUpdate(@Nonnull Optional<JsonNode> jsonOpt) {
|
public static ProductUpdate parseProductUpdate(@Nonnull Optional<JsonNode> jsonOpt) {
|
||||||
|
return parseProductUpdate(jsonOpt, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse JSON into a ProductUpdate object, decorating the ctaLink with clientId if provided.
|
||||||
|
*
|
||||||
|
* @param jsonOpt Optional JSON node containing product update data
|
||||||
|
* @param clientId Optional client ID to append to ctaLink as a query parameter
|
||||||
|
* @return ProductUpdate object if parsing succeeds and update is enabled, null otherwise
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static ProductUpdate parseProductUpdate(
|
||||||
|
@Nonnull Optional<JsonNode> jsonOpt, @Nullable String clientId) {
|
||||||
if (jsonOpt.isEmpty()) {
|
if (jsonOpt.isEmpty()) {
|
||||||
log.debug("No product update JSON available");
|
log.debug("No product update JSON available");
|
||||||
return null;
|
return null;
|
||||||
@ -51,6 +67,11 @@ public class ProductUpdateParser {
|
|||||||
String ctaText = json.has("ctaText") ? json.get("ctaText").asText() : "Learn more";
|
String ctaText = json.has("ctaText") ? json.get("ctaText").asText() : "Learn more";
|
||||||
String ctaLink = json.has("ctaLink") ? json.get("ctaLink").asText() : "";
|
String ctaLink = json.has("ctaLink") ? json.get("ctaLink").asText() : "";
|
||||||
|
|
||||||
|
// Decorate ctaLink with clientId if provided
|
||||||
|
if (clientId != null && !clientId.trim().isEmpty() && !ctaLink.isEmpty()) {
|
||||||
|
ctaLink = decorateUrlWithClientId(ctaLink, clientId);
|
||||||
|
}
|
||||||
|
|
||||||
// Build the ProductUpdate response
|
// Build the ProductUpdate response
|
||||||
ProductUpdate productUpdate = new ProductUpdate();
|
ProductUpdate productUpdate = new ProductUpdate();
|
||||||
productUpdate.setEnabled(enabled);
|
productUpdate.setEnabled(enabled);
|
||||||
@ -69,4 +90,26 @@ public class ProductUpdateParser {
|
|||||||
|
|
||||||
return productUpdate;
|
return productUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorates a URL with a clientId query parameter.
|
||||||
|
*
|
||||||
|
* <p>Adds "?q={clientId}" if the URL has no query parameters, or "&q={clientId}" if it already
|
||||||
|
* has query parameters.
|
||||||
|
*
|
||||||
|
* @param url The URL to decorate
|
||||||
|
* @param clientId The client ID to append
|
||||||
|
* @return The decorated URL
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
private static String decorateUrlWithClientId(@Nonnull String url, @Nonnull String clientId) {
|
||||||
|
try {
|
||||||
|
String encodedClientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.toString());
|
||||||
|
String separator = url.contains("?") ? "&" : "?";
|
||||||
|
return url + separator + "q=" + encodedClientId;
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
log.warn("Failed to URL-encode clientId, using original URL: {}", e.getMessage());
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
package com.linkedin.datahub.graphql.resolvers.config;
|
package com.linkedin.datahub.graphql.resolvers.config;
|
||||||
|
|
||||||
|
import com.linkedin.common.urn.UrnUtils;
|
||||||
|
import com.linkedin.data.template.RecordTemplate;
|
||||||
|
import com.linkedin.datahub.graphql.QueryContext;
|
||||||
import com.linkedin.datahub.graphql.featureflags.FeatureFlags;
|
import com.linkedin.datahub.graphql.featureflags.FeatureFlags;
|
||||||
import com.linkedin.datahub.graphql.generated.ProductUpdate;
|
import com.linkedin.datahub.graphql.generated.ProductUpdate;
|
||||||
|
import com.linkedin.metadata.Constants;
|
||||||
|
import com.linkedin.metadata.entity.EntityService;
|
||||||
import com.linkedin.metadata.service.ProductUpdateService;
|
import com.linkedin.metadata.service.ProductUpdateService;
|
||||||
|
import com.linkedin.telemetry.TelemetryClientId;
|
||||||
import graphql.schema.DataFetcher;
|
import graphql.schema.DataFetcher;
|
||||||
import graphql.schema.DataFetchingEnvironment;
|
import graphql.schema.DataFetchingEnvironment;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@ -17,22 +23,28 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
* disabled.
|
* disabled.
|
||||||
*
|
*
|
||||||
* <p>Supports an optional {@code refreshCache} argument to clear the cache before fetching.
|
* <p>Supports an optional {@code refreshCache} argument to clear the cache before fetching.
|
||||||
|
*
|
||||||
|
* <p>Decorates the CTA link with the instance's client ID.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ProductUpdateResolver implements DataFetcher<CompletableFuture<ProductUpdate>> {
|
public class ProductUpdateResolver implements DataFetcher<CompletableFuture<ProductUpdate>> {
|
||||||
|
|
||||||
private final ProductUpdateService _productUpdateService;
|
private final ProductUpdateService _productUpdateService;
|
||||||
private final FeatureFlags _featureFlags;
|
private final FeatureFlags _featureFlags;
|
||||||
|
private final EntityService<?> _entityService;
|
||||||
|
|
||||||
public ProductUpdateResolver(
|
public ProductUpdateResolver(
|
||||||
@Nonnull final ProductUpdateService productUpdateService,
|
@Nonnull final ProductUpdateService productUpdateService,
|
||||||
@Nonnull final FeatureFlags featureFlags) {
|
@Nonnull final FeatureFlags featureFlags,
|
||||||
|
@Nonnull final EntityService<?> entityService) {
|
||||||
this._productUpdateService = productUpdateService;
|
this._productUpdateService = productUpdateService;
|
||||||
this._featureFlags = featureFlags;
|
this._featureFlags = featureFlags;
|
||||||
|
this._entityService = entityService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<ProductUpdate> get(DataFetchingEnvironment environment) {
|
public CompletableFuture<ProductUpdate> get(DataFetchingEnvironment environment) {
|
||||||
|
final QueryContext context = environment.getContext();
|
||||||
final Boolean refreshCache = environment.getArgument("refreshCache");
|
final Boolean refreshCache = environment.getArgument("refreshCache");
|
||||||
final boolean shouldRefresh = refreshCache != null && refreshCache;
|
final boolean shouldRefresh = refreshCache != null && refreshCache;
|
||||||
|
|
||||||
@ -49,9 +61,22 @@ public class ProductUpdateResolver implements DataFetcher<CompletableFuture<Prod
|
|||||||
_productUpdateService.clearCache();
|
_productUpdateService.clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String clientId = null;
|
||||||
|
try {
|
||||||
|
clientId = getClientId(context);
|
||||||
|
if (clientId != null) {
|
||||||
|
log.debug("Retrieved client ID for product update decoration: {}", clientId);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn(
|
||||||
|
"Failed to retrieve client ID, product update link will not be decorated: {}",
|
||||||
|
e.getMessage());
|
||||||
|
log.debug("Client ID retrieval error details", e);
|
||||||
|
}
|
||||||
|
|
||||||
ProductUpdate productUpdate =
|
ProductUpdate productUpdate =
|
||||||
ProductUpdateParser.parseProductUpdate(
|
ProductUpdateParser.parseProductUpdate(
|
||||||
_productUpdateService.getLatestProductUpdate());
|
_productUpdateService.getLatestProductUpdate(), clientId);
|
||||||
|
|
||||||
if (productUpdate != null) {
|
if (productUpdate != null) {
|
||||||
log.debug(
|
log.debug(
|
||||||
@ -72,4 +97,21 @@ public class ProductUpdateResolver implements DataFetcher<CompletableFuture<Prod
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getClientId(@Nonnull final QueryContext context) {
|
||||||
|
try {
|
||||||
|
RecordTemplate clientIdAspect =
|
||||||
|
_entityService.getLatestAspect(
|
||||||
|
context.getOperationContext(),
|
||||||
|
UrnUtils.getUrn(Constants.CLIENT_ID_URN),
|
||||||
|
Constants.CLIENT_ID_ASPECT);
|
||||||
|
|
||||||
|
if (clientIdAspect instanceof TelemetryClientId) {
|
||||||
|
return ((TelemetryClientId) clientIdAspect).getClientId();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("Error retrieving client ID: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -383,4 +383,194 @@ public class ProductUpdateParserTest {
|
|||||||
assertNotNull(result.getDescription());
|
assertNotNull(result.getDescription());
|
||||||
assertEquals(result.getDescription().length(), 10000);
|
assertEquals(result.getDescription().length(), 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateWithClientId() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
String clientId = "abc-123-def-456";
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode), clientId);
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getCtaLink(), "https://example.com?q=abc-123-def-456");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateWithClientIdAndExistingQueryParams() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com?foo=bar\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
String clientId = "abc-123-def-456";
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode), clientId);
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getCtaLink(), "https://example.com?foo=bar&q=abc-123-def-456");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateWithClientIdMultipleQueryParams() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com?foo=bar&baz=qux#anchor\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
String clientId = "test-uuid";
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode), clientId);
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getCtaLink(), "https://example.com?foo=bar&baz=qux#anchor&q=test-uuid");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateWithNullClientId() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode), null);
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getCtaLink(), "https://example.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateWithEmptyClientId() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode), "");
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getCtaLink(), "https://example.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateWithWhitespaceClientId() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode), " ");
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getCtaLink(), "https://example.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateWithClientIdAndEmptyCtaLink() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
String clientId = "abc-123";
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode), clientId);
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getCtaLink(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateWithClientIdAndNoCtaLink() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{" + "\"enabled\": true," + "\"id\": \"v1.0.0\"," + "\"title\": \"What's New\"" + "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
String clientId = "abc-123";
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode), clientId);
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getCtaLink(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateWithClientIdSpecialCharacters() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
String clientId = "abc 123+def/456";
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode), clientId);
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getCtaLink(), "https://example.com?q=abc+123%2Bdef%2F456");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateWithClientIdUnicodeCharacters() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
String clientId = "测试-client-id-🎉";
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode), clientId);
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertTrue(result.getCtaLink().startsWith("https://example.com?q="));
|
||||||
|
assertTrue(result.getCtaLink().contains("%"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseProductUpdateBackwardCompatibilityWithoutClientId() throws Exception {
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
|
||||||
|
ProductUpdate result = ProductUpdateParser.parseProductUpdate(Optional.of(jsonNode));
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getCtaLink(), "https://example.com");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,16 @@ import static org.testng.Assert.*;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.linkedin.common.urn.UrnUtils;
|
||||||
|
import com.linkedin.datahub.graphql.QueryContext;
|
||||||
import com.linkedin.datahub.graphql.featureflags.FeatureFlags;
|
import com.linkedin.datahub.graphql.featureflags.FeatureFlags;
|
||||||
import com.linkedin.datahub.graphql.generated.ProductUpdate;
|
import com.linkedin.datahub.graphql.generated.ProductUpdate;
|
||||||
|
import com.linkedin.metadata.Constants;
|
||||||
|
import com.linkedin.metadata.entity.EntityService;
|
||||||
import com.linkedin.metadata.service.ProductUpdateService;
|
import com.linkedin.metadata.service.ProductUpdateService;
|
||||||
|
import com.linkedin.telemetry.TelemetryClientId;
|
||||||
import graphql.schema.DataFetchingEnvironment;
|
import graphql.schema.DataFetchingEnvironment;
|
||||||
|
import io.datahubproject.metadata.context.OperationContext;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
@ -19,7 +25,10 @@ public class ProductUpdateResolverTest {
|
|||||||
|
|
||||||
@Mock private ProductUpdateService mockProductUpdateService;
|
@Mock private ProductUpdateService mockProductUpdateService;
|
||||||
@Mock private FeatureFlags mockFeatureFlags;
|
@Mock private FeatureFlags mockFeatureFlags;
|
||||||
|
@Mock private EntityService<?> mockEntityService;
|
||||||
@Mock private DataFetchingEnvironment mockDataFetchingEnvironment;
|
@Mock private DataFetchingEnvironment mockDataFetchingEnvironment;
|
||||||
|
@Mock private QueryContext mockQueryContext;
|
||||||
|
@Mock private OperationContext mockOperationContext;
|
||||||
|
|
||||||
private ProductUpdateResolver resolver;
|
private ProductUpdateResolver resolver;
|
||||||
private ObjectMapper objectMapper;
|
private ObjectMapper objectMapper;
|
||||||
@ -28,7 +37,10 @@ public class ProductUpdateResolverTest {
|
|||||||
public void setupTest() {
|
public void setupTest() {
|
||||||
MockitoAnnotations.openMocks(this);
|
MockitoAnnotations.openMocks(this);
|
||||||
objectMapper = new ObjectMapper();
|
objectMapper = new ObjectMapper();
|
||||||
resolver = new ProductUpdateResolver(mockProductUpdateService, mockFeatureFlags);
|
resolver =
|
||||||
|
new ProductUpdateResolver(mockProductUpdateService, mockFeatureFlags, mockEntityService);
|
||||||
|
when(mockDataFetchingEnvironment.getContext()).thenReturn(mockQueryContext);
|
||||||
|
when(mockQueryContext.getOperationContext()).thenReturn(mockOperationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -357,4 +369,62 @@ public class ProductUpdateResolverTest {
|
|||||||
assertEquals(result.getCtaText(), "");
|
assertEquals(result.getCtaText(), "");
|
||||||
assertEquals(result.getCtaLink(), "");
|
assertEquals(result.getCtaLink(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetProductUpdateWithClientIdDecoration() throws Exception {
|
||||||
|
// Setup
|
||||||
|
when(mockFeatureFlags.isShowProductUpdates()).thenReturn(true);
|
||||||
|
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
when(mockProductUpdateService.getLatestProductUpdate()).thenReturn(Optional.of(jsonNode));
|
||||||
|
|
||||||
|
TelemetryClientId clientIdAspect = new TelemetryClientId().setClientId("test-client-id-123");
|
||||||
|
when(mockEntityService.getLatestAspect(
|
||||||
|
eq(mockOperationContext),
|
||||||
|
eq(UrnUtils.getUrn(Constants.CLIENT_ID_URN)),
|
||||||
|
eq(Constants.CLIENT_ID_ASPECT)))
|
||||||
|
.thenReturn(clientIdAspect);
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
ProductUpdate result = resolver.get(mockDataFetchingEnvironment).get();
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getId(), "v1.0.0");
|
||||||
|
assertEquals(result.getCtaLink(), "https://example.com?q=test-client-id-123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetProductUpdateWithClientIdDecorationFailure() throws Exception {
|
||||||
|
// Setup
|
||||||
|
when(mockFeatureFlags.isShowProductUpdates()).thenReturn(true);
|
||||||
|
|
||||||
|
String jsonString =
|
||||||
|
"{"
|
||||||
|
+ "\"enabled\": true,"
|
||||||
|
+ "\"id\": \"v1.0.0\","
|
||||||
|
+ "\"title\": \"What's New\","
|
||||||
|
+ "\"ctaLink\": \"https://example.com\""
|
||||||
|
+ "}";
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(jsonString);
|
||||||
|
when(mockProductUpdateService.getLatestProductUpdate()).thenReturn(Optional.of(jsonNode));
|
||||||
|
|
||||||
|
when(mockEntityService.getLatestAspect(any(), any(), any()))
|
||||||
|
.thenThrow(new RuntimeException("Entity service error"));
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
ProductUpdate result = resolver.get(mockDataFetchingEnvironment).get();
|
||||||
|
|
||||||
|
// Verify - should still return product update without clientId decoration
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(result.getId(), "v1.0.0");
|
||||||
|
assertEquals(result.getCtaLink(), "https://example.com");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user