use of com.linkedin.data.schema.validation.ValidationOptions in project rest.li by linkedin.
the class RequestBuilderSpecGenerator method generateRootRequestBuilder.
private RootBuilderSpec generateRootRequestBuilder(RootBuilderSpec parentRootBuilder, ResourceSchema resource, String sourceFile, Map<String, String> pathKeyTypes) throws IOException {
ValidationResult validationResult = ValidateDataAgainstSchema.validate(resource.data(), resource.schema(), new ValidationOptions(RequiredMode.MUST_BE_PRESENT));
if (!validationResult.isValid()) {
throw new IllegalArgumentException(String.format("Resource validation error. Resource File '%s', Error Details '%s'", sourceFile, validationResult.toString()));
}
String packageName = resource.getNamespace();
String resourceName = CodeUtil.capitalize(resource.getName());
String className;
if (_version == RestliVersion.RESTLI_2_0_0) {
className = getBuilderClassNameByVersion(RestliVersion.RESTLI_2_0_0, null, resource.getName(), true);
} else {
className = getBuilderClassNameByVersion(RestliVersion.RESTLI_1_0_0, null, resource.getName(), true);
}
RootBuilderSpec rootBuilderSpec = null;
if (resource.hasCollection()) {
rootBuilderSpec = new CollectionRootBuilderSpec(resource);
} else if (resource.hasSimple()) {
rootBuilderSpec = new SimpleRootBuilderSpec(resource);
} else if (resource.hasActionsSet()) {
rootBuilderSpec = new ActionSetRootBuilderSpec(resource);
} else {
throw new IllegalArgumentException("unsupported resource type for resource: '" + resourceName + '\'');
}
rootBuilderSpec.setNamespace(packageName);
rootBuilderSpec.setClassName(className);
if (_version == RestliVersion.RESTLI_2_0_0) {
rootBuilderSpec.setBaseClassName("BuilderBase");
}
rootBuilderSpec.setSourceIdlName(sourceFile);
String resourcePath = getResourcePath(resource.getPath());
rootBuilderSpec.setResourcePath(resourcePath);
List<String> pathKeys = getPathKeys(resourcePath);
rootBuilderSpec.setPathKeys(pathKeys);
rootBuilderSpec.setParentRootBuilder(parentRootBuilder);
StringArray supportsList = null;
RestMethodSchemaArray restMethods = null;
FinderSchemaArray finders = null;
ResourceSchemaArray subresources = null;
ActionSchemaArray resourceActions = null;
ActionSchemaArray entityActions = null;
String keyClass = null;
if (resource.getCollection() != null) {
CollectionSchema collection = resource.getCollection();
String keyName = collection.getIdentifier().getName();
// Complex key is not supported
keyClass = collection.getIdentifier().getType();
pathKeyTypes.put(keyName, collection.getIdentifier().getType());
supportsList = collection.getSupports();
restMethods = collection.getMethods();
finders = collection.getFinders();
subresources = collection.getEntity().getSubresources();
resourceActions = collection.getActions();
entityActions = collection.getEntity().getActions();
} else if (resource.getSimple() != null) {
SimpleSchema simpleSchema = resource.getSimple();
keyClass = "Void";
supportsList = simpleSchema.getSupports();
restMethods = simpleSchema.getMethods();
subresources = simpleSchema.getEntity().getSubresources();
resourceActions = simpleSchema.getActions();
} else if (resource.getActionsSet() != null) {
ActionsSetSchema actionsSet = resource.getActionsSet();
resourceActions = actionsSet.getActions();
}
Set<ResourceMethod> supportedMethods = getSupportedMethods(supportsList);
if (!supportedMethods.isEmpty()) {
for (ResourceMethod resourceMethod : supportedMethods) {
validateResourceMethod(resource, resourceName, resourceMethod);
}
}
List<RootBuilderMethodSpec> restMethodSpecs = new ArrayList<RootBuilderMethodSpec>();
List<RootBuilderMethodSpec> finderSpecs = new ArrayList<RootBuilderMethodSpec>();
List<RootBuilderMethodSpec> resourceActionSpecs = new ArrayList<RootBuilderMethodSpec>();
List<RootBuilderMethodSpec> entityActionSpecs = new ArrayList<RootBuilderMethodSpec>();
List<RootBuilderSpec> subresourceSpecs = new ArrayList<RootBuilderSpec>();
String schemaClass = resource.getSchema();
if (restMethods != null) {
restMethodSpecs = generateBasicMethods(rootBuilderSpec, keyClass, schemaClass, supportedMethods, restMethods, resourceName, pathKeys, pathKeyTypes);
}
if (finders != null) {
finderSpecs = generateFinders(rootBuilderSpec, finders, keyClass, schemaClass, resourceName, pathKeys, pathKeyTypes);
}
if (resourceActions != null) {
resourceActionSpecs = generateActions(rootBuilderSpec, resourceActions, keyClass, resourceName, pathKeys, pathKeyTypes);
}
if (entityActions != null) {
entityActionSpecs = generateActions(rootBuilderSpec, entityActions, keyClass, resourceName, pathKeys, pathKeyTypes);
}
if (subresources != null) {
subresourceSpecs = generateSubResources(sourceFile, rootBuilderSpec, subresources, pathKeyTypes);
}
// assign to rootBuilderClass
if (rootBuilderSpec instanceof CollectionRootBuilderSpec) {
CollectionRootBuilderSpec rootBuilder = (CollectionRootBuilderSpec) rootBuilderSpec;
rootBuilder.setRestMethods(restMethodSpecs);
rootBuilder.setFinders(finderSpecs);
rootBuilder.setResourceActions(resourceActionSpecs);
rootBuilder.setEntityActions(entityActionSpecs);
rootBuilder.setSubresources(subresourceSpecs);
} else if (rootBuilderSpec instanceof SimpleRootBuilderSpec) {
SimpleRootBuilderSpec rootBuilder = (SimpleRootBuilderSpec) rootBuilderSpec;
rootBuilder.setRestMethods(restMethodSpecs);
rootBuilder.setResourceActions(resourceActionSpecs);
rootBuilder.setSubresources(subresourceSpecs);
} else if (rootBuilderSpec instanceof ActionSetRootBuilderSpec) {
ActionSetRootBuilderSpec rootBuilder = (ActionSetRootBuilderSpec) rootBuilderSpec;
rootBuilder.setResourceActions(resourceActionSpecs);
}
registerBuilderSpec(rootBuilderSpec);
return rootBuilderSpec;
}
use of com.linkedin.data.schema.validation.ValidationOptions in project rest.li by linkedin.
the class RestLiDataValidator method validatePatch.
/**
* Checks that if the patch is applied to a valid entity, the modified entity will also be valid.
* This method
* (1) Checks that required/ReadOnly/CreateOnly fields are not deleted.
* (2) Checks that new values for record templates contain all required fields.
* (3) Applies the patch to an empty entity and validates the entity for custom validation rules
* and Rest.li annotations (Allows required fields to be absent by using {@link RequiredMode#IGNORE},
* because a patch does not necessarily contain all fields).
*
* NOTE: Updating a part of an array is not supported. So if the array contains a required field that is
* readonly or createonly, the field cannot be present (no partial updates on readonly/createonly)
* but cannot be absent either (no missing required fields). This means the array cannot be changed by a
* partial update request. This is something that should be fixed.
*
* @param patchRequest the patch
* @return the final validation result
*/
private ValidationResult validatePatch(PatchRequest<?> patchRequest) {
// Instantiate an empty entity.
RecordTemplate entity;
try {
entity = _valueClass.newInstance();
} catch (InstantiationException e) {
return validationResultWithErrorMessage(INSTANTIATION_ERROR);
} catch (IllegalAccessException e) {
return validationResultWithErrorMessage(ILLEGAL_ACCESS_ERROR);
}
// Apply the patch to the entity and get paths that $set and $delete operations were performed on.
@SuppressWarnings("unchecked") PatchRequest<RecordTemplate> patch = (PatchRequest<RecordTemplate>) patchRequest;
DataComplexProcessor processor = new DataComplexProcessor(new Patch(true), patch.getPatchDocument(), entity.data());
MessageList<Message> messages;
try {
messages = processor.runDataProcessing(false);
} catch (DataProcessingException e) {
return validationResultWithErrorMessage("Error while applying patch: " + e.getMessage());
}
ValidationErrorResult checkDeleteResult = new ValidationErrorResult();
checkDeletesAreValid(entity.schema(), messages, checkDeleteResult);
if (!checkDeleteResult.isValid()) {
return checkDeleteResult;
}
ValidationResult checkSetResult = checkNewRecordsAreNotMissingFields(entity, messages);
if (checkSetResult != null) {
return checkSetResult;
}
// It's okay if required fields are absent in a partial update request, so use ignore mode.
return ValidateDataAgainstSchema.validate(new SimpleDataElement(entity.data(), entity.schema()), new ValidationOptions(RequiredMode.IGNORE), new DataValidator(entity.schema()));
}
use of com.linkedin.data.schema.validation.ValidationOptions in project rest.li by linkedin.
the class RestLiDataValidator method checkNewRecordsAreNotMissingFields.
private ValidationResult checkNewRecordsAreNotMissingFields(RecordTemplate entity, MessageList<Message> messages) {
for (Message message : messages) {
Object[] path = message.getPath();
if (path[path.length - 1].toString().equals(PatchConstants.SET_COMMAND)) {
// Replace $set with the field name to get the full path
path[path.length - 1] = message.getFormat();
DataElement element = DataElementUtil.element(new SimpleDataElement(entity.data(), entity.schema()), path);
ValidationResult result = ValidateDataAgainstSchema.validate(element, new ValidationOptions());
if (!result.isValid()) {
return result;
}
}
}
return null;
}
use of com.linkedin.data.schema.validation.ValidationOptions in project rest.li by linkedin.
the class RestLiDataValidator method validateInputEntity.
private ValidationResult validateInputEntity(RecordTemplate entity) {
ValidationOptions validationOptions = new ValidationOptions();
if (readOnlyOptional.contains(_resourceMethod)) {
// Even if ReadOnly fields are non-optional, the client cannot supply them in a create request, so they should be treated as optional.
validationOptions.setTreatOptional(_readOnlyPredicate);
}
ValidationResult result = ValidateDataAgainstSchema.validate(entity, validationOptions, new DataValidator(entity.schema()));
return result;
}
use of com.linkedin.data.schema.validation.ValidationOptions in project rest.li by linkedin.
the class TestFilters method testGetOldBuilders.
/**
* This is a simple test that verifies the behavior of request and response filters. This test
* hooks up two filters, one request filter and one response filter to the greeting resource.
*
* The behavior of the request filter is such that if the incoming request is of type create, the
* filter modifies the incoming create request as follows:
*
* 1. If the tone of the incoming greeting is friendly, the filter modifies it to sincere.
*
* 2. If the tone of the incoming greeting is sincere, the filter modifies it to insulting.
*
* 3. If the tone of the incoming greeting is insulting, the filter throws an exception saying
* that creation of a greeting with an insulting tone is not permitted. The HTTP status code is
* set to 403.
*
* The behavior of the response filter is as follows:
*
* 1. If the response is an error, and the HTTP status code is 403, the filter updates the
* outgoing error message and sets the status code to 400.
*
* 2. If the response is not an error, and the incoming request is a get, then the response filter
* modifies the tone of the outgoing greeting message as follows:
*
* a. If the tone of the outgoing greeting from the resource is sincere, the filter modifies it to
* friendly.
*
* b. If the tone of the outgoing greeting from the resource is insulting, the filter modifies it
* to sincere.
*
* @param builders type of request builder.
* @param tone tone of the greeting to be created.
* @param responseFilter flag indicating whether or not the response filter is to be hooked up. NOTE: The
* request filter is always hooked up.
* @param responseFilterException the exception the response filter will throw.
* @throws Exception if anything unexpected happens.
*/
@Test(dataProvider = "requestBuilderDataProvider")
public void testGetOldBuilders(RootBuilderWrapper<Long, Greeting> builders, Tone tone, boolean responseFilter, RuntimeException responseFilterException) throws Exception {
setupFilters(responseFilter, responseFilterException);
Greeting greeting = generateTestGreeting("Test greeting.....", tone);
Long createdId = null;
try {
createdId = createTestData(builders, greeting);
} catch (RestLiResponseException e) {
if (tone != Tone.INSULTING) {
fail();
}
if (responseFilter) {
assertEquals(e.getServiceErrorMessage(), RESP_FILTER_ERROR_MESSAGE);
assertEquals(e.getResponse().getStatus(), RESP_FILTER_ERROR_STATUS.getCode());
} else {
assertEquals(e.getServiceErrorMessage(), REQ_FILTER_ERROR_MESSAGE);
assertEquals(e.getResponse().getStatus(), REQ_FILTER_ERROR_STATUS.getCode());
}
verifyFilters(tone, responseFilter);
return;
}
if (tone == Tone.INSULTING) {
fail();
}
if (!responseFilter) {
greeting.setTone(mapToneForIncomingRequest(tone));
}
greeting.setId(createdId);
Request<Greeting> getRequest = builders.get().id(createdId).build();
Greeting getReturnedGreeting = getClient().sendRequest(getRequest).getResponse().getEntity();
ValidateDataAgainstSchema.validate(getReturnedGreeting.data(), getReturnedGreeting.schema(), new ValidationOptions());
assertEquals(getReturnedGreeting, greeting);
deleteAndVerifyTestData(builders, createdId);
verifyFilters(tone, responseFilter);
}
Aggregations