Skip to content
Snippets Groups Projects
Commit bd7bd08f authored by Neelesh Thakur's avatar Neelesh Thakur
Browse files

address schema array processing bug & handle custom response code

parent 62860973
No related branches found
No related tags found
2 merge requests!183Locking down maven central,!179Address schema parsing bug for `array` type if `items` is null or missing & handle custom response code
Pipeline #55239 failed
...@@ -83,6 +83,12 @@ public class GlobalExceptionMapperCore extends ResponseEntityExceptionHandler { ...@@ -83,6 +83,12 @@ public class GlobalExceptionMapperCore extends ResponseEntityExceptionHandler {
this.logger.warning(exceptionMsg, e); this.logger.warning(exceptionMsg, e);
} }
return new ResponseEntity<Object>(e.getError(), HttpStatus.resolve(e.getError().getCode())); // Support for non standard HttpStatus Codes
HttpStatus httpStatus = HttpStatus.resolve(e.getError().getCode());
if (httpStatus == null) {
return ResponseEntity.status(e.getError().getCode()).body(e);
} else {
return new ResponseEntity<>(e.getError(), httpStatus);
}
} }
} }
\ No newline at end of file
...@@ -14,6 +14,15 @@ ...@@ -14,6 +14,15 @@
package org.opengroup.osdu.indexer.schema.converter; package org.opengroup.osdu.indexer.schema.converter;
import org.apache.http.HttpStatus;
import org.opengroup.osdu.core.common.Constants;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.opengroup.osdu.core.common.search.Preconditions;
import org.opengroup.osdu.indexer.schema.converter.config.SchemaConverterConfig;
import org.opengroup.osdu.indexer.schema.converter.config.SchemaConverterPropertiesConfig;
import org.opengroup.osdu.indexer.schema.converter.exeption.SchemaProcessingException;
import org.opengroup.osdu.indexer.schema.converter.tags.*;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
...@@ -24,18 +33,6 @@ import java.util.Optional; ...@@ -24,18 +33,6 @@ import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.http.HttpStatus;
import org.opengroup.osdu.core.common.Constants;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.opengroup.osdu.core.common.search.Preconditions;
import org.opengroup.osdu.indexer.schema.converter.config.SchemaConverterConfig;
import org.opengroup.osdu.indexer.schema.converter.config.SchemaConverterPropertiesConfig;
import org.opengroup.osdu.indexer.schema.converter.exeption.SchemaProcessingException;
import org.opengroup.osdu.indexer.schema.converter.tags.AllOfItem;
import org.opengroup.osdu.indexer.schema.converter.tags.Definition;
import org.opengroup.osdu.indexer.schema.converter.tags.Definitions;
import org.opengroup.osdu.indexer.schema.converter.tags.Items;
import org.opengroup.osdu.indexer.schema.converter.tags.TypeProperty;
public class PropertiesProcessor { public class PropertiesProcessor {
...@@ -185,9 +182,14 @@ public class PropertiesProcessor { ...@@ -185,9 +182,14 @@ public class PropertiesProcessor {
} }
if ("array".equals(entry.getValue().getType())) { if ("array".equals(entry.getValue().getType())) {
Items items = entry.getValue().getItems(); Items items = entry.getValue().getItems();
if(Objects.nonNull(items) && items.isComplexTypeItems()){
if (items == null) {
errors.add(String.format("Invalid array attribute: '%s', missing or null 'items'", entry.getKey()));
return Stream.empty();
}
if (Objects.nonNull(items) && items.isComplexTypeItems()) {
return processComplexTypeItems(entry, items); return processComplexTypeItems(entry, items);
} }
...@@ -215,7 +217,7 @@ public class PropertiesProcessor { ...@@ -215,7 +217,7 @@ public class PropertiesProcessor {
if (Objects.nonNull(entry.getValue().getRef())) { if (Objects.nonNull(entry.getValue().getRef())) {
PropertiesProcessor propertiesProcessor = new PropertiesProcessor(definitions PropertiesProcessor propertiesProcessor = new PropertiesProcessor(definitions
, pathPrefixWithDot + entry.getKey(), new SchemaConverterPropertiesConfig()); , pathPrefixWithDot + entry.getKey(), new SchemaConverterPropertiesConfig());
Stream<Map<String, Object>> refResult = propertiesProcessor.processRef(entry.getValue().getRef()); Stream<Map<String, Object>> refResult = propertiesProcessor.processRef(entry.getValue().getRef());
errors.addAll(propertiesProcessor.getErrors()); errors.addAll(propertiesProcessor.getErrors());
return refResult; return refResult;
} }
...@@ -232,28 +234,28 @@ public class PropertiesProcessor { ...@@ -232,28 +234,28 @@ public class PropertiesProcessor {
Map<String, String> indexHint = entry.getValue().getIndexHint(); Map<String, String> indexHint = entry.getValue().getIndexHint();
String indexingType = Objects.isNull(indexHint) ? String indexingType = Objects.isNull(indexHint) ?
schemaConverterConfig.getDefaultObjectArraysType() : schemaConverterConfig.getDefaultObjectArraysType() :
indexHint.getOrDefault(TYPE_KEY,schemaConverterConfig.getDefaultObjectArraysType()); indexHint.getOrDefault(TYPE_KEY, schemaConverterConfig.getDefaultObjectArraysType());
if(schemaConverterConfig.getProcessedArraysTypes().contains(indexingType)){ if (schemaConverterConfig.getProcessedArraysTypes().contains(indexingType)) {
PropertiesProcessor propertiesProcessor = new PropertiesProcessor(definitions, new SchemaConverterPropertiesConfig()); PropertiesProcessor propertiesProcessor = new PropertiesProcessor(definitions, new SchemaConverterPropertiesConfig());
Stream<Map<String, Object>> propertiesStream = Stream.empty(); Stream<Map<String, Object>> propertiesStream = Stream.empty();
if(Objects.nonNull(items.getProperties())){ if (Objects.nonNull(items.getProperties())) {
propertiesStream = items.getProperties().entrySet().stream().flatMap(propertiesProcessor::processPropertyEntry); propertiesStream = items.getProperties().entrySet().stream().flatMap(propertiesProcessor::processPropertyEntry);
} }
if (Objects.nonNull(items.getRef())){ if (Objects.nonNull(items.getRef())) {
propertiesStream = Stream.concat(propertiesStream, propertiesProcessor.processRef(items.getRef())); propertiesStream = Stream.concat(propertiesStream, propertiesProcessor.processRef(items.getRef()));
} }
if(Objects.nonNull(items.getAllOf())){ if (Objects.nonNull(items.getAllOf())) {
propertiesStream = Stream.concat(propertiesStream, items.getAllOf().stream().flatMap(propertiesProcessor::processItem)); propertiesStream = Stream.concat(propertiesStream, items.getAllOf().stream().flatMap(propertiesProcessor::processItem));
} }
return storageSchemaObjectArrayEntry( return storageSchemaObjectArrayEntry(
indexingType, indexingType,
entry.getKey(), entry.getKey(),
propertiesStream); propertiesStream);
}else { } else {
return storageSchemaEntry(indexingType, pathPrefixWithDot + entry.getKey()); return storageSchemaEntry(indexingType, pathPrefixWithDot + entry.getKey());
} }
} }
...@@ -300,14 +302,14 @@ public class PropertiesProcessor { ...@@ -300,14 +302,14 @@ public class PropertiesProcessor {
return Stream.of(map); return Stream.of(map);
} }
private Stream<Map<String, Object>> storageSchemaObjectArrayEntry(String kind, String path,Stream<Map<String, Object>> mapStream) { private Stream<Map<String, Object>> storageSchemaObjectArrayEntry(String kind, String path, Stream<Map<String, Object>> mapStream) {
Preconditions.checkNotNullOrEmpty(kind, "kind cannot be null or empty"); Preconditions.checkNotNullOrEmpty(kind, "kind cannot be null or empty");
Preconditions.checkNotNullOrEmpty(path, "path cannot be null or empty"); Preconditions.checkNotNullOrEmpty(path, "path cannot be null or empty");
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("kind", kind); map.put("kind", kind);
map.put("path", path); map.put("path", path);
map.put(Constants.PROPERTIES,mapStream.collect(Collectors.toList())); map.put(Constants.PROPERTIES, mapStream.collect(Collectors.toList()));
return Stream.of(map); return Stream.of(map);
} }
......
// Copyright © 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.indexer.error;
import javassist.NotFoundException;
import org.apache.http.HttpStatus;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.opengroup.osdu.core.common.model.http.RequestStatus;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import javax.validation.ValidationException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@RunWith(PowerMockRunner.class)
public class GlobalExceptionMapperCoreTest {
@Mock
private JaxRsDpsLog log;
@InjectMocks
private GlobalExceptionMapperCore sut;
@Test
public void should_useValuesInAppExceptionInResponse_When_AppExceptionIsHandledByGlobalExceptionMapper() {
AppException exception = new AppException(409, "any reason", "any message");
ResponseEntity<Object> response = sut.handleAppException(exception);
assertEquals(409, response.getStatusCodeValue());
assertEquals(exception.getError(), response.getBody());
}
@Test
public void should_use404ValueInResponse_When_NotFoundExceptionIsHandledByGlobalExceptionMapper() {
NotFoundException exception = new NotFoundException("any message");
ResponseEntity<Object> response = sut.handleNotFoundException(exception);
assertEquals(404, response.getStatusCodeValue());
assertTrue(response.getBody().toString().contains("any message"));
}
@Test
public void should_useGenericValuesInResponse_When_ExceptionIsHandledByGlobalExceptionMapper() {
Exception exception = new Exception("any message");
ResponseEntity<Object> response = sut.handleGeneralException(exception);
assertEquals(500, response.getStatusCodeValue());
assertEquals("AppError(code=500, reason=Server error., message=An unknown error has occurred., errors=null, debuggingInfo=null, originalException=java.lang.Exception: any message)", response.getBody().toString());
}
@Test
public void should_useBadRequestInResponse_When_handleValidationExceptionIsHandledByGlobalExceptionMapper() {
ValidationException exception = new ValidationException();
ResponseEntity<Object> response = sut.handleValidationException(exception);
assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCodeValue());
}
@Test
public void should_useBadRequestInResponse_When_handleAccessDeniedExceptionIsHandledByGlobalExceptionMapper() {
AccessDeniedException exception = new AccessDeniedException("Access is denied.");
ResponseEntity<Object> response = sut.handleAccessDeniedException(exception);
assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCodeValue());
}
@Test
public void should_useCustomErrorCodeInResponse_When_AppExceptionIsHandledByGlobalExceptionMapper() {
AppException exception = new AppException(RequestStatus.INVALID_RECORD, "Invalid request", "Successful Storage service response with wrong json");
ResponseEntity<Object> response = sut.handleAppException(exception);
assertEquals(RequestStatus.INVALID_RECORD, response.getStatusCodeValue());
}
}
\ No newline at end of file
...@@ -60,6 +60,11 @@ public class SchemaToStorageFormatImplTest { ...@@ -60,6 +60,11 @@ public class SchemaToStorageFormatImplTest {
testSingleFile("/converter/bad-schema/wrong-definitions-and-missed-type.json", KIND); testSingleFile("/converter/bad-schema/wrong-definitions-and-missed-type.json", KIND);
} }
@Test(expected = SchemaProcessingException.class)
public void wrongArrayDefinitions() {
testSingleFile("/converter/bad-schema/wrong-array.json", KIND);
}
@Test @Test
public void firstSchemaPassed() { public void firstSchemaPassed() {
testSingleFile("/converter/basic/schema.json", "osdu:osdu:Wellbore:1.0.0"); testSingleFile("/converter/basic/schema.json", "osdu:osdu:Wellbore:1.0.0");
......
{
"license": "Copyright 2017-2020, Schlumberger\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "The wellbore schema. Used to capture the general information about a wellbore. This information is sometimes called a \"wellbore header\". A wellbore represents the path from surface to a unique bottomhole location. The wellbore object is uniquely identified within the context of one well object.",
"title": "Wellbore",
"type": "object",
"definitions": {
"legal": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "legal",
"type": "object"
},
"metaItem": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "metaItem",
"type": "object"
},
"tagDictionary": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "tagDictionary",
"type": "object"
},
"linkList": {
"type": "object",
"properties": {
"name": {
"link": "string"
}
}
},
"wellboreData": {
"description": "The domain specific data container for a wellbore.",
"title": "Wellbore Data",
"type": "object",
"properties": {
"WB_NAME": {
"description": "TBD",
"title": "Wellbore Name",
"type": "string",
"example": "SMP G09995 001S0B1"
},
"SPUD_DATE": {
"format": "date",
"description": "The date and time when activities to drill the borehole begin to create a hole in the earth. For a sidetrack, this is the date kickoff operations began. The format follows ISO 8601 YYYY-MM-DD extended format",
"title": "Spud Date",
"type": "string",
"example": "2013-03-22"
},
"TVD": {
"description": "TBD",
"title": "True Vertical Depth",
"type": "string",
"example": [
20711,
"TBD"
]
},
"PERMIT_NUMBER": {
"description": "Ther permit number for the wellbore",
"title": "Permit Number",
"type": "string",
"example": "SMP-09995"
},
"CRS": {
"description": "Wellbore location CRS",
"title": "CRS",
"type": "string",
"example": "World Geodetic System 1984"
},
"LONGUITUDE": {
"description": "TBD",
"title": "Longuitude",
"type": "number",
"example": [
-119.2,
"TBD"
]
},
"STATE": {
"description": "The state, in which the wellbore is located.",
"title": "State",
"type": "string",
"example": [
"Texas"
]
},
"CLASS": {
"description": "The current class of the wellbore",
"title": "class",
"type": "string",
"example": "NEW FIELD WILDCAT"
},
"WELLBORE_SHAPE": {
"description": "The shape of the wellbore",
"title": "Wellbore Shape",
"type": "string",
"example": [
"DIRECTIONAL",
"VERTICAL"
]
},
"FORMATION_AT_TD": {
"description": "The formation name at the wellbore total depth",
"title": "Formation at TD",
"type": "string",
"example": "MIOCENE LOWER"
},
"PERMIT_DATE": {
"format": "date",
"description": "The date and time when the wellbore permit was issued. The format follows ISO 8601 YYYY-MM-DD extended format",
"title": "Permit Date",
"type": "string",
"example": "2013-03-22"
},
"STATUS": {
"description": "The current status of the wellbore",
"title": "Status",
"type": "string",
"example": "DRY & ABANDONED"
},
"COUNTRY": {
"description": "The country, in which the wellbore is located. The country name follows the convention in ISO 3166-1 'English short country name', see https://en.wikipedia.org/wiki/ISO_3166-1",
"title": "Country",
"type": "string",
"example": [
"United States of America"
]
},
"WB_NUMBER": {
"description": "TBD",
"title": "Wellbore Number",
"type": "string",
"example": "001S0B1"
},
"MD": {
"description": "TBD",
"title": "Measured Depth",
"type": "string",
"example": "12.20"
},
"ORIGINAL_OPERATOR": {
"description": "The original operator of the wellbore.",
"title": "Original Operator",
"type": "string",
"example": "Anadarko Petroleum"
},
"BASIN": {
"description": "The basin name, to which the wellbore belongs.",
"title": "Basin",
"type": "string",
"example": "ATWATER"
},
"EPSG_CODE": {
"description": "EPSG code of the CRS",
"title": "EPSG Code",
"type": "string",
"example": "4326"
},
"COUNTY": {
"description": "The county, in which the wellbore is located.",
"title": "County",
"type": "string",
"example": [
"ATWATER VALLEY"
]
},
"UNIT_SYSTEM": {
"description": "Unit system used for the wellbore measurements",
"title": "Unit Sustem",
"type": "string",
"example": "English"
},
"UWI": {
"description": "The unique wellbore identifier, aka. API number, US well number or UBHI. Codes can have 10, 12 or 14 digits depending on the availability of directional sidetrack (2 digits) and event sequence codes (2 digits).",
"title": "Unique Wellbore Identifier",
"type": "string",
"x-osdu-natural-key": 1,
"example": [
"SP435844921288",
"42-501-20130-01-02"
]
},
"FIELD": {
"description": "The field name, to which the wellbore belongs.",
"title": "Field",
"type": "string",
"example": "ATWATER VLLY B 8"
},
"INITIAL_COMPLETION_DATE": {
"format": "date",
"description": "The date and time of the initial completion of the wellbore. The format follows ISO 8601 YYYY-MM-DD extended format",
"title": "Initial Completion Date",
"type": "string",
"example": "2013-03-22"
},
"ELEVATION": {
"description": "TBD",
"title": "Elevation",
"type": "string",
"example": [
84,
"TBD"
]
},
"STATUS_DATE": {
"format": "date",
"description": "The date and time of the current status of the wellbore. The format follows ISO 8601 YYYY-MM-DD extended format",
"title": "Status Date",
"type": "string",
"example": "2013-03-22"
},
"OPERATOR": {
"description": "The operator of the wellbore.",
"title": "Operator",
"type": "string",
"example": "Anadarko Petroleum"
},
"LEASE": {
"description": "The lease name, to which the wellbore belongs.",
"title": "LEASE",
"type": "string",
"example": "SMP G09995"
},
"API": {
"description": "Second parameter used for relationship tests",
"title": "Api relationship test",
"type": "string",
"example": "test-wellbore-api"
},
"LATITUDE": {
"description": "TBD",
"title": "Latitude",
"type": "number",
"example": [
60.2,
"TBD"
]
},
"ELEVATION_REF": {
"description": "Elevation reference used for the measurements",
"title": "Elevation reference",
"type": "string",
"example": "MSL"
},
"Wellbores": {
"pattern": ".*U1A1NjA3MDM2Mzk5MzUy:.*",
"description": "The Well ID reference.",
"x-osdu-relationship": [
{
"EntityType": "Wellbore",
"GroupType": "master-data"
}
],
"type": "array"
}
},
"$id": "definitions/wellboreData"
}
},
"properties": {
"ancestry": {
"description": "The links to data, which constitute the inputs.",
"title": "Ancestry",
"$ref": "#/definitions/linkList"
},
"data": {
"description": "Wellbore data container",
"title": "Wellbore Data",
"$ref": "#/definitions/wellboreData"
},
"kind": {
"description": "OSDU demo wellbore kind specification",
"title": "Wellbore Kind",
"type": "string"
},
"meta": {
"description": "The meta data section linking the 'unitKey', 'crsKey' to self-contained definitions (persistableReference)",
"title": "Frame of Reference Meta Data",
"type": "array",
"items": {
"$ref": "#/definitions/metaItem"
}
},
"legal": {
"description": "The geological interpretation's legal tags",
"title": "Legal Tags",
"$ref": "#/definitions/legal"
},
"acl": {
"description": "The access control tags associated with this entity.",
"title": "Access Control List",
"$ref": "#/definitions/tagDictionary"
},
"id": {
"description": "The unique identifier of the wellbore",
"title": "Wellbore ID",
"type": "string"
},
"type": {
"description": "The reference entity type as declared in common:metadata:entity:*.",
"title": "Entity Type",
"type": "string"
},
"version": {
"format": "int64",
"description": "The version number of this wellbore; set by the framework.",
"title": "Entity Version Number",
"type": "number",
"example": "1040815391631285"
}
}
}
\ No newline at end of file
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