Skip to content
Snippets Groups Projects
Commit 1fa2df9f authored by Alok Joshi's avatar Alok Joshi
Browse files

Merge branch 'use_azure_native_patch' of...

Merge branch 'use_azure_native_patch' of https://community.opengroup.org/osdu/platform/system/storage into use_azure_native_patch
parents 7e64dff8 282949b4
No related branches found
No related tags found
2 merge requests!744Upgraded packages to mitigated vulns in netty, guava, snakeyaml,!639Extend patch API to support data block modification
Pipeline #174470 failed
Showing
with 352 additions and 278 deletions
......@@ -23,8 +23,7 @@ import lombok.NoArgsConstructor;
import org.opengroup.osdu.core.common.model.storage.SwaggerDoc;
import org.opengroup.osdu.core.common.model.storage.validation.ValidationDoc;
import org.opengroup.osdu.storage.validation.api.ValidBulkQueryPatch;
import org.opengroup.osdu.storage.validation.api.ValidJsonPatchOperation;
import org.opengroup.osdu.storage.validation.api.ValidJsonPatchPath;
import org.opengroup.osdu.storage.validation.api.ValidJsonPatch;
import javax.validation.constraints.NotNull;
......@@ -41,7 +40,6 @@ public class PatchRecordsRequestModel {
@ApiModelProperty(value = org.opengroup.osdu.storage.swagger.SwaggerDoc.PATCH_RECORD_OPERATIONS, required = true)
@NotNull(message = org.opengroup.osdu.storage.validation.ValidationDoc.PATCH_RECORD_OPERATIONS_NOT_EMPTY)
@ValidJsonPatchOperation(message = org.opengroup.osdu.storage.validation.ValidationDoc.INVALID_PATCH_OPERATION)
@ValidJsonPatchPath(message = org.opengroup.osdu.storage.validation.ValidationDoc.INVALID_PATCH_PATH)
@ValidJsonPatch
private JsonPatch ops;
}
......@@ -16,10 +16,11 @@ package org.opengroup.osdu.storage.validation;
public class ValidationDoc {
public static final String PATCH_RECORD_OPERATIONS_NOT_EMPTY = "Record patch operations cannot be empty";
public static final String INVALID_PATCH_PATH = "Invalid Patch Path: can only be '/acl/viewers', 'acl/owners', '/legal/legaltags', '/tags', '/kind', '/ancestry/parents', '/data' or '/meta'";
public static final String INVALID_PATCH_PATH_START = "Invalid Patch Path: can only starts with '/acl/viewers', 'acl/owners', '/legal/legaltags', '/tags', '/kind', '/ancestry/parents', '/data' or '/meta'";
public static final String INVALID_PATCH_OPERATION = "Invalid Patch Operation: can only be 'replace' or 'add' or 'remove'";
public static final String INVALID_PATCH_OPERATION_SIZE = "Invalid Patch Operation: the number of operations can only be between 1 and 100";
public static final String INVALID_PATCH_PATH_FOR_ADD_OR_REMOVE_OPERATION = "Invalid Patch Operation: path for add/remove operation must contain index of the value to be added/deleted";
public static final String INVALID_PATCH_PATH_FOR_ADD_OPERATION = "Invalid Patch Operation: path for add operation must contain index of the value to be added";
public static final String INVALID_PATCH_PATH_FOR_REMOVE_OPERATION = "Invalid Patch Operation: path for remove operation must contain index of the value to be deleted";
public static final String INVALID_PATCH_PATH_END = "Invalid Patch Operation: path cannot ends with '/'";
public static final String INVALID_PATCH_OPERATION_TYPE_FOR_KIND = "Invalid Patch Operation: for patching '/kind' only 'replace' operation is allowed";
public static final String INVALID_PATCH_PATH_FOR_KIND = "Invalid Patch Operation: for patching 'kind' only '/kind' path is allowed";
......@@ -28,4 +29,6 @@ public class ValidationDoc {
public static final String RECORD_ID_LIST_NOT_EMPTY = "The list of record IDs cannot be empty";
public static final String PATCH_RECORDS_MAX = "Up to 100 records can be patched at a time";
public static final String INVALID_RECORD_ID_PATCH = "Invalid record format: '%s'. The following format is expected: {tenant-name}:{object-type}:{unique-identifier}";
public static final String INVALID_PATCH_VALUE_FOR_ADD_OPERATION = "Invalid Patch Operation: for 'add' operation only single 'value' is allowed";
public static final String INVALID_PATCH_VALUE_FOR_REPLACE_OPERATION = "Invalid Patch Operation: for 'replace' operation with the specified index(or end of an array sign '-') of the element to be replaced, only single value is allowed";
}
......@@ -14,7 +14,7 @@
package org.opengroup.osdu.storage.validation.api;
import org.opengroup.osdu.storage.validation.impl.JsonPatchPathValidator;
import org.opengroup.osdu.storage.validation.impl.JsonPatchValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
......@@ -26,9 +26,9 @@ import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {JsonPatchPathValidator.class})
@Constraint(validatedBy = {JsonPatchValidator.class})
@Documented
public @interface ValidJsonPatchPath {
public @interface ValidJsonPatch {
String message() default "";
Class<?>[] groups() default {};
......
// Copyright 2017-2023, Schlumberger
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.opengroup.osdu.storage.validation.api;
import org.opengroup.osdu.storage.validation.impl.JsonPatchOperationValidator;
import java.lang.annotation.*;
import javax.validation.Constraint;
import javax.validation.Payload;
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {JsonPatchOperationValidator.class})
@Documented
public @interface ValidJsonPatchOperation {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// Copyright 2017-2023, Schlumberger
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.opengroup.osdu.storage.validation.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.fge.jsonpatch.JsonPatch;
import org.opengroup.osdu.storage.util.api.PatchOperations;
import org.opengroup.osdu.storage.validation.RequestValidationException;
import org.opengroup.osdu.storage.validation.ValidationDoc;
import org.opengroup.osdu.storage.validation.api.ValidJsonPatchOperation;
import org.springframework.http.HttpStatus;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.function.Predicate;
import java.util.stream.StreamSupport;
import static org.opengroup.osdu.storage.util.api.PatchOperations.ADD;
import static org.opengroup.osdu.storage.util.api.PatchOperations.REMOVE;
import static org.opengroup.osdu.storage.util.api.PatchOperations.REPLACE;
public class JsonPatchOperationValidator implements ConstraintValidator<ValidJsonPatchOperation, JsonPatch> {
public static final int MIN_NUMBER = 1;
public static final int MAX_NUMBER = 100;
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean isValid(JsonPatch jsonPatch, ConstraintValidatorContext context) {
boolean isNumberOfOperationsValid;
boolean isOperationTypeValid;
long operationsNumber = objectMapper.convertValue(jsonPatch, JsonNode.class).size();
isNumberOfOperationsValid = operationsNumber >= MIN_NUMBER && operationsNumber <= MAX_NUMBER;
if (!isNumberOfOperationsValid) {
throw RequestValidationException.builder()
.status(HttpStatus.BAD_REQUEST)
.message(ValidationDoc.INVALID_PATCH_OPERATION_SIZE)
.build();
}
isOperationTypeValid = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
.map(operation -> operation.get("op").toString().replace("\"", ""))
.map(PatchOperations::forOperation)
.allMatch(getAllowedOperations());
if (!isOperationTypeValid) {
throw RequestValidationException.builder()
.status(HttpStatus.BAD_REQUEST)
.message(ValidationDoc.INVALID_PATCH_OPERATION)
.build();
}
return true;
}
private Predicate<PatchOperations> getAllowedOperations() {
return operation -> ADD.equals(operation) || REMOVE.equals(operation) || REPLACE.equals(operation);
}
}
......@@ -16,11 +16,13 @@ package org.opengroup.osdu.storage.validation.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.github.fge.jsonpatch.JsonPatch;
import org.opengroup.osdu.storage.util.api.PatchOperations;
import org.opengroup.osdu.storage.validation.RequestValidationException;
import org.opengroup.osdu.storage.validation.ValidationDoc;
import org.opengroup.osdu.storage.validation.api.ValidJsonPatchPath;
import org.opengroup.osdu.storage.validation.api.ValidJsonPatch;
import org.springframework.http.HttpStatus;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
......@@ -32,30 +34,68 @@ import java.util.stream.StreamSupport;
import static org.opengroup.osdu.storage.util.api.PatchOperations.ADD;
import static org.opengroup.osdu.storage.util.api.PatchOperations.REMOVE;
import static org.opengroup.osdu.storage.util.api.PatchOperations.REPLACE;
public class JsonPatchPathValidator implements ConstraintValidator<ValidJsonPatchPath, JsonPatch> {
public class JsonPatchValidator implements ConstraintValidator<ValidJsonPatch, JsonPatch> {
public static final int MIN_NUMBER = 1;
public static final int MAX_NUMBER = 100;
private static final Set<String> VALID_PATH_BEGINNINGS = new HashSet<>(Arrays.asList("/tags", "/acl/viewers", "/acl/owners", "/legal/legaltags", "/ancestry/parents", "/kind", "/data", "/meta"));
private static final Set<String> INVALID_PATHS_FOR_REMOVE_OR_ADD_OPERATION = new HashSet<>(Arrays.asList("/acl/viewers", "/acl/owners", "/legal/legaltags"));
private static final String PATH = "path";
private static final Set<String> INVALID_PATHS_FOR_ADD_OPERATION = new HashSet<>(Arrays.asList("/tags", "/acl/viewers", "/acl/owners", "/legal/legaltags", "/ancestry/parents"));
private static final Set<String> INVALID_PATHS_FOR_REMOVE_OPERATION = new HashSet<>(Arrays.asList("/acl/viewers", "/acl/owners", "/legal/legaltags"));
private static final String OP = "op";
private static final String PATH = "path";
private static final String VALUE = "value";
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean isValid(JsonPatch jsonPatch, ConstraintValidatorContext context) {
boolean isPathStartValid;
boolean isPathEndValid;
boolean isValidPathForRemoveOperations;
isPathStartValid = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
validateOperationSize(jsonPatch);
validateOperationType(jsonPatch);
validatePathStartAndEnd(jsonPatch);
validateAddOperation(jsonPatch);
validateReplaceOperation(jsonPatch);
validateRemoveOperation(jsonPatch);
return true;
}
private void validateOperationSize(JsonPatch jsonPatch) {
long operationsNumber = objectMapper.convertValue(jsonPatch, JsonNode.class).size();
boolean isNumberOfOperationsValid = operationsNumber >= MIN_NUMBER && operationsNumber <= MAX_NUMBER;
if (!isNumberOfOperationsValid) {
throw RequestValidationException.builder()
.status(HttpStatus.BAD_REQUEST)
.message(ValidationDoc.INVALID_PATCH_OPERATION_SIZE)
.build();
}
}
private void validateOperationType(JsonPatch jsonPatch) {
boolean isOperationTypeValid = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
.map(operation -> removeExtraQuotes(operation.get(OP)))
.map(PatchOperations::forOperation)
.allMatch(getAllowedOperations());
if (!isOperationTypeValid) {
throw RequestValidationException.builder()
.status(HttpStatus.BAD_REQUEST)
.message(ValidationDoc.INVALID_PATCH_OPERATION)
.build();
}
}
private void validatePathStartAndEnd(JsonPatch jsonPatch) {
boolean isPathStartValid = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
.map(operation -> removeExtraQuotes(operation.get(PATH)))
.allMatch(getAllowedPaths());
.allMatch(getAllowedPathStart());
if (!isPathStartValid) {
throw RequestValidationException.builder()
.message(ValidationDoc.INVALID_PATCH_PATH)
.message(ValidationDoc.INVALID_PATCH_PATH_START)
.build();
}
isPathEndValid = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
boolean isPathEndValid = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
.map(operation -> removeExtraQuotes(operation.get(PATH)))
.noneMatch(getInvalidPathEnd());
if (!isPathEndValid) {
......@@ -63,21 +103,62 @@ public class JsonPatchPathValidator implements ConstraintValidator<ValidJsonPatc
.message(ValidationDoc.INVALID_PATCH_PATH_END)
.build();
}
}
private void validateAddOperation(JsonPatch jsonPatch) {
//multiple values are not allowed for add operation
boolean isAddOperationValueValid = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
.filter(addOperation())
.map(operation -> operation.get(VALUE).getClass())
.noneMatch(aClass -> aClass.equals(ArrayNode.class));
if (!isAddOperationValueValid) {
throw RequestValidationException.builder()
.status(HttpStatus.BAD_REQUEST)
.message(ValidationDoc.INVALID_PATCH_VALUE_FOR_ADD_OPERATION)
.build();
}
isValidPathForRemoveOperations = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
.filter(addOrRemoveOperation())
boolean isValidPathForAddOperations = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
.filter(addOperation())
.map(operation -> removeExtraQuotes(operation.get(PATH)))
.noneMatch(getInvalidPathsForAddOperation());
if (!isValidPathForAddOperations) {
throw RequestValidationException.builder()
.message(ValidationDoc.INVALID_PATCH_PATH_FOR_ADD_OPERATION)
.build();
}
}
private void validateRemoveOperation(JsonPatch jsonPatch) {
boolean isValidPathForRemoveOperations = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
.filter(removeOperation())
.map(operation -> removeExtraQuotes(operation.get(PATH)))
.noneMatch(getInvalidPathsForRemoveOperation());
if (!isValidPathForRemoveOperations) {
throw RequestValidationException.builder()
.message(ValidationDoc.INVALID_PATCH_PATH_FOR_ADD_OR_REMOVE_OPERATION)
.message(ValidationDoc.INVALID_PATCH_PATH_FOR_REMOVE_OPERATION)
.build();
}
}
return true;
private void validateReplaceOperation(JsonPatch jsonPatch) {
//single value is allowed for replace operation with the path ends with index(number) or endOfArray sign '-'
boolean isReplaceOperationValid = StreamSupport.stream(objectMapper.convertValue(jsonPatch, JsonNode.class).spliterator(), false)
.filter(replaceOperation())
.filter(pathEndsWithNumberOrEndOfArraySign())
.map(operation -> operation.get(VALUE).getClass())
.noneMatch(aClass -> aClass.equals(ArrayNode.class));
if (!isReplaceOperationValid) {
throw RequestValidationException.builder()
.status(HttpStatus.BAD_REQUEST)
.message(ValidationDoc.INVALID_PATCH_VALUE_FOR_REPLACE_OPERATION)
.build();
}
//TODO multiple values are allowed for replace operation with the path exactly /acls/viewers, acls/owners, /legal/legaltags, /ancestry/parents, ...
}
private Predicate<String> getAllowedPaths() {
private Predicate<String> getAllowedPathStart() {
return path -> VALID_PATH_BEGINNINGS.stream().anyMatch(path::startsWith);
}
......@@ -85,15 +166,35 @@ public class JsonPatchPathValidator implements ConstraintValidator<ValidJsonPatc
return path -> path.endsWith("/");
}
private Predicate<JsonNode> addOrRemoveOperation() {
private Predicate<JsonNode> addOperation() {
return operation -> ADD.equals(PatchOperations.forOperation(removeExtraQuotes(operation.get(OP))));
}
private Predicate<JsonNode> removeOperation() {
return operation -> REMOVE.equals(PatchOperations.forOperation(removeExtraQuotes(operation.get(OP))));
}
private Predicate<String> getInvalidPathsForAddOperation() {
return path -> INVALID_PATHS_FOR_ADD_OPERATION.stream().anyMatch(path::equals);
}
private Predicate<? super String> getInvalidPathsForRemoveOperation() {
return path -> INVALID_PATHS_FOR_REMOVE_OPERATION.stream().anyMatch(path::equals);
}
private Predicate<? super JsonNode> pathEndsWithNumberOrEndOfArraySign() {
return operation -> {
PatchOperations patchOperation = PatchOperations.forOperation(removeExtraQuotes(operation.get(OP)));
return ADD.equals(patchOperation) || REMOVE.equals(patchOperation);
String path = removeExtraQuotes(operation.get(PATH));
return path.matches(".+?\\d") || path.endsWith("/-");
};
}
private Predicate<String> getInvalidPathsForRemoveOperation() {
return path -> INVALID_PATHS_FOR_REMOVE_OR_ADD_OPERATION.stream().anyMatch(path::equals);
private Predicate<PatchOperations> getAllowedOperations() {
return operation -> ADD.equals(operation) || REMOVE.equals(operation) || REPLACE.equals(operation);
}
private Predicate<JsonNode> replaceOperation() {
return operation -> REPLACE.equals(PatchOperations.forOperation(removeExtraQuotes(operation.get(OP))));
}
private String removeExtraQuotes(JsonNode jsonNode) {
......
......@@ -96,7 +96,7 @@ public class PatchApiControllerTest extends ApiTest<PatchRecordsRequestModel> {
AppError appError = gson.fromJson(response.getContentAsString(), AppError.class);
Assert.assertEquals(400, appError.getCode());
Assert.assertEquals("Validation failed", appError.getReason());
Assert.assertEquals(ValidationDoc.INVALID_PATCH_PATH, appError.getMessage());
Assert.assertEquals(ValidationDoc.INVALID_PATCH_PATH_START, appError.getMessage());
}
@Test
......@@ -198,10 +198,8 @@ public class PatchApiControllerTest extends ApiTest<PatchRecordsRequestModel> {
return "[\n" +
" {\n" +
" \"op\": \"add\",\n" +
" \"path\": \"/tags\",\n" +
" \"value\": {\n" +
" \"tag3\" : \"value3\"\n" +
" }\n" +
" \"path\": \"/tags/tag3\",\n" +
" \"value\": \"value3\"\n" +
" }\n" +
"]";
}
......
// Copyright 2017-2023, Schlumberger
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.opengroup.osdu.storage.validation;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.fge.jsonpatch.JsonPatch;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.opengroup.osdu.storage.validation.impl.JsonPatchOperationValidator;
import javax.validation.ConstraintValidatorContext;
import java.io.IOException;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@RunWith(MockitoJUnitRunner.Silent.class)
public class JsonPatchOperationValidatorTest {
private final ObjectMapper mapper = new ObjectMapper();
@Mock
private ConstraintValidatorContext context;
private JsonPatchOperationValidator sut;
@Rule
public ExpectedException exceptionRule = ExpectedException.none();
@Before
public void setup() {
sut = new JsonPatchOperationValidator();
ConstraintValidatorContext.ConstraintViolationBuilder builder = mock(ConstraintValidatorContext.ConstraintViolationBuilder.class);
}
@Test
public void should_doNothingInInitialize() {
// for coverage purposes. Do nothing method!
this.sut.initialize(null);
}
@Test(expected = RequestValidationException.class)
public void shouldThrowException_ifPatchHasMoveOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"move\"," +
" \"from\": \"/acl/viewers\"," +
" \"path\": \"/acl/owners\"" +
" }]";
sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context);
}
@Test(expected = RequestValidationException.class)
public void shouldThrowException_ifPatchHasCopyOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"copy\"," +
" \"from\": \"/acl/viewers\"," +
" \"path\": \"/acl/owners\"" +
" }]";
sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context);
}
@Test(expected = RequestValidationException.class)
public void shouldThrowException_ifPatchHasTestOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"test\"," +
" \"path\": \"/acl/viewers\"," +
" \"value\": \"some_value\"" +
" }]";
sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context);
}
@Test
public void should_returnTrue_ifPatchHasAddOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"add\"," +
" \"path\": \"/acl/viewers\"," +
" \"value\": \"some_value\"" +
" }]";
assertTrue(sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context));
}
@Test
public void should_returnTrue_ifPatchHasRemoveOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"remove\"," +
" \"path\": \"/acl/viewers/1\"" +
" }]";
assertTrue(sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context));
}
@Test
public void should_returnTrue_ifPatchHasReplaceOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"replace\"," +
" \"path\": \"/acl/viewers\"," +
" \"value\": \"some_value\"" +
" }]";
assertTrue(sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context));
}
@Test
public void shouldThrowException_ifPatchHasEmptyOperations() throws IOException {
String jsonString = "[]";
exceptionRule.expect(RequestValidationException.class);
exceptionRule.expectMessage(ValidationDoc.INVALID_PATCH_OPERATION_SIZE);
sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context);
}
@Test
public void shouldThrowException_ifPatchExceedLimitOfOperations() throws IOException {
StringBuilder jsonString = new StringBuilder("[");
for (int i = 0; i <= JsonPatchOperationValidator.MAX_NUMBER; i++) {
jsonString.append("{\"op\": \"add\", \"path\": \"/acl/viewers\", \"value\": \"value\"},");
}
jsonString.deleteCharAt(jsonString.length() - 1).append("]");
exceptionRule.expect(RequestValidationException.class);
exceptionRule.expectMessage(ValidationDoc.INVALID_PATCH_OPERATION_SIZE);
sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString.toString())), context);
}
}
......@@ -23,7 +23,7 @@ import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.opengroup.osdu.storage.validation.impl.JsonPatchPathValidator;
import org.opengroup.osdu.storage.validation.impl.JsonPatchValidator;
import javax.validation.ConstraintValidatorContext;
import java.io.IOException;
......@@ -31,7 +31,7 @@ import java.io.IOException;
import static org.junit.Assert.assertTrue;
@RunWith(MockitoJUnitRunner.class)
public class JsonPatchPathValidatorTest {
public class JsonPatchValidatorTest {
private final ObjectMapper mapper = new ObjectMapper();
......@@ -41,11 +41,11 @@ public class JsonPatchPathValidatorTest {
@Rule
public ExpectedException exceptionRule = ExpectedException.none();
private JsonPatchPathValidator sut;
private JsonPatchValidator sut;
@Before
public void setup() {
sut = new JsonPatchPathValidator();
sut = new JsonPatchValidator();
}
@Test
......@@ -54,6 +54,127 @@ public class JsonPatchPathValidatorTest {
this.sut.initialize(null);
}
@Test
public void shouldThrowException_ifPatchHasEmptyOperations() throws IOException {
String jsonString = "[]";
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_OPERATION_SIZE);
}
@Test
public void shouldThrowException_ifPatchExceedLimitOfOperations() throws IOException {
StringBuilder jsonString = new StringBuilder("[");
for (int i = 0; i <= JsonPatchValidator.MAX_NUMBER; i++) {
jsonString.append("{\"op\": \"add\", \"path\": \"/acl/viewers\", \"value\": \"value\"},");
}
jsonString.deleteCharAt(jsonString.length() - 1).append("]");
exceptionRule.expect(RequestValidationException.class);
exceptionRule.expectMessage(ValidationDoc.INVALID_PATCH_OPERATION_SIZE);
sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString.toString())), context);
}
@Test
public void shouldThrowException_ifPatchHasMoveOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"move\"," +
" \"from\": \"/acl/viewers\"," +
" \"path\": \"/acl/owners\"" +
" }]";
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_OPERATION);
}
@Test
public void shouldThrowException_ifPatchHasCopyOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"copy\"," +
" \"from\": \"/acl/viewers\"," +
" \"path\": \"/acl/owners\"" +
" }]";
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_OPERATION);
}
@Test
public void shouldThrowException_ifPatchHasTestOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"test\"," +
" \"path\": \"/acl/viewers\"," +
" \"value\": \"some_value\"" +
" }]";
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_OPERATION);
}
@Test
public void should_returnTrue_ifPatchHasAddOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"add\"," +
" \"path\": \"/acl/viewers/-\"," +
" \"value\": \"some_value\"" +
" }]";
assertTrue(sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context));
}
@Test
public void should_returnTrue_ifPatchHasRemoveOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"remove\"," +
" \"path\": \"/acl/viewers/1\"" +
" }]";
assertTrue(sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context));
}
@Test
public void should_returnTrue_ifPatchHasReplaceOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"replace\"," +
" \"path\": \"/acl/viewers\"," +
" \"value\": \"some_value\"" +
" }]";
assertTrue(sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context));
}
@Test
public void shouldThrowException_ifPatchHasMultipleValuesForAddOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"add\"," +
" \"path\": \"/acl/viewers\"," +
" \"value\": [\"value1\", \"value2\"]" +
" }]";
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_VALUE_FOR_ADD_OPERATION);
}
@Test
public void shouldThrowException_ifPatchHasMultipleValuesForReplaceOperationWithSpecifiedIndexInPath() throws IOException {
String jsonString = "[{" +
" \"op\": \"replace\"," +
" \"path\": \"/acl/viewers/1\"," +
" \"value\": [\"value1\", \"value2\"]" +
" }]";
exceptionRule.expect(RequestValidationException.class);
exceptionRule.expectMessage(ValidationDoc.INVALID_PATCH_VALUE_FOR_REPLACE_OPERATION);
sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context);
}
@Test
public void shouldThrowException_ifPatchHasMultipleValuesForReplaceOperationWithSpecifiedEndOfArrayInPath() throws IOException {
String jsonString = "[{" +
" \"op\": \"replace\"," +
" \"path\": \"/acl/viewers/-\"," +
" \"value\": [\"value1\", \"value2\"]" +
" }]";
exceptionRule.expect(RequestValidationException.class);
exceptionRule.expectMessage(ValidationDoc.INVALID_PATCH_VALUE_FOR_REPLACE_OPERATION);
sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context);
}
@Test(expected = RequestValidationException.class)
public void should_returnFalse_ifPatchHasInvalidPath() throws IOException {
String jsonString = "[{" +
......@@ -84,7 +205,7 @@ public class JsonPatchPathValidatorTest {
" \"value\": \"some_value\"" +
" }]";
exceptionRulesAndMethodRun(jsonString);
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_PATH_FOR_ADD_OPERATION);
}
@Test
......@@ -99,25 +220,25 @@ public class JsonPatchPathValidatorTest {
}
@Test
public void should_fail_ifPatchHasInValidLegalPathForAddOperation() throws IOException {
public void should_fail_ifPatchHasInvalidLegalPathForAddOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"add\"," +
" \"path\": \"/legal/legaltags\"," +
" \"value\": \"some_value\"" +
" }]";
exceptionRulesAndMethodRun(jsonString);
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_PATH_FOR_ADD_OPERATION);
}
@Test
public void should_returnTrue_ifPatchHasValidAncestryPath() throws IOException {
public void should_fail_ifPatchHasInvalidAncestryPathForAddOperation() throws IOException {
String jsonString = "[{" +
" \"op\": \"add\"," +
" \"path\": \"/ancestry/parents\"," +
" \"value\": \"some_value\"" +
" }]";
assertTrue(sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context));
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_PATH_FOR_ADD_OPERATION);
}
@Test
......@@ -156,19 +277,19 @@ public class JsonPatchPathValidatorTest {
@Test
public void shouldThrowException_ifPatchHasInValidPathForRemoveLegalTagsOperation() throws IOException {
String jsonString = "[{ \"op\": \"remove\", \"path\": \"/legal/legaltags\" }]";
exceptionRulesAndMethodRun(jsonString);
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_PATH_FOR_REMOVE_OPERATION);
}
@Test
public void shouldThrowException_ifPatchHasInValidPathForRemoveAclViewersOperation() throws IOException {
String jsonString = "[{ \"op\": \"remove\", \"path\": \"/acl/viewers\" }]";
exceptionRulesAndMethodRun(jsonString);
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_PATH_FOR_REMOVE_OPERATION);
}
@Test
public void shouldThrowException_ifPatchHasInValidPathForRemoveAclOwnersOperation() throws IOException {
String jsonString = "[{ \"op\": \"remove\", \"path\": \"/acl/owners\" }]";
exceptionRulesAndMethodRun(jsonString);
exceptionRulesAndMethodRun(jsonString, ValidationDoc.INVALID_PATCH_PATH_FOR_REMOVE_OPERATION);
}
@Test
......@@ -192,9 +313,9 @@ public class JsonPatchPathValidatorTest {
assertTrue(sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context));
}
private void exceptionRulesAndMethodRun(String jsonString) throws IOException {
private void exceptionRulesAndMethodRun(String jsonString, String message) throws IOException {
exceptionRule.expect(RequestValidationException.class);
exceptionRule.expectMessage(ValidationDoc.INVALID_PATCH_PATH_FOR_ADD_OR_REMOVE_OPERATION);
exceptionRule.expectMessage(message);
sut.isValid(JsonPatch.fromJson(mapper.readTree(jsonString)), context);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment