Skip to content

Commit

Permalink
translate ModelValidationException to 4XX (#489)
Browse files Browse the repository at this point in the history
  • Loading branch information
JiaoMaWHU authored Jan 2, 2025
1 parent afc85c2 commit d2e3af5
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 42 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.linkedin.metadata.dao.exception;

/**
* Exception thrown when the requested aspect is not defined in the asset model / there is invalid aspect present
* in the database that is not defined in the asset model.
*/
public class ModelValidationException extends RuntimeException {

public ModelValidationException(String message) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.linkedin.data.template.StringArray;
import com.linkedin.data.template.UnionTemplate;
import com.linkedin.metadata.dao.AspectKey;
import com.linkedin.metadata.dao.exception.ModelValidationException;
import com.linkedin.metadata.dao.ingestion.AspectCallbackRegistry;
import com.linkedin.metadata.dao.ingestion.AspectCallbackResponse;
import com.linkedin.metadata.dao.ingestion.AspectCallbackRoutingClient;
Expand Down Expand Up @@ -143,8 +144,12 @@ protected Task<VALUE> get(@Nonnull KEY id, @QueryParam(PARAM_ASPECTS) @Optional
public Task<SNAPSHOT> getSnapshot(@ActionParam(PARAM_URN) @Nonnull String urnString,
@ActionParam(PARAM_ASPECTS) @Optional @Nullable String[] aspectNames) {
final URN urn = parseUrnParam(urnString);
return getSnapshot(urnString, aspectNames,
getResourceLix().testGetSnapshot(String.valueOf(urn), ModelUtils.getEntityType(urn)));
try {
return getSnapshot(urnString, aspectNames,
getResourceLix().testGetSnapshot(String.valueOf(urn), ModelUtils.getEntityType(urn)));
} catch (ModelValidationException e) {
throw RestliUtils.invalidArgumentsException(e.getMessage());
}
}

@Nonnull
Expand Down Expand Up @@ -202,27 +207,31 @@ protected Task<SNAPSHOT> getSnapshot(@ActionParam(PARAM_URN) @Nonnull String urn
@Override
public Task<ASSET> getAsset(@ActionParam(PARAM_URN) @Nonnull String urnString,
@ActionParam(PARAM_ASPECTS) @Optional @Nullable String[] aspectNames) {
try {
return RestliUtils.toTask(() -> {
final URN urn = parseUrnParam(urnString);
final Set<Class<? extends RecordTemplate>> aspectClasses = parseAspectsParam(aspectNames, true);

return RestliUtils.toTask(() -> {
final URN urn = parseUrnParam(urnString);
final Set<Class<? extends RecordTemplate>> aspectClasses = parseAspectsParam(aspectNames, true);

if (!containsRoutingAspect(aspectClasses)) {
// Get snapshot from Local DAO.
final List<INTERNAL_ASPECT_UNION> aspectUnions = getInternalAspectsFromLocalDao(urn, aspectClasses);
return ModelUtils.newAsset(_assetClass, urn, aspectUnions);
} else {
final Set<Class<? extends RecordTemplate>> nonRoutingAspects = getNonRoutingAspects(aspectClasses);
final List<INTERNAL_ASPECT_UNION> aspectsFromLocalDao = getInternalAspectsFromLocalDao(urn, nonRoutingAspects);
final Set<Class<? extends RecordTemplate>> routingAspects = getRoutingAspects(aspectClasses);
final List<INTERNAL_ASPECT_UNION> aspectsFromGms = routingAspects.stream()
.map(routingAspect -> getInternalAspectsFromGms(urn, routingAspect))
.flatMap(List::stream)
.collect(Collectors.toList());
return ModelUtils.newAsset(_assetClass, urn,
Stream.concat(aspectsFromGms.stream(), aspectsFromLocalDao.stream()).collect(Collectors.toList()));
}
});
if (!containsRoutingAspect(aspectClasses)) {
// Get snapshot from Local DAO.
final List<INTERNAL_ASPECT_UNION> aspectUnions = getInternalAspectsFromLocalDao(urn, aspectClasses);
return ModelUtils.newAsset(_assetClass, urn, aspectUnions);
} else {
final Set<Class<? extends RecordTemplate>> nonRoutingAspects = getNonRoutingAspects(aspectClasses);
final List<INTERNAL_ASPECT_UNION> aspectsFromLocalDao =
getInternalAspectsFromLocalDao(urn, nonRoutingAspects);
final Set<Class<? extends RecordTemplate>> routingAspects = getRoutingAspects(aspectClasses);
final List<INTERNAL_ASPECT_UNION> aspectsFromGms = routingAspects.stream()
.map(routingAspect -> getInternalAspectsFromGms(urn, routingAspect))
.flatMap(List::stream)
.collect(Collectors.toList());
return ModelUtils.newAsset(_assetClass, urn,
Stream.concat(aspectsFromGms.stream(), aspectsFromLocalDao.stream()).collect(Collectors.toList()));
}
});
} catch (ModelValidationException e) {
throw RestliUtils.invalidArgumentsException(e.getMessage());
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.linkedin.metadata.dao.BaseLocalDAO;
import com.linkedin.metadata.dao.ListResult;
import com.linkedin.metadata.dao.UrnAspectEntry;
import com.linkedin.metadata.dao.exception.ModelValidationException;
import com.linkedin.metadata.dao.tracking.BaseTrackingManager;
import com.linkedin.metadata.dao.utils.ModelUtils;
import com.linkedin.metadata.events.IngestionMode;
Expand Down Expand Up @@ -477,9 +478,13 @@ protected Task<Void> rawIngestAssetInternal(@Nonnull ASSET asset,
@Nonnull
public Task<SNAPSHOT> getSnapshot(@ActionParam(PARAM_URN) @Nonnull String urnString,
@ActionParam(PARAM_ASPECTS) @Optional @Nullable String[] aspectNames) {
final URN urn = parseUrnParam(urnString);
return getSnapshot(urnString, aspectNames,
getResourceLix().testGetSnapshot(String.valueOf(urn), ModelUtils.getEntityType(urn)));
try {
final URN urn = parseUrnParam(urnString);
return getSnapshot(urnString, aspectNames,
getResourceLix().testGetSnapshot(String.valueOf(urn), ModelUtils.getEntityType(urn)));
} catch (ModelValidationException e) {
throw RestliUtils.invalidArgumentsException(e.getMessage());
}
}

@Deprecated
Expand Down Expand Up @@ -525,27 +530,30 @@ protected Task<SNAPSHOT> getSnapshot(@ActionParam(PARAM_URN) @Nonnull String urn
@Nonnull
public Task<ASSET> getAsset(@ActionParam(PARAM_URN) @Nonnull String urnString,
@ActionParam(PARAM_ASPECTS) @Optional @Nullable String[] aspectNames) {
try {
return RestliUtils.toTask(() -> {
final URN urn = parseUrnParam(urnString);

return RestliUtils.toTask(() -> {
final URN urn = parseUrnParam(urnString);

if (!getLocalDAO().exists(urn)) {
throw RestliUtils.resourceNotFoundException();
}
if (!getLocalDAO().exists(urn)) {
throw RestliUtils.resourceNotFoundException();
}

final Set<AspectKey<URN, ? extends RecordTemplate>> keys = parseAspectsParam(aspectNames, true).stream()
.map(aspectClass -> new AspectKey<>(aspectClass, urn, LATEST_VERSION))
.collect(Collectors.toSet());
final Set<AspectKey<URN, ? extends RecordTemplate>> keys = parseAspectsParam(aspectNames, true).stream()
.map(aspectClass -> new AspectKey<>(aspectClass, urn, LATEST_VERSION))
.collect(Collectors.toSet());

final List<UnionTemplate> aspects = getLocalDAO().get(keys)
.values()
.stream()
.filter(java.util.Optional::isPresent)
.map(aspect -> ModelUtils.newAspectUnion(_internalAspectUnionClass, aspect.get()))
.collect(Collectors.toList());
final List<UnionTemplate> aspects = getLocalDAO().get(keys)
.values()
.stream()
.filter(java.util.Optional::isPresent)
.map(aspect -> ModelUtils.newAspectUnion(_internalAspectUnionClass, aspect.get()))
.collect(Collectors.toList());

return ModelUtils.newAsset(_assetClass, urn, aspects);
});
return ModelUtils.newAsset(_assetClass, urn, aspects);
});
} catch (ModelValidationException e) {
throw RestliUtils.invalidArgumentsException(e.getMessage());
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.linkedin.metadata.dao.ListResult;
import com.linkedin.metadata.dao.UrnAspectEntry;
import com.linkedin.metadata.dao.builder.BaseLocalRelationshipBuilder.LocalRelationshipUpdates;
import com.linkedin.metadata.dao.exception.ModelValidationException;
import com.linkedin.metadata.dao.internal.BaseGraphWriterDAO;
import com.linkedin.metadata.dao.utils.ModelUtils;
import com.linkedin.metadata.dao.utils.RecordUtils;
Expand Down Expand Up @@ -713,6 +714,18 @@ public void testGetSnapshotWithOneAspect() {
assertEquals(snapshot.getAspects().get(0).getAspectFoo(), foo);
}

@Test
public void testGetSnapshotWithModelValidationException() {
FooUrn urn = makeFooUrn(1);
AspectFoo foo = new AspectFoo().setValue("foo");
AspectKey<FooUrn, ? extends RecordTemplate> fooKey = new AspectKey<>(AspectFoo.class, urn, LATEST_VERSION);
Set<AspectKey<FooUrn, ? extends RecordTemplate>> aspectKeys = ImmutableSet.of(fooKey);
when(_mockLocalDAO.get(aspectKeys)).thenThrow(new ModelValidationException("model validation exception"));
String[] aspectNames = new String[]{ModelUtils.getAspectName(AspectFoo.class)};

assertThrows(RestLiServiceException.class, () -> runAndWait(_resource.getSnapshot(urn.toString(), aspectNames)));
}

@Test
public void testGetSnapshotWithAllAspects() {
FooUrn urn = makeFooUrn(1);
Expand Down Expand Up @@ -1487,6 +1500,20 @@ public void testGetAsset() {
assertEquals(asset.getAspectAttributes(), attributes);
}

@Test
public void testGetAssetWithModelValidationException() {
FooUrn urn = makeFooUrn(1);

AspectKey<FooUrn, ? extends RecordTemplate> fooKey = new AspectKey<>(AspectFoo.class, urn, LATEST_VERSION);

Set<AspectKey<FooUrn, ? extends RecordTemplate>> aspectKeys =
ImmutableSet.of(fooKey);
when(_mockLocalDAO.get(aspectKeys)).thenThrow(new ModelValidationException("model validation exception"));

assertThrows(RestLiServiceException.class,
() -> runAndWait(_resource.getAsset(urn.toString(), new String[] { "com.linkedin.testing.AspectFoo" })));
}

@Test
public void testGetNonExistAsset() {
// Test get non existing assets
Expand Down

0 comments on commit d2e3af5

Please sign in to comment.