From b2d02f10de33e0218b095e0497fe97321c0115d8 Mon Sep 17 00:00:00 2001 From: Mohd Kaif Siddique Date: Tue, 3 Dec 2024 13:13:00 +0530 Subject: [PATCH] review changes and modified testcases Signed-off-by: Mohd Kaif Siddique --- .../controller/IdentityController.java | 33 ++- .../validator/IdentitySchemaValidator.java | 44 ++- .../resources/application-default.properties | 4 +- .../resources/application-local.properties | 6 +- .../controller/IdentityControllerTest.java | 49 +++- .../resources/application-test.properties | 3 + .../src/test/resources/bootstrap.properties | 2 + .../resources/mock-identity-test-schema.json | 267 ++++++++++++++++++ 8 files changed, 379 insertions(+), 29 deletions(-) create mode 100644 mock-identity-system/src/test/resources/application-test.properties create mode 100644 mock-identity-system/src/test/resources/bootstrap.properties create mode 100644 mock-identity-system/src/test/resources/mock-identity-test-schema.json diff --git a/mock-identity-system/src/main/java/io/mosip/esignet/mock/identitysystem/controller/IdentityController.java b/mock-identity-system/src/main/java/io/mosip/esignet/mock/identitysystem/controller/IdentityController.java index 6396bd60..aefb57dd 100644 --- a/mock-identity-system/src/main/java/io/mosip/esignet/mock/identitysystem/controller/IdentityController.java +++ b/mock-identity-system/src/main/java/io/mosip/esignet/mock/identitysystem/controller/IdentityController.java @@ -5,12 +5,18 @@ */ package io.mosip.esignet.mock.identitysystem.controller; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; import javax.validation.Valid; import io.mosip.esignet.mock.identitysystem.dto.*; +import io.mosip.esignet.mock.identitysystem.dto.Error; import io.mosip.esignet.mock.identitysystem.validator.IdentitySchema; +import io.mosip.kernel.core.exception.ErrorResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -18,7 +24,9 @@ import io.mosip.esignet.mock.identitysystem.service.IdentityService; import io.mosip.esignet.mock.identitysystem.util.HelperUtil; +import java.util.ArrayList; import java.util.List; +import java.util.Set; @RestController @@ -32,7 +40,7 @@ public class IdentityController { @PostMapping(value = "identity", consumes = { MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE }) public ResponseWrapper createIdentity - (@Valid @RequestBody @IdentitySchema(isCreate=true) RequestWrapper< IdentityData> requestWrapper) throws MockIdentityException { + (@RequestBody @IdentitySchema(isCreate=true) RequestWrapper< IdentityData> requestWrapper) throws MockIdentityException { ResponseWrapper response = new ResponseWrapper(); IdentityStatus identityStatus = new IdentityStatus(); @@ -46,7 +54,7 @@ public class IdentityController { @PutMapping(value = "identity", consumes = { MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE }) public ResponseWrapper updateIdentity - (@Valid @RequestBody @IdentitySchema(isCreate=false) RequestWrapper requestWrapper) throws MockIdentityException { + (@RequestBody @IdentitySchema(isCreate=false) RequestWrapper requestWrapper) throws MockIdentityException { ResponseWrapper response = new ResponseWrapper(); IdentityStatus identityStatus = new IdentityStatus(); @@ -77,4 +85,25 @@ public ResponseWrapper createVerifiedClaim(@Valid @RequestB return response; } + + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity handleMethodArgumentNotValidException(ConstraintViolationException ex) { + List errors = new ArrayList<>(); + if(ex != null) { + Set> violations = ((ConstraintViolationException) ex).getConstraintViolations(); + for(ConstraintViolation cv : violations) { + errors.add(new Error(cv.getMessage(), cv.getPropertyPath().toString() + ": " + cv.getMessage())); + } + return new ResponseEntity(getResponseWrapper(errors), HttpStatus.OK); + } + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(errors); + } + + private ResponseWrapper getResponseWrapper(List errors) { + ResponseWrapper responseWrapper = new ResponseWrapper<>(); + responseWrapper.setResponseTime(HelperUtil.getCurrentUTCDateTime()); + responseWrapper.setErrors(errors); + return responseWrapper; + } } diff --git a/mock-identity-system/src/main/java/io/mosip/esignet/mock/identitysystem/validator/IdentitySchemaValidator.java b/mock-identity-system/src/main/java/io/mosip/esignet/mock/identitysystem/validator/IdentitySchemaValidator.java index c0aee892..62c6c632 100644 --- a/mock-identity-system/src/main/java/io/mosip/esignet/mock/identitysystem/validator/IdentitySchemaValidator.java +++ b/mock-identity-system/src/main/java/io/mosip/esignet/mock/identitysystem/validator/IdentitySchemaValidator.java @@ -11,6 +11,8 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.io.IOException; @@ -21,14 +23,14 @@ @Slf4j public class IdentitySchemaValidator implements ConstraintValidator { - @Value("${mosip.mock.ida.identity.create.schema.url}") - private String createSchemaUrl; + @Value("${mosip.mock.ida.identity.schema.url}") + private String identitySchemaUrl; - @Value("#{${mosip.mock.ida.identity.update.exempted.field}}") - private Set exemptedField; + @Value("#{${mosip.mock.ida.update-identity.non-mandatory.fields}}") + private Set nonMandatoryFieldsOnUpdate; private boolean isCreate; - private volatile JsonSchema cachedSchema; + private volatile JsonSchema schema; @Autowired ObjectMapper objectMapper; @@ -36,6 +38,7 @@ public class IdentitySchemaValidator implements ConstraintValidator errors = getCachedSchema().validate(identityJsonNode); + Set errors = getSchema().validate(identityJsonNode); boolean isValid=true; + String errorName=""; if(!isCreate){ for(ValidationMessage validationMessage: errors){ String field=validationMessage. getInstanceLocation().getName(0); // Ignore validation errors with code 1029 (null value) for exempted fields when validating updateIdentity - if(!validationMessage.getCode().equals("1029") || !exemptedField.contains(field)){ + if(!validationMessage.getCode().equals("1029") || !nonMandatoryFieldsOnUpdate.contains(field)){ + errorName="invalid_"+field.toLowerCase(); isValid=false; break; } } + }else if(!errors.isEmpty()){ + isValid=false; } if (!isValid) { + context.disableDefaultConstraintViolation(); + if(StringUtils.isEmpty(errorName)) + errorName="invalid_"+errors.iterator().next().getInstanceLocation().getName(0).toLowerCase(); + context.buildConstraintViolationWithTemplate(errorName) + .addConstraintViolation(); log.error("Validation failed for IdentityData: {}", errors); return false; } return true; } - - private JsonSchema getCachedSchema() { - if(cachedSchema !=null ) return cachedSchema; + private JsonSchema getSchema() { + if(schema !=null ) return schema; synchronized (this) { - if (cachedSchema == null) { - InputStream schemaResponse = getResource(createSchemaUrl); + if (schema == null) { + InputStream schemaResponse = getResource(identitySchemaUrl); JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012); - cachedSchema = jsonSchemaFactory.getSchema(schemaResponse); + schema = jsonSchemaFactory.getSchema(schemaResponse); } } - return cachedSchema; + return schema; } private InputStream getResource(String url) { diff --git a/mock-identity-system/src/main/resources/application-default.properties b/mock-identity-system/src/main/resources/application-default.properties index 8f0975df..880c5e75 100644 --- a/mock-identity-system/src/main/resources/application-default.properties +++ b/mock-identity-system/src/main/resources/application-default.properties @@ -27,8 +27,8 @@ ##-----------------------------------------Mock-identity-system properties---------------------------------------------- -mosip.mock.ida.identity.create.schema.url=classpath:/mock-identity-schema.json -mosip.mock.ida.identity.update.exempted.field={"fullName","name","givenName","familyName","middleName","nickName","preferredUsername","gender","streetAddress","locality","region","country","pin","preferredLang","dateOfBirth","postalCode","encodedPhoto","email","phone","zoneInfo","locale","password"} +mosip.mock.ida.identity.schema.url=classpath:/mock-identity-schema.json +mosip.mock.ida.update-identity.non-mandatory.fields={"fullName","name","givenName","familyName","middleName","nickName","preferredUsername","gender","streetAddress","locality","region","country","pin","preferredLang","dateOfBirth","postalCode","encodedPhoto","email","phone","zoneInfo","locale","password"} ##----------------------------------------- Database properties -------------------------------------------------------- mosip.mockidentitysystem.database.hostname=${database.host} diff --git a/mock-identity-system/src/main/resources/application-local.properties b/mock-identity-system/src/main/resources/application-local.properties index 38d357dc..a450017a 100644 --- a/mock-identity-system/src/main/resources/application-local.properties +++ b/mock-identity-system/src/main/resources/application-local.properties @@ -1,12 +1,12 @@ ##-----------------------------------------Mock-identity-system properties---------------------------------------------- -mosip.mock.ida.identity.create.schema.url=classpath:/mock-identity-schema.json -mosip.mock.ida.identity.update.exempted.field={"fullName","name","givenName","familyName","middleName","nickName","preferredUsername","gender","streetAddress","locality","region","country","pin","preferredLang","dateOfBirth","postalCode","encodedPhoto","email","phone","zoneInfo","locale","password"} +mosip.mock.ida.identity.schema.url=classpath:/mock-identity-schema.json +mosip.mock.ida.update-identity.non-mandatory.fields={"fullName","name","givenName","familyName","middleName","nickName","preferredUsername","gender","streetAddress","locality","region","country","pin","preferredLang","dateOfBirth","postalCode","encodedPhoto","email","phone","zoneInfo","locale","password"} ##----------------------------------------- Database properties -------------------------------------------------------- -spring.datasource.url=jdbc:postgresql://localhost:5455/mosip_mockidentitysystem?currentSchema=mockidentitysystem +spring.datasource.url=jdbc:postgresql://localhost:5432/mosip_mockidentitysystem?currentSchema=mockidentitysystem spring.datasource.username=postgres spring.datasource.password=postgres spring.datasource.driver-class-name=org.postgresql.Driver diff --git a/mock-identity-system/src/test/java/io/mosip/esignet/mock/identitysystem/controller/IdentityControllerTest.java b/mock-identity-system/src/test/java/io/mosip/esignet/mock/identitysystem/controller/IdentityControllerTest.java index 08c32aaa..00a46c6a 100644 --- a/mock-identity-system/src/test/java/io/mosip/esignet/mock/identitysystem/controller/IdentityControllerTest.java +++ b/mock-identity-system/src/test/java/io/mosip/esignet/mock/identitysystem/controller/IdentityControllerTest.java @@ -30,7 +30,6 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.mosip.esignet.mock.identitysystem.service.IdentityService; @@ -113,7 +112,6 @@ public void createIdentity_withValidIdentity_returnSuccessResponse() throws Exce requestWrapper.setRequest(identityRequest); Mockito.doNothing().when(identityService).addIdentity(identityRequest); - mockMvc.perform(post("/identity").content(objectMapper.writeValueAsString(requestWrapper)) .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) .andExpect(jsonPath("$.response.status").value("mock identity data created successfully")); @@ -128,18 +126,41 @@ public void createIdentity_withInvalidIdentity_returnErrorResponse() throws Exce requestWrapper.setRequest(identityRequest); Mockito.doNothing().when(identityService).addIdentity(identityRequest); - mockMvc.perform(post("/identity").content(objectMapper.writeValueAsString(requestWrapper)) .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) .andExpect(jsonPath("$.errors").isNotEmpty()) - .andExpect(jsonPath("$.errors[0].errorCode").value(ErrorConstants.INVALID_INDIVIDUAL_ID)); + .andExpect(jsonPath("$.errors[0].errorCode").value("invalid_individualid")); + } + + @Test + public void createIdentity_withInvalidFullName_returnErrorResponse() throws Exception { + RequestWrapper requestWrapper = new RequestWrapper(); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + requestWrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); + + List nameList=new ArrayList<>(); + LanguageValue engLangValue= new LanguageValue(); + engLangValue.setValue("Siddharth K سيدارت"); + engLangValue.setLanguage("eng"); + LanguageValue arabicLangValue= new LanguageValue(); + arabicLangValue.setLanguage("ara"); + arabicLangValue.setValue("سيدارت ك منصور"); + nameList.add(engLangValue); + nameList.add(arabicLangValue); + identityRequest.setFullName(nameList); + requestWrapper.setRequest(identityRequest); + + Mockito.doNothing().when(identityService).addIdentity(identityRequest); + mockMvc.perform(post("/identity").content(objectMapper.writeValueAsString(requestWrapper)) + .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorCode").value("invalid_fullname")); } @Test public void getIdentity_withValidId_returnSuccessResponse() throws Exception { identityRequest.setIndividualId("123456789"); Mockito.when(identityService.getIdentity(Mockito.anyString())).thenReturn(identityRequest); - mockMvc.perform(get("/identity/{individualId}", "123456789") .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) .andExpect(jsonPath("$.response.individualId").value("123456789")); @@ -163,7 +184,6 @@ public void addVerifiedClaims_withValidDetails_returnSuccessResponse() throws Ex verifiedClaimRequestDto.setVerificationDetail(verificationDetail); requestWrapper.setRequest(verifiedClaimRequestDto); - Mockito.doNothing().when(identityService).addVerifiedClaim(verifiedClaimRequestDto); Mockito.when(identityService.getIdentity(Mockito.anyString())).thenReturn(identityRequest); @@ -185,7 +205,6 @@ public void addVerifiedClaim_withInvalidClaim_returnErrorResponse() throws Exce verifiedClaimRequestDto.setVerificationDetail(verificationDetail); requestWrapper.setRequest(verifiedClaimRequestDto); - Mockito.doNothing().when(identityService).addVerifiedClaim(verifiedClaimRequestDto); mockMvc.perform(post("/identity/add-verified-claim").content(objectMapper.writeValueAsString(requestWrapper)) @@ -202,10 +221,24 @@ public void updateIdentity_withValidIdentity_thenPass() throws Exception { requestWrapper.setRequest(identityRequest); Mockito.doNothing().when(identityService).updateIdentity(identityRequest); - mockMvc.perform(put("/identity").content(objectMapper.writeValueAsString(requestWrapper)) .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) .andExpect(jsonPath("$.response.status").value("mock Identity data updated successfully")); } + @Test + public void updateIdentity_withInValidIdentity_thenFail() throws Exception { + RequestWrapper requestWrapper = new RequestWrapper(); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + requestWrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); + identityRequest.setFullName(null); + requestWrapper.setRequest(identityRequest); + + Mockito.doNothing().when(identityService).updateIdentity(identityRequest); + mockMvc.perform(put("/identity").content(objectMapper.writeValueAsString(requestWrapper)) + .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorCode").value("invalid_fullname")); + } + } diff --git a/mock-identity-system/src/test/resources/application-test.properties b/mock-identity-system/src/test/resources/application-test.properties new file mode 100644 index 00000000..f968ad02 --- /dev/null +++ b/mock-identity-system/src/test/resources/application-test.properties @@ -0,0 +1,3 @@ + +mosip.mock.ida.identity.schema.url=classpath:/mock-identity-test-schema.json +mosip.mock.ida.update-identity.non-mandatory.fields={"name","givenName","familyName","middleName","nickName","preferredUsername","gender","streetAddress","locality","region","country","pin","preferredLang","dateOfBirth","postalCode","encodedPhoto","email","phone","zoneInfo","locale","password"} diff --git a/mock-identity-system/src/test/resources/bootstrap.properties b/mock-identity-system/src/test/resources/bootstrap.properties new file mode 100644 index 00000000..b78f2069 --- /dev/null +++ b/mock-identity-system/src/test/resources/bootstrap.properties @@ -0,0 +1,2 @@ + +spring.profiles.active=test \ No newline at end of file diff --git a/mock-identity-system/src/test/resources/mock-identity-test-schema.json b/mock-identity-system/src/test/resources/mock-identity-test-schema.json new file mode 100644 index 00000000..bebd989b --- /dev/null +++ b/mock-identity-system/src/test/resources/mock-identity-test-schema.json @@ -0,0 +1,267 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "$defs": { + "langField": { + "type": "array", + "items": { + "type": "object", + "properties": { + "language": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "language", + "value" + ], + "additionalProperties": false + } + } + }, + "properties": { + "individualId": { + "type": "string", + "pattern": "^[0-9]{5,19}$" + }, + "fullName": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ\\s]{1,40}|[ء-ي\\s-٩ٱ-ڿﹰ-\uFEFF\u0600-ۿ]{1,40})$" + } + } + } + } + ] + }, + "name": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "nullable": true + }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ\\s]{1,40}|[ء-ي\\s-٩ٱ-ڿﹰ-\uFEFF\u0600-ۿ]{1,40})$" + + } + } + } + } + ] + }, + "givenName": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ\\s]{1,40}|[ء-ي\\s-٩ٱ-ڿﹰ-\uFEFF\u0600-ۿ]{1,40})$" + } + } + } + } + ] + }, + "familyName": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ\\s]{1,40}|[ء-ي\\s-٩ٱ-ڿﹰ-\uFEFF\u0600-ۿ]{1,40})$" + } + } + } + } + ] + }, + "middleName": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ\\s]{1,40}|[ء-ي\\s-٩ٱ-ڿﹰ-\uFEFF\u0600-ۿ]{1,40})$" + } + } + } + } + ] + }, + "nickName": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ\\s]{1,40}|[ء-ي\\s-٩ٱ-ڿﹰ-\uFEFF\u0600-ۿ]{1,40})$" + } + } + } + } + ] + }, + "preferredUsername": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ\\s]{1,40}|[ء-ي\\s-٩ٱ-ڿﹰ-\uFEFF\u0600-ۿ]{1,40})$" + } + } + } + } + ] + }, + "gender": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ\\s]{1,20}|^[ء-ي\\s\\u0621-\\u064A\\u0660-\\u0669\\u0671-\\u06BF\\uFE70-\\uFEFF\\u0600-\\u06FFگچپژیلفقهمو ء-ي]{1,20}$)$" + } + } + } + } + ] + }, + "streetAddress": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ0-9\\s.,°№-]{1,200}|[ء-ي\\s-٩ٱ-ڿﹰ-\uFEFF\u0600-ۿ0-9]{1,200})$" + + } + } + } + } + ] + }, + "locality": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ0-9\\s.,°№-]{1,200}|[ء-ي\\s-٩ٱ-ڿﹰ-\uFEFF\u0600-ۿ0-9]{1,200})$" + } + } + } + } + ] + }, + "region": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ0-9\\s.,°№-]{1,200}|[ء-ي\\s-٩ٱ-ڿﹰ-\uFEFF\u0600-ۿ0-9]{1,200})$" + } + } + } + } + ] + }, + "country": { + "allOf": [ + { "$ref": "#/$defs/langField" }, + { + "items": { + "properties": { + "value": { + "pattern": "^(?:[a-zA-ZÀ-ÿ\\s]{1,20}|^[ء-ي\\s\\u0621-\\u064A\\u0660-\\u0669\\u0671-\\u06BF\\uFE70-\\uFEFF\\u0600-\\u06FFگچپژیلفقهمو ء-ي]{1,20}$)$" + } + } + } + } + ] + }, + "pin": { + "type": "string", + "pattern": "\\S" + }, + "preferredLang": { + "type": "string" + }, + "dateOfBirth": { + "type": "string", + "pattern": "\\S" + }, + "postalCode": { + "type": "string", + "pattern": "\\S" + }, + "encodedPhoto": { + "type": "string" + }, + "email": { + "type": "string", + "pattern": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$" + }, + "phone": { + "type": "string", + "pattern": "\\S" + }, + "zoneInfo": { + "type": "string" + }, + "locale": { + "type": "string", + "pattern": "\\S" + }, + "password": { + "type": "string", + "pattern": "\\S" + } + }, + "required": [ + "individualId", + "fullName", + "name", + "givenName", + "familyName", + "middleName", + "nickName", + "preferredUsername", + "gender", + "streetAddress", + "locality", + "region", + "country", + "pin", + "preferredLang", + "dateOfBirth", + "postalCode", + "encodedPhoto", + "email", + "phone", + "zoneInfo", + "locale", + "password" + ], + "additionalProperties": false +} \ No newline at end of file