diff --git a/Makefile b/Makefile index ef8bc79fe..ef25466de 100644 --- a/Makefile +++ b/Makefile @@ -28,10 +28,11 @@ java/registry/target/registry.jar: $(SOURCES) cd java && ./mvnw clean install test: build + @echo "VIEW_DIR=java/apitest/src/test/resources/views" >> .env || echo "no permission to append to file" @docker-compose down @rm -rf db-data* || echo "no permission to delete" - # test with ES & standard definition manager - @RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 DB_DIR=db-data-1 docker-compose up -d db es keycloak registry certificate-signer certificate-api + # test with distributed definition manager and native search + @SEARCH_PROVIDER_NAME=dev.sunbirdrc.registry.service.NativeSearchService RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 MANAGER_TYPE=DistributedDefinitionsManager DB_DIR=db-data-1 docker-compose up -d db keycloak registry certificate-signer certificate-api redis @echo "Starting the test" && sh build/wait_for_port.sh 8080 @echo "Starting the test" && sh build/wait_for_port.sh 8081 @docker-compose ps @@ -39,49 +40,15 @@ test: build @cd java/apitest && ../mvnw -Pe2e test || echo 'Tests failed' @docker-compose down @rm -rf db-data-1 || echo "no permission to delete" - # test with distributed definition manager - @RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 MANAGER_TYPE=DistributedDefinitionsManager DB_DIR=db-data-2 docker-compose up -d db es keycloak registry certificate-signer certificate-api redis - @echo "Starting the test" && sh build/wait_for_port.sh 8080 - @echo "Starting the test" && sh build/wait_for_port.sh 8081 - @docker-compose ps - @curl -v http://localhost:8081/health - @cd java/apitest && ../mvnw -Pe2e test || echo 'Tests failed' - @docker-compose down - @rm -rf db-data-2 || echo "no permission to delete" - # test with native search service - @RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 DB_DIR=db-data-3 SEARCH_PROVIDER_NAME=dev.sunbirdrc.registry.service.NativeSearchService docker-compose up -d db keycloak registry certificate-signer certificate-api - @echo "Starting the test" && sh build/wait_for_port.sh 8080 - @echo "Starting the test" && sh build/wait_for_port.sh 8081 - @docker-compose ps - @curl -v http://localhost:8081/health - @cd java/apitest && ../mvnw -Pe2e test || echo 'Tests failed' - @docker-compose down - @rm -rf db-data-3 || echo "no permission to delete" - # test with kafka(async) - @ASYNC_ENABLED=true RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 DB_DIR=db-data-4 docker-compose up -d db es keycloak registry certificate-signer certificate-api kafka zookeeper + # test with kafka(async), events, notifications, + @NOTIFICATION_ENABLED=true NOTIFICATION_URL=http://notification-ms:8765/notification-service/v1/notification TRACK_NOTIFICATIONS=true EVENT_ENABLED=true ASYNC_ENABLED=true RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 DB_DIR=db-data-2 docker-compose up -d db es keycloak registry certificate-signer certificate-api kafka zookeeper notification-ms metrics @echo "Starting the test" && sh build/wait_for_port.sh 8080 @echo "Starting the test" && sh build/wait_for_port.sh 8081 @docker-compose ps @curl -v http://localhost:8081/health @cd java/apitest && MODE=async ../mvnw -Pe2e test || echo 'Tests failed' @docker-compose down - @rm -rf db-data-4 || echo "no permission to delete" - @RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources EVENT_ENABLED=true KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 DB_DIR=db-data-5 SEARCH_PROVIDER_NAME=dev.sunbirdrc.registry.service.NativeSearchService docker-compose up -d db keycloak kafka zookeeper keycloak clickhouse registry certificate-signer certificate-api metrics redis - @echo "Starting the test" && sh build/wait_for_port.sh 8080 - @echo "Starting the test" && sh build/wait_for_port.sh 8081 - @docker-compose ps - @curl -v http://localhost:8081/health - @cd java/apitest && MODE=events ../mvnw -Pe2e test || echo 'Tests failed' - @docker-compose down - @rm -rf db-data-5 || echo "no permission to delete" - @NOTIFICATION_URL=http://notification-ms:8765/notification-service/v1/notification TRACK_NOTIFICATIONS=true KAFKA_BOOTSTRAP_SERVERS=kafka:9092 NOTIFICATION_ENABLED=true NOTIFICATION_ASYNC_ENABLED=false RELEASE_VERSION=latest KEYCLOAK_IMPORT_DIR=java/apitest/src/test/resources KEYCLOAK_SECRET=a52c5f4a-89fd-40b9-aea2-3f711f14c889 DB_DIR=db-data-6 docker-compose up -d db es keycloak registry certificate-signer certificate-api notification-ms kafka - @echo "Starting the test" && sh build/wait_for_port.sh 8080 - @echo "Starting the test" && sh build/wait_for_port.sh 8081 - @docker-compose ps - @curl -v http://localhost:8081/health - @cd java/apitest && MODE=notification ../mvnw -Pe2e test || echo 'Tests failed' - @docker-compose down - @rm -rf db-data-6 || echo "no permission to delete" + @rm -rf db-data-2 || echo "no permission to delete" make -C services/certificate-signer test make -C services/public-key-service test make -C services/context-proxy-service test diff --git a/deps/keycloak/Dockerfile b/deps/keycloak/Dockerfile index 8afca8c14..a0e91a3d4 100644 --- a/deps/keycloak/Dockerfile +++ b/deps/keycloak/Dockerfile @@ -1,6 +1,6 @@ -FROM jboss/keycloak:14.0.0 +FROM sunbirdrc/keycloak ADD themes /opt/jboss/keycloak/themes ADD providers /opt/jboss/keycloak/providers -ENTRYPOINT ["/opt/jboss/tools/docker-entrypoint.sh"] \ No newline at end of file +ENTRYPOINT ["/opt/jboss/tools/docker-entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml index 865759bae..8b49b3986 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,7 +34,8 @@ services: registry: image: dockerhub/sunbird-rc-core:${RELEASE_VERSION} volumes: - - ${PWD}/${SCHEMA_DIR-java/registry/src/main/resources/public/_schemas}:/home/sunbirdrc/config/public/_schemas + - ./${SCHEMA_DIR-java/registry/src/main/resources/public/_schemas}:/home/sunbirdrc/config/public/_schemas + - ./${VIEW_DIR-java/registry/src/main/resources/views}:/home/sunbirdrc/config/views/ environment: - connectionInfo_uri=jdbc:postgresql://db:5432/registry - connectionInfo_username=postgres @@ -96,7 +97,7 @@ services: keycloak: image: dockerhub/sunbird-rc-keycloak:latest volumes: - - ${PWD}/${KEYCLOAK_IMPORT_DIR-imports}:/opt/jboss/keycloak/imports + - ./${KEYCLOAK_IMPORT_DIR-imports}:/opt/jboss/keycloak/imports environment: - KEYCLOAK_LOGO=https://svgshare.com/i/hCs.svg - DB_VENDOR=postgres @@ -291,7 +292,7 @@ services: digilocker-certificate-api: image: dockerhub/sunbird-rc-digilocker-certificate-api volumes: - - ${PWD}/services/digilocker-certificate-api/config/:/go/config/ + - ./services/digilocker-certificate-api/config/:/go/config/ ports: - "8087:8087" environment: diff --git a/java/apitest/src/test/java/e2e/registry/registry.feature b/java/apitest/src/test/java/e2e/registry/registry.feature index 6d8f0564b..7de2d9d78 100644 --- a/java/apitest/src/test/java/e2e/registry/registry.feature +++ b/java/apitest/src/test/java/e2e/registry/registry.feature @@ -345,137 +345,224 @@ Feature: Registry api tests And assert response[0].address[0].phoneNo.length == 1 And assert response[0].address[0].phoneNo[0] == "444" - Scenario: write a api test, to test the schema not found error - # make keycloak authentication enabled:false in file application.yml - # make serach providerName: ${search_providerName:dev.sunbirdrc.registry.service.ElasticSearchService} - in file application.yml - * url baseUrl(http://localhost:8081) - - # invite schema which is unavailable/not found - Given url baseUrl - And path 'api/v1/{entityName}/invite' - method post - Body.raw.json - schema data in json format - Then status 404 - And response.params.status == "UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # delete unavailable schema with id - Given url baseUrl - And path '/api/v1/{entityName}/{entityId}' - When method delete - Then status 404 - response.status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # serach unavailable schema - Given url baseUrl - And path '/api/v1/{entityName}/search' - And request { "filters": { "osid": {"eq":"1-ca9f97a9-ecea-406e-ba2c-b38d1c8d8aa8" } } } - method post - Then status 404 - response.status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # update unavailable schema with id - Given url baseUrl - And path '/api/v1/{entityName}/{entityId}' - Body.raw.json - schema data in json format - method put - Then status 404 - response.status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # post unavailable schema with name - Given url baseUrl - And path '/api/v1/{entityName}' - Body.raw.json - schema data in json format - method post - Then status 404 - status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # update unavailable schema - Given url baseUrl - And path '/api/v1/{entityName}/{entityId}/**' - Body.raw.json - schema data in json format - method put - Then status 404 - status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # update unavailable schema - Given url baseUrl - And path '/api/v1/{entityName}/{entityId}/**' - Body.raw.json - schema data in json format - method post - Then status 404 - status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # get unavailable schema - Given url baseUrl - And path '/partner/api/v1/{entityName}' - method get - Then status 404 - status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # get unavailable schema with id and - Given url baseUrl - And path '/api/v1/{entityName}/{entityId}' - method get - header Content-Type - application/pdf or application/json - Then status 404 - status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # get unavailable schema with id - Given url baseUrl - nd path '/api/v1/{entityName}/{entityId}' - method get - Then status 404 - status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # get unavailable schema with name - Given url baseUrl - And path '/api/v1/{entityName}' - method get - Then status 404 - status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # patch unavailable schema with name - Given url baseUrl - And path '/api/v1/{entityName}/{entityId}' - method patch - Body.raw.json - schema data in json format - Then status 404 - status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - # patch unavailable schema with sign - Given url baseUrl - And path '/api/v1/{entityName}/sign' - method patch - Then status 404 - status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found - - # get unavailable schema with attestations - Given url baseUrl - And path '/api/v1/{entityName}/{entityId}/attestation/{attestationName}/{attestationId}' - header Accept - application/json, Content-Type - application/json - method get - Then status 404 - status =="UNSUCCESSFUL" - And response.params.errmsg == "Schema '%s' not found" - - - - - + Scenario: write a api test, to test the schema not found error + # get admin token + * url authUrl + * path 'auth/realms/sunbird-rc/protocol/openid-connect/token' + * header Content-Type = 'application/x-www-form-urlencoded; charset=utf-8' + * header Host = 'keycloak:8080' + * form field grant_type = 'client_credentials' + * form field client_id = 'admin-api' + * form field client_secret = client_secret + * method post + Then status 200 + And print response.access_token + * def admin_token = 'Bearer ' + response.access_token + # invite schema which is unavailable/not found + Given url registryUrl + And path 'api/v1/Teacher1/invite' + And header Authorization = admin_token + And request read('TeacherRequest.json') + When method post + Then status 404 + And print response + And response.params.status == "UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # delete unavailable schema with id + Given url registryUrl + And path '/api/v1/Teacher1/123' + And header Authorization = admin_token + And request read('TeacherRequest.json') + When method delete + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # search unavailable schema + Given url registryUrl + And path '/api/v1/Teacher1/search' + And header Authorization = admin_token + And request { "filters": { } } + When method post + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # update unavailable schema with id + Given url registryUrl + And path '/api/v1/Teacher1/{entityId}' + And header Authorization = admin_token + And request read('TeacherRequest.json') + When method put + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # post unavailable schema with name + Given url registryUrl + And path '/api/v1/{entityName}' + And header Authorization = admin_token + And request read('TeacherRequest.json') + When method post + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # update unavailable schema + Given url registryUrl + And path '/api/v1/Teacher1/123/contact/456' + And header Authorization = admin_token + And request read('TeacherRequest.json') + When method put + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # update unavailable schema + Given url registryUrl + And path '/api/v1/Teacher1/123/name/' + And header Authorization = admin_token + And request read('TeacherRequest.json') + When method post + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # get unavailable schema + Given url registryUrl + And path '/partner/api/v1/Teacher1' + And header Authorization = admin_token + When method get + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # get unavailable schema with id and + Given url registryUrl + And path '/api/v1/Teacher1/123' + And header Authorization = admin_token + When method get + And request read('TeacherRequest.json') + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # get unavailable schema with id + Given url registryUrl + And path '/api/v1/Teacher1/{entityId}' + And header Authorization = admin_token + When method get + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # get unavailable schema with name + Given url registryUrl + And path '/api/v1/Teacher1' + And header Authorization = admin_token + When method get + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # patch unavailable schema with name + Given url registryUrl + And path '/api/v1/{entityName}/{entityId}' + And header Authorization = admin_token + And request read('TeacherRequest.json') + When method patch + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # patch unavailable schema with sign + Given url registryUrl + And path '/api/v1/Teacher/sign' + And header Authorization = admin_token + And request read('TeacherRequest.json') + When method patch + Then status 404 + Then response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + # get unavailable schema with attestations + Given url registryUrl + And path '/api/v1/Teacher1/123/attestation/teacherAttest/456' + And header Accept = "application/json" + And header Content-Type = "application/json" + And header Authorization = admin_token + When method get + Then status 404 + And response.params.status =="UNSUCCESSFUL" + And response.params.errmsg == "Schema 'Teacher1' not found" + Scenario: Create student with password schema and verify if password is set + # get admin token + * url authUrl + * path 'auth/realms/sunbird-rc/protocol/openid-connect/token' + * header Content-Type = 'application/x-www-form-urlencoded; charset=utf-8' + * header Host = 'keycloak:8080' + * form field grant_type = 'client_credentials' + * form field client_id = 'admin-api' + * form field client_secret = client_secret + * method post + Then status 200 + And print response.access_token + * def admin_token = 'Bearer ' + response.access_token +# create student schema + Given url registryUrl + And path 'api/v1/Schema' + And header Authorization = admin_token + And request read('StudentWithPasswordSchemaRequest.json') + When method post + Then status 200 + And response.params.status == "SUCCESSFUL" + # invite entity for student + Given url registryUrl + And path 'api/v1/StudentWithPassword/invite' + * def studentRequest = read('StudentWithPasswordRequest.json') + And request studentRequest + When method post + Then status 200 + * def studentOsid = response.result.StudentWithPassword.osid + # get student token + * url authUrl + * path 'auth/realms/sunbird-rc/protocol/openid-connect/token' + * header Content-Type = 'application/x-www-form-urlencoded; charset=utf-8' + * header Host = 'keycloak:8080' + * form field grant_type = 'password' + * form field client_id = 'registry-frontend' + * form field username = studentRequest.contactDetails.mobile + * form field password = studentRequest.userDetails.passkey + * method post + Then status 200 + And print response.access_token + * def student_token = 'Bearer ' + response.access_token + * sleep(3000) + # get student info + Given url registryUrl + And path 'api/v1/StudentWithPassword/' + studentOsid + And header Authorization = student_token + When method get + Then status 200 + And response.osid.length > 0 + # get student info with view template + Given url registryUrl + And path 'api/v1/StudentWithPassword/' + studentOsid + And header Authorization = student_token + And header viewTemplateId = 'student_view_template.json' + When method get + Then status 200 + * match response.contactDetails == { mobile: '#notpresent', email: '#present', osid: '#present' } + @env=async + Scenario: Check if events are published + # should get metrics + * sleep(11000) + Given url metricsUrl + And path '/v1/metrics' + When method get + Then status 200 + And assert response.birthcertificate.READ == "5" + And assert response.birthcertificate.UPDATE == "1" + And assert response.birthcertificate.ADD == "1" + And assert response.birthcertificate.DELETE == "1" + @env=async + Scenario: Check if notifications are sent + Given url notificationsUrl + And path '/notification-service/v1/notification' + When method get + Then status 200 + * def studentRequest = read('StudentRequest.json') + * def notificationStudent = studentRequest.contact + And print response[notificationStudent] + And assert response[notificationStudent] != null diff --git a/java/apitest/src/test/resources/views/student_view_template.json b/java/apitest/src/test/resources/views/student_view_template.json new file mode 100644 index 000000000..76a4d7276 --- /dev/null +++ b/java/apitest/src/test/resources/views/student_view_template.json @@ -0,0 +1,22 @@ +{ + "id": "studentView", + "subject": "Student", + "fields": [ + { + "name": "userDetails", + "title": "userDetails", + "display": true + }, + { + "title": "contactDetails", + "function": "#/functionDefinitions/removePath($contactDetails, $.mobile)", + "$comment": "This is a virtual field not defined in the schema" + } + ], + "functionDefinitions": [ + { + "name": "removePath", + "provider": "dev.sunbirdrc.provider.RemovePathFunctionProvider" + } + ] +} diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/Constants.java b/java/registry/src/main/java/dev/sunbirdrc/registry/Constants.java index fc2360f16..2b67ccd30 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/Constants.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/Constants.java @@ -34,4 +34,5 @@ public class Constants { public static final String FILE_URL = "fileUrl"; public static final String CREDENTIAL_TEMPLATE = "credentialTemplate"; + public static final String VIEW_TEMPLATE_ID = "viewTemplateId"; } diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java index eb4c959f6..997b4fb5a 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java @@ -24,7 +24,9 @@ import dev.sunbirdrc.registry.transform.Configuration; import dev.sunbirdrc.registry.transform.Data; import dev.sunbirdrc.registry.transform.ITransformer; +import dev.sunbirdrc.registry.util.ViewTemplateManager; import dev.sunbirdrc.validators.ValidationException; +import org.agrona.Strings; import org.apache.commons.lang3.StringUtils; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.jetbrains.annotations.NotNull; @@ -63,6 +65,8 @@ public class RegistryEntityController extends AbstractController { @Autowired private AsyncRequest asyncRequest; + @Autowired + private ViewTemplateManager viewTemplateManager; @Value("${authentication.enabled:true}") boolean securityEnabled; @@ -496,8 +500,8 @@ private ArrayList getConsentFields(HttpServletRequest request) { {MediaType.APPLICATION_PDF_VALUE, MediaType.TEXT_HTML_VALUE, Constants.SVG_MEDIA_TYPE}) public ResponseEntity getEntityType(@PathVariable String entityName, @PathVariable String entityId, - HttpServletRequest request) { - + HttpServletRequest request, + @RequestHeader(required = false) String viewTemplateId) { ResponseParams responseParams = new ResponseParams(); Response response ; if (registryHelper.doesEntityOperationRequireAuthorization(entityName) && securityEnabled) { @@ -519,7 +523,8 @@ public ResponseEntity getEntityType(@PathVariable String entityName, } try { String readerUserId = getUserId(entityName, request); - JsonNode node = registryHelper.readEntity(readerUserId, entityName, entityId, false, null, false) + JsonNode node = registryHelper.readEntity(readerUserId, entityName, entityId, false, + viewTemplateManager.getViewTemplateById(viewTemplateId), false) .get(entityName); JsonNode signedNode = objectMapper.readTree(node.get(OSSystemFields._osSignedData.name()).asText()); return new ResponseEntity<>(certificateService.getCertificate(signedNode, @@ -563,8 +568,8 @@ private String getTemplateUrlFromRequest(HttpServletRequest request, String enti public ResponseEntity getEntity( @PathVariable String entityName, @PathVariable String entityId, - @RequestHeader HttpHeaders header, HttpServletRequest request) throws RecordNotFoundException { - + @RequestHeader HttpHeaders header, HttpServletRequest request, + @RequestHeader(required = false) String viewTemplateId) { boolean requireLDResponse = false; boolean requireVCResponse = false; for (MediaType t : header.getAccept()) { @@ -591,7 +596,7 @@ public ResponseEntity getEntity( try { checkEntityNameInDefinitionManager(entityName); String readerUserId = getUserId(entityName, request); - JsonNode node = getEntityJsonNode(entityName, entityId, requireLDResponse, readerUserId); + JsonNode node = getEntityJsonNode(entityName, entityId, requireLDResponse, readerUserId, viewTemplateId); if (requireLDResponse) { addJsonLDSpec(node); } else if (requireVCResponse) { @@ -620,8 +625,10 @@ private void addJsonLDSpec(JsonNode node) { } - private JsonNode getEntityJsonNode(@PathVariable String entityName, @PathVariable String entityId, boolean requireLDResponse, String userId) throws Exception { - JsonNode resultNode = registryHelper.readEntity(userId, entityName, entityId, false, null, false); + private JsonNode getEntityJsonNode(@PathVariable String entityName, @PathVariable String entityId, + boolean requireLDResponse, String userId, String viewTemplateId) throws Exception { + JsonNode resultNode = registryHelper.readEntity(userId, entityName, entityId, false, + viewTemplateManager.getViewTemplateById(viewTemplateId), false); Data data = new Data<>(resultNode); Configuration config = configurationHelper.getResponseConfiguration(requireLDResponse); ITransformer responseTransformer = transformer.getInstance(config); @@ -636,18 +643,25 @@ private JsonNode getEntityJsonNode(@PathVariable String entityName, @PathVariabl } @RequestMapping(value = "/api/v1/{entityName}", method = RequestMethod.GET) - public ResponseEntity getEntityByToken(@PathVariable String entityName, HttpServletRequest request) throws RecordNotFoundException { - + public ResponseEntity getEntityByToken(@PathVariable String entityName, HttpServletRequest request, + @RequestHeader(required = false) String viewTemplateId) throws RecordNotFoundException { ResponseParams responseParams = new ResponseParams(); Response response = new Response(Response.API_ID.GET, "OK", responseParams); try { checkEntityNameInDefinitionManager(entityName); - JsonNode result = registryHelper.getRequestedUserDetails(request, entityName); - if (result.get(entityName).size() > 0) { - ArrayNode responseFromDb = registryHelper.fetchFromDBUsingEsResponse(entityName, (ArrayNode) result.get(entityName)); - return new ResponseEntity<>(responseFromDb, HttpStatus.OK); + String userId = registryHelper.getUserId(request, entityName); + if (!Strings.isEmpty(userId)) { + JsonNode responseFromDb = registryHelper.searchEntitiesByUserId(entityName, userId, viewTemplateId); + JsonNode entities = responseFromDb.get(entityName); + if (entities.size() > 0) { + return new ResponseEntity<>(entities, HttpStatus.OK); + } else { + responseParams.setErrmsg("No record found"); + responseParams.setStatus(Response.Status.UNSUCCESSFUL); + return new ResponseEntity<>(response, HttpStatus.NOT_FOUND); + } } else { - responseParams.setErrmsg("Entity not found"); + responseParams.setErrmsg("User id is empty"); responseParams.setStatus(Response.Status.UNSUCCESSFUL); return new ResponseEntity<>(response, HttpStatus.NOT_FOUND); } @@ -863,7 +877,7 @@ public ResponseEntity revokeACredential ( try { String tag = "RegistryController.revokeAnExistingCredential " + entityName; watch.start(tag); - JsonNode existingEntityNode = getEntityJsonNode(entityName, entityId,false, userId); + JsonNode existingEntityNode = getEntityJsonNode(entityName, entityId,false, userId, null); String signedData = existingEntityNode.get(OSSystemFields._osSignedData.name()).asText(); if (signedData.equals(new String()) || signedData.equals(null)) { throw new RecordNotFoundException("Credential is already revoked"); diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java b/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java index c17e646b7..51327ae9b 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java @@ -42,6 +42,7 @@ import dev.sunbirdrc.views.ViewTransformer; import io.minio.errors.*; import lombok.Setter; +import org.agrona.Strings; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; @@ -111,6 +112,9 @@ public class RegistryHelper { @Autowired private ISearchService searchService; + @Autowired + private NativeSearchService nativeSearchService; + @Autowired private ViewTemplateManager viewTemplateManager; @@ -157,6 +161,9 @@ public class RegistryHelper { @Value("${workflow.enabled:true}") private boolean workflowEnabled; + @Value("${view_template.decrypt_private_fields:false}") + private boolean viewTemplateDecryptPrivateFields; + @Autowired private EntityTypeHandler entityTypeHandler; @@ -266,7 +273,8 @@ public JsonNode readEntity(JsonNode inputJson, String userId, boolean requireLDR return readEntity(userId, entityType, label, includeSignatures, viewTemplateManager.getViewTemplate(inputJson), requireLDResponse); } - public JsonNode readEntity(String userId, String entityType, String label, boolean includeSignatures, ViewTemplate viewTemplate, boolean requireLDResponse) throws Exception { + public JsonNode readEntity(String userId, String entityType, String label, boolean includeSignatures, + ViewTemplate viewTemplate, boolean requireLDResponse) throws Exception { boolean includePrivateFields = false; JsonNode resultNode = null; RecordIdentifier recordId = RecordIdentifier.parse(label); @@ -286,7 +294,9 @@ public JsonNode readEntity(String userId, String entityType, String label, boole } if (viewTemplate != null) { ViewTransformer vTransformer = new ViewTransformer(); - resultNode = includePrivateFields ? decryptionHelper.getDecryptedJson(resultNode) : resultNode; + if (viewTemplateDecryptPrivateFields) { + resultNode = includePrivateFields ? decryptionHelper.getDecryptedJson(resultNode) : resultNode; + } resultNode = vTransformer.transform(viewTemplate, resultNode); } logger.debug("readEntity ends"); @@ -320,8 +330,12 @@ public JsonNode readEntity(JsonNode inputJson, String userId) throws Exception { * @throws Exception */ public JsonNode searchEntity(JsonNode inputJson) throws Exception { + return searchEntity(inputJson, searchService); + } + + private JsonNode searchEntity(JsonNode inputJson, ISearchService service) throws Exception { logger.debug("searchEntity starts"); - JsonNode resultNode = searchService.search(inputJson); + JsonNode resultNode = service.search(inputJson); ViewTemplate viewTemplate = viewTemplateManager.getViewTemplate(inputJson); if (viewTemplate != null) { ViewTransformer vTransformer = new ViewTransformer(); @@ -766,11 +780,7 @@ private JsonNode getUserInfoFromKeyCloak(HttpServletRequest request, String enti private JsonNode getUserInfoFromRegistry(HttpServletRequest request, String entityName) throws Exception { String userId = getUserId(request,entityName); if (userId != null) { - ObjectNode payload = JsonNodeFactory.instance.objectNode(); - payload.set(ENTITY_TYPE, JsonNodeFactory.instance.arrayNode().add(entityName)); - ObjectNode filters = JsonNodeFactory.instance.objectNode(); - filters.set(OSSystemFields.osOwner.toString(), JsonNodeFactory.instance.objectNode().put("contains", userId)); - payload.set(FILTERS, filters); + ObjectNode payload = getSearchByOwnerQuery(entityName, userId); watch.start("RegistryController.searchEntity"); JsonNode result = searchEntity(payload); @@ -780,6 +790,16 @@ private JsonNode getUserInfoFromRegistry(HttpServletRequest request, String enti throw new Exception("Forbidden"); } + @NotNull + private ObjectNode getSearchByOwnerQuery(String entityName, String userId) { + ObjectNode payload = JsonNodeFactory.instance.objectNode(); + payload.set(ENTITY_TYPE, JsonNodeFactory.instance.arrayNode().add(entityName)); + ObjectNode filters = JsonNodeFactory.instance.objectNode(); + filters.set(OSSystemFields.osOwner.toString(), JsonNodeFactory.instance.objectNode().put("contains", userId)); + payload.set(FILTERS, filters); + return payload; + } + public String authorize(String entityName, String entityId, HttpServletRequest request) throws Exception { String userIdFromRequest = getUserId(request, entityName); if (getManageRoles(entityName).size() > 0) { @@ -841,6 +861,13 @@ public ArrayNode fetchFromDBUsingEsResponse(String entity, ArrayNode esSearchRes } return result; } + public JsonNode searchEntitiesByUserId(String entity, String userId, String viewTemplateId) throws Exception { + ObjectNode searchByOwnerQuery = getSearchByOwnerQuery(entity, userId); + if (!Strings.isEmpty(viewTemplateId)) { + searchByOwnerQuery.put(VIEW_TEMPLATE_ID, viewTemplateId); + } + return searchEntity(searchByOwnerQuery, nativeSearchService); + } public void authorizeInviteEntity(HttpServletRequest request, String entityName) throws Exception { List inviteRoles = definitionsManager.getDefinition(entityName) diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/service/NativeSearchService.java b/java/registry/src/main/java/dev/sunbirdrc/registry/service/NativeSearchService.java index 67c3452e6..fdf00989e 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/service/NativeSearchService.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/service/NativeSearchService.java @@ -1,10 +1,7 @@ package dev.sunbirdrc.registry.service; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Transaction; @@ -27,7 +24,6 @@ import dev.sunbirdrc.registry.dao.IRegistryDao; import dev.sunbirdrc.registry.dao.RegistryDaoImpl; import dev.sunbirdrc.registry.dao.SearchDaoImpl; -import dev.sunbirdrc.registry.middleware.util.Constants; import dev.sunbirdrc.registry.middleware.util.JSONUtil; import dev.sunbirdrc.registry.model.DBConnectionInfo; import dev.sunbirdrc.registry.model.DBConnectionInfoMgr; @@ -66,22 +62,25 @@ public class NativeSearchService implements ISearchService { @Value("${search.offset}") private int offset; - + @Value("${search.limit}") private int limit; @Value("${audit.enabled}") private boolean auditEnabled; - + @Value("${audit.frame.suffix}") private String auditSuffix; @Value("${search.expandInternal}") private boolean expandInternal; + @Value("${search.removeNonPublicFieldsForNativeSearch:true}") + private boolean removeNonPublicFieldsForNativeSearch; + @Override public JsonNode search(JsonNode inputQueryNode) throws IOException { - + ArrayNode result = JsonNodeFactory.instance.arrayNode(); SearchQuery searchQuery = getSearchQuery(inputQueryNode, offset, limit); @@ -120,8 +119,10 @@ public JsonNode search(JsonNode inputQueryNode) throws IOException { String prefix = shard.getShardLabel() + RecordIdentifier.getSeparator(); JSONUtil.addPrefix((ObjectNode) shardResult, prefix, new ArrayList<>(Arrays.asList(uuidPropertyName))); } - removeNonPublicFields(result, searchQuery, shardResult); - transaction.add(tx.hashCode()); + result = removeNonPublicFields(searchQuery, shardResult); + if (tx != null) { + transaction.add(tx.hashCode()); + } } } catch (Exception e) { logger.error("search operation failed: {}", e); @@ -140,21 +141,27 @@ public JsonNode search(JsonNode inputQueryNode) throws IOException { } } - + return buildResultNode(searchQuery, result); } - private void removeNonPublicFields(ArrayNode result, SearchQuery searchQuery, ObjectNode shardResult) throws Exception { + private ArrayNode removeNonPublicFields(SearchQuery searchQuery, ObjectNode shardResult) throws Exception { + ArrayNode result = JsonNodeFactory.instance.arrayNode(); for(String entityType: searchQuery.getEntityTypes()) { ArrayNode arrayNode = (ArrayNode) shardResult.get(entityType); - for(JsonNode node : arrayNode) { - result.add(JSONUtil.removeNodesByPath(node, definitionsManager.getExcludingFieldsForEntity(entityType))); + if (removeNonPublicFieldsForNativeSearch) { + for(JsonNode node : arrayNode) { + result.add(JSONUtil.removeNodesByPath(node, definitionsManager.getExcludingFieldsForEntity(entityType))); + } + } else { + result = arrayNode; } } + return result; } /** - * Builds result node from given array of shard nodes + * Builds result node from given array of shard nodes * @param searchQuery * @param allShardResult * @return diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/sink/DBProviderFactory.java b/java/registry/src/main/java/dev/sunbirdrc/registry/sink/DBProviderFactory.java index 8f2341d32..3ca17f0a3 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/sink/DBProviderFactory.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/sink/DBProviderFactory.java @@ -20,15 +20,15 @@ public class DBProviderFactory { @Autowired DBConnectionInfoMgr dbConnectionInfoMgr; - + public DatabaseProvider getInstance(DBConnectionInfo connectionInfo) { DatabaseProvider provider = null; String dbProvider = environment.getProperty(Constants.DATABASE_PROVIDER); String uuidPropertyName = dbConnectionInfoMgr.getUuidPropertyName(); - - // In tests, we use TinkerGraph presently. - if (!dbProvider.equalsIgnoreCase(Constants.GraphDatabaseProvider.TINKERGRAPH.getName()) && - dbProviderInstances.containsKey(connectionInfo.getShardId())) { + if (dbProvider == null) { + throw new RuntimeException("No Database Provider is configured. Please configure a Database Provider"); + } + if (connectionInfo != null && dbProviderInstances.containsKey(connectionInfo.getShardId())) { provider = dbProviderInstances.get(connectionInfo.getShardId()); } else { if (dbProvider.equalsIgnoreCase(Constants.GraphDatabaseProvider.ORIENTDB.getName())) { diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/util/ViewTemplateManager.java b/java/registry/src/main/java/dev/sunbirdrc/registry/util/ViewTemplateManager.java index 1978ec252..804eb686d 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/util/ViewTemplateManager.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/util/ViewTemplateManager.java @@ -15,6 +15,7 @@ import javax.annotation.PostConstruct; +import org.agrona.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -23,10 +24,10 @@ @Component public class ViewTemplateManager { - + private static Logger logger = LoggerFactory.getLogger(ViewTemplateManager.class); - + public static final String viewLocation = "classpath*:views/*.json"; private static final String viewTemplateId = "viewTemplateId"; private static final String viewTemplate = "viewTemplate"; @@ -34,7 +35,7 @@ public class ViewTemplateManager { private OSResourceLoader osResourceLoader; private ObjectMapper mapper = new ObjectMapper(); private Map templates = new HashMap<>(); - + @Autowired private ResourceLoader resourceLoader; @@ -58,10 +59,10 @@ public void loadTemplates() throws Exception { } } - + /** - * Returns the view template based on the request parameter viewTemplateId, viewTemplate - * + * Returns the view template based on the request parameter viewTemplateId, viewTemplate + * * @param requestNode * @return * @throws JsonParseException @@ -76,7 +77,7 @@ public ViewTemplate getViewTemplate(JsonNode requestNode) { if (requestNode.has(viewTemplateId)) { name = requestNode.get(viewTemplateId).asText(); logger.info("Applying view template {}", name); - viewTemp = templates.get(name); + viewTemp = getViewTemplateById(name); if(viewTemp == null) logger.error("view template for {} not found!", name); } else if (requestNode.has(viewTemplate)) { @@ -88,7 +89,15 @@ public ViewTemplate getViewTemplate(JsonNode requestNode) { } return viewTemp; } - + + public ViewTemplate getViewTemplateById(String name) { + if (Strings.isEmpty(name) || !templates.containsKey(name)) { + return null; + } + return templates.get(name); + } + + private ViewTemplate getViewTemplateByContent(String templateContent) throws IOException { return mapper.readValue(templateContent, ViewTemplate.class); @@ -108,5 +117,5 @@ public boolean isPrivateFieldEnabled(ViewTemplate viewTemplate, String entityTyp } return privateFieldEnabled; } - + } diff --git a/java/registry/src/main/resources/application.yml b/java/registry/src/main/resources/application.yml index 560f46c22..aefd99c47 100644 --- a/java/registry/src/main/resources/application.yml +++ b/java/registry/src/main/resources/application.yml @@ -69,6 +69,8 @@ registry: workflow: enabled: ${workflow.enable:true} +view_template_decrypt_private_fields: ${view_template_decrypt_private_fields:false} + async: enabled: ${async_enabled:false} @@ -101,6 +103,7 @@ search: # not offer high speed reads. This is the default search service, if this config is not provided. # If ElasticSearchService, then Elastic search is used. expandInternal: ${search_expandInternal:true} + removeNonPublicFieldsForNativeSearch: ${remove_non_public_fields_for_native_search:true} providerName: ${search_providerName:dev.sunbirdrc.registry.service.NativeSearchService} # This property is to be used for read request @@ -334,6 +337,8 @@ name: test-yaml enviroment: test workflow: enabled: ${workflow.enable:true} +view_template: + decrypt_private_fields: ${view_template_decrypt_private_fields:false} notification: service: enabled: true diff --git a/java/registry/src/test/java/dev/sunbirdrc/registry/controller/RegistryEntityControllerTest.java b/java/registry/src/test/java/dev/sunbirdrc/registry/controller/RegistryEntityControllerTest.java index c5233ffd9..85504c9e0 100644 --- a/java/registry/src/test/java/dev/sunbirdrc/registry/controller/RegistryEntityControllerTest.java +++ b/java/registry/src/test/java/dev/sunbirdrc/registry/controller/RegistryEntityControllerTest.java @@ -15,6 +15,7 @@ import dev.sunbirdrc.registry.transform.*; import dev.sunbirdrc.registry.util.DefinitionsManager; import org.apache.tinkerpop.gremlin.structure.Vertex; +import dev.sunbirdrc.registry.util.ViewTemplateManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -71,6 +72,9 @@ public class RegistryEntityControllerTest { @InjectMocks private RegistryEntityController registryEntityController; + @MockBean + private ViewTemplateManager viewTemplateManager; + @Before public void setUp() { abstractController = new RegistryEntityController(); diff --git a/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java b/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java index 8be779999..5dcd4c904 100644 --- a/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java +++ b/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java @@ -583,7 +583,7 @@ public void shouldTriggerProviderFunctionOnAttestationCompleted() throws Excepti FunctionExecutor functionExecutorMock = Mockito.spy(FunctionExecutor.class); ReflectionTestUtils.setField(registryHelper, "functionExecutor", functionExecutorMock); definitionsManager.getDefinition("Student").getOsSchemaConfiguration().setFunctionDefinitions(Arrays.asList( - FunctionDefinition.builder().name("userDefinedConcat").provider("org.example.provider.UUIDFunctionProvider").build() + FunctionDefinition.builder().name("userDefinedConcat").provider("dev.sunbirdrc.provider.UUIDFunctionProvider").build() )); PluginResponseMessage pluginResponseMessage = PluginResponseMessage.builder() .policyName("test") diff --git a/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java b/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java index d9781e051..af111bead 100644 --- a/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java +++ b/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java @@ -1,48 +1,180 @@ package dev.sunbirdrc.registry.service.impl; - -import dev.sunbirdrc.registry.app.SunbirdRCApplication; -import dev.sunbirdrc.registry.config.GenericConfiguration; -import dev.sunbirdrc.registry.controller.RegistryTestBase; -import dev.sunbirdrc.registry.dao.IRegistryDao; -import dev.sunbirdrc.registry.dao.SearchDao; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import dev.sunbirdrc.registry.dao.VertexWriter; import dev.sunbirdrc.registry.middleware.util.Constants; -import dev.sunbirdrc.registry.service.RegistryService; -import dev.sunbirdrc.registry.service.ISearchService; +import dev.sunbirdrc.registry.model.DBConnectionInfo; +import dev.sunbirdrc.registry.model.DBConnectionInfoMgr; +import dev.sunbirdrc.registry.service.NativeSearchService; +import dev.sunbirdrc.registry.sink.DBProviderFactory; +import dev.sunbirdrc.registry.sink.DatabaseProvider; +import dev.sunbirdrc.registry.sink.shard.DefaultShardAdvisor; +import dev.sunbirdrc.registry.sink.shard.ShardManager; +import dev.sunbirdrc.registry.util.Definition; +import dev.sunbirdrc.registry.util.DefinitionsManager; +import dev.sunbirdrc.registry.util.OSResourceLoader; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.junit.Assert; import org.junit.Before; import org.junit.FixMethodOrder; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.rules.ExpectedException; +import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; +import org.keycloak.common.util.RandomString; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.util.ReflectionTestUtils; +import javax.annotation.PreDestroy; import java.io.IOException; +import java.util.Collections; -@Ignore @RunWith(SpringRunner.class) -@SpringBootTest(classes = { SunbirdRCApplication.class, IRegistryDao.class, SearchDao.class, ISearchService.class, - GenericConfiguration.class }) +@SpringBootTest(classes = {DefinitionsManager.class, ObjectMapper.class, DBProviderFactory.class, DBConnectionInfoMgr.class, + OSResourceLoader.class, ShardManager.class, DefaultShardAdvisor.class}) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @ActiveProfiles(Constants.TEST_ENVIRONMENT) -public class NativeSearchServiceTest extends RegistryTestBase { +public class NativeSearchServiceTest { - private static final String BASE_SEARCH_JSONLD = "base_search_context.jsonld"; - private static final String CONTEXT_NAMESPACE = "http://example.com/voc/teacher/1.0.0/"; - @Rule - public ExpectedException expectedEx = ExpectedException.none(); + private static Graph graph; + private DatabaseProvider databaseProvider; + @Autowired + private DefinitionsManager definitionsManager; @Autowired - private ISearchService searchService; + private DBProviderFactory dbProviderFactory; @Autowired - private RegistryService registryService; + private DBConnectionInfoMgr dbConnectionInfoMgr; + @Autowired + private ShardManager shardManager; + + private NativeSearchService nativeSearchService; + @Before - public void initialize() throws IOException { + public void init() throws IOException { + dbConnectionInfoMgr.setUuidPropertyName("tid"); + DBConnectionInfo dbConnectionInfo = new DBConnectionInfo(); + dbConnectionInfo.setShardId(RandomString.randomCode(4)); + dbConnectionInfo.setShardLabel(""); + dbConnectionInfoMgr.setConnectionInfo(Collections.singletonList(dbConnectionInfo)); + + databaseProvider = dbProviderFactory.getInstance(dbConnectionInfo); + graph = databaseProvider.getOSGraph().getGraphStore(); + populateGraph(); + createTeacherDefinition(); + nativeSearchService = new NativeSearchService(); + ReflectionTestUtils.setField(nativeSearchService, "definitionsManager", definitionsManager); + ReflectionTestUtils.setField(nativeSearchService, "dbConnectionInfoMgr", dbConnectionInfoMgr); + ReflectionTestUtils.setField(nativeSearchService, "shardManager", shardManager); + ReflectionTestUtils.setField(nativeSearchService, "limit", 100); + ReflectionTestUtils.setField(nativeSearchService, "uuidPropertyName", "osid"); + + } + + + @Test + public void shouldReturnRecordsMatchingFilters() throws IOException { + JsonNode query = getSearchQuery(); + JsonNode results = nativeSearchService.search(query); + Assert.assertEquals(1, results.get("Teacher").size()); + } + + private JsonNode getSearchQuery() throws JsonProcessingException { + JsonNode query = new ObjectMapper().readTree("{\n" + + " \"entityType\": [\"Teacher\"],\n" + + " \"filters\": {\n" + + " \"teacherName\": {\n" + + " \"eq\": \"ram\"\n" + + " }\n" + + " }\n" + + "}"); + return query; + } + + @Test + public void shouldRemovePublicFields() throws IOException { + ReflectionTestUtils.setField(nativeSearchService, "removeNonPublicFieldsForNativeSearch", true); + JsonNode query = getSearchQuery(); + JsonNode results = nativeSearchService.search(query); + System.out.println(results.get("Teacher")); + Assert.assertEquals(1, results.get("Teacher").size()); + Assert.assertEquals(4, results.get("Teacher").get(0).size()); + Assert.assertNull(results.get("Teacher").get(0).get("serialNum")); + + } + + @Test + public void shouldNotRemovePublicFields() throws IOException { + ReflectionTestUtils.setField(nativeSearchService, "removeNonPublicFieldsForNativeSearch", false); + JsonNode query = getSearchQuery(); + JsonNode results = nativeSearchService.search(query); + Assert.assertEquals(1, results.get("Teacher").size()); + Assert.assertEquals(5, results.get("Teacher").get(0).size()); + Assert.assertNotNull(results.get("Teacher").get(0).get("serialNum")); + } + + + @PreDestroy + public void shutdown() throws Exception { + + graph.close(); + } + + + private void populateGraph() { + VertexWriter vertexWriter = new VertexWriter(graph, databaseProvider, "tid"); + Vertex v1 = vertexWriter.createVertex("Teacher"); + v1.property("serialNum", 1); + v1.property("teacherName", "mark"); + Vertex v2 = vertexWriter.createVertex("Teacher"); + v2.property("serialNum", 2); + v2.property("teacherName", "zuer"); + Vertex v3 = vertexWriter.createVertex("Teacher"); + v3.property("serialNum", 3); + v3.property("teacherName", "ram"); + } + private void createTeacherDefinition() throws JsonProcessingException { + Definition definition = new Definition(new ObjectMapper().readTree("{\n" + + " \"$schema\": \"http://json-schema.org/draft-07/schema\",\n" + + " \"type\": \"object\",\n" + + " \"properties\": {\n" + + " \"Teacher\": {\n" + + " \"$ref\": \"#/definitions/Teacher\"\n" + + " }\n" + + " },\n" + + " \"required\": [\n" + + " \"Teacher\"\n" + + " ],\n" + + " \"title\":\"Teacher\",\n" + + " \"definitions\": {\n" + + " \"Teacher\": {\n" + + " \"$id\": \"#/properties/Teacher\",\n" + + " \"type\": \"object\",\n" + + " \"title\": \"The Teacher Schema\",\n" + + " \"required\": [\n" + + "\n" + + " ],\n" + + " \"properties\": {\n" + + " \"serialNum\": {\n" + + " \"type\": \"string\"\n" + + " },\n" + + " \"teacherName\": {\n" + + " \"type\": \"string\"\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"_osConfig\": {\n" + + " \"internalFields\": [\"$.serialNum\"]\n" + + " }\n" + + "}\n")); + definitionsManager.appendNewDefinition(definition); } } diff --git a/java/view-templates/src/main/java/dev/sunbirdrc/provider/RemovePathFunctionProvider.java b/java/view-templates/src/main/java/dev/sunbirdrc/provider/RemovePathFunctionProvider.java new file mode 100644 index 000000000..42f485e69 --- /dev/null +++ b/java/view-templates/src/main/java/dev/sunbirdrc/provider/RemovePathFunctionProvider.java @@ -0,0 +1,45 @@ +package dev.sunbirdrc.provider; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import dev.sunbirdrc.views.IViewFunctionProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +public class RemovePathFunctionProvider implements IViewFunctionProvider { + private static final Logger logger = LoggerFactory.getLogger(RemovePathFunctionProvider.class); + + @Override + public JsonNode doAction(List values) { + return null; + } + + @Override + public JsonNode doAction(List values, String[] paths) { + try { + if (values.size() == 1 && paths.length > 1) { + JsonNode jsonObject = (JsonNode) values.get(0); + DocumentContext documentContext = JsonPath.parse(new ObjectMapper().writeValueAsString(jsonObject)); + for (int i = 1, pathsLength = paths.length; i < pathsLength; i++) { + String path = paths[i]; + try { + documentContext.delete(JsonPath.compile(path)); + } catch (Exception e) { + logger.error("Error while deleting path: ", e); + } + } + return new ObjectMapper().readTree(documentContext.jsonString()); + } + } catch (Exception e) { + logger.error("Error while removing paths: ", e); + } + + return JsonNodeFactory.instance.textNode(""); + + } +} diff --git a/java/view-templates/src/main/java/org/example/provider/SampleViewFunctionProvider.java b/java/view-templates/src/main/java/dev/sunbirdrc/provider/SampleViewFunctionProvider.java similarity index 82% rename from java/view-templates/src/main/java/org/example/provider/SampleViewFunctionProvider.java rename to java/view-templates/src/main/java/dev/sunbirdrc/provider/SampleViewFunctionProvider.java index ef6dc07af..71d889c18 100644 --- a/java/view-templates/src/main/java/org/example/provider/SampleViewFunctionProvider.java +++ b/java/view-templates/src/main/java/dev/sunbirdrc/provider/SampleViewFunctionProvider.java @@ -1,4 +1,4 @@ -package org.example.provider; +package dev.sunbirdrc.provider; import dev.sunbirdrc.views.IViewFunctionProvider; import java.util.List; @@ -14,9 +14,14 @@ public String doAction(List values) { return concat(values); } + @Override + public String doAction(List values, String[] paths) { + return doAction(values); + } + /** * simple concat for the values as string and comma(',') as seperator - * + * * @param args * @return */ diff --git a/java/view-templates/src/main/java/org/example/provider/UUIDFunctionProvider.java b/java/view-templates/src/main/java/dev/sunbirdrc/provider/UUIDFunctionProvider.java similarity index 90% rename from java/view-templates/src/main/java/org/example/provider/UUIDFunctionProvider.java rename to java/view-templates/src/main/java/dev/sunbirdrc/provider/UUIDFunctionProvider.java index 4a1ea9dfa..d91d67a4d 100644 --- a/java/view-templates/src/main/java/org/example/provider/UUIDFunctionProvider.java +++ b/java/view-templates/src/main/java/dev/sunbirdrc/provider/UUIDFunctionProvider.java @@ -1,4 +1,4 @@ -package org.example.provider; +package dev.sunbirdrc.provider; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; @@ -24,9 +24,14 @@ public String doAction(List values) { return generateUUID(values); } + @Override + public String doAction(List values, String[] paths) { + return doAction(values); + } + /** * simple concat for the values as string and comma(',') as seperator - * + * * @param args * @return */ diff --git a/java/view-templates/src/main/java/dev/sunbirdrc/views/EvaluatorFactory.java b/java/view-templates/src/main/java/dev/sunbirdrc/views/EvaluatorFactory.java index d804e7931..00df2dbe7 100644 --- a/java/view-templates/src/main/java/dev/sunbirdrc/views/EvaluatorFactory.java +++ b/java/view-templates/src/main/java/dev/sunbirdrc/views/EvaluatorFactory.java @@ -3,7 +3,7 @@ import java.util.List; public class EvaluatorFactory { - + /** * returns the instance of IEvaluator implementations (like:FunctionEvaluator, ProviderEvaluator * @@ -17,14 +17,14 @@ public static IEvaluator getInstance(FunctionDefinition functiondef, Lis FieldFunction function = null; if (functiondef.getResult() != null) { - function = getFieldFunction(functiondef.getResult(), actualValues); + function = getFieldFunction(functiondef.getResult(), actualValues, argumentsPath); evaluator = new FunctionEvaluator(function); } else if (functiondef.getProvider() != null) { - function = getFieldFunction(functiondef.getProvider(), actualValues); + function = getFieldFunction(functiondef.getProvider(), actualValues, argumentsPath); evaluator = new ProviderEvaluator(function); } else if (functiondef.getReference() != null) { - //TODO: implementation for reference evaluator + //TODO: implementation for reference evaluator } return evaluator; @@ -33,13 +33,15 @@ public static IEvaluator getInstance(FunctionDefinition functiondef, Lis * Creates FieldFunction and sets argValues * @param expression * @param actualValues + * @param argumentsPath * @return */ - private static FieldFunction getFieldFunction(String expression, List actualValues) { + private static FieldFunction getFieldFunction(String expression, List actualValues, String[] argumentsPath) { FieldFunction function = new FieldFunction(expression); function.setArgValues(actualValues); + function.setArgumentsPaths(argumentsPath); return function; } - + } diff --git a/java/view-templates/src/main/java/dev/sunbirdrc/views/FieldFunction.java b/java/view-templates/src/main/java/dev/sunbirdrc/views/FieldFunction.java index a01204498..ef33218c0 100644 --- a/java/view-templates/src/main/java/dev/sunbirdrc/views/FieldFunction.java +++ b/java/view-templates/src/main/java/dev/sunbirdrc/views/FieldFunction.java @@ -6,6 +6,7 @@ public class FieldFunction { private String expression; private List argValues; + private String[] argumentsPath; public FieldFunction(String expression) { this.expression = expression; @@ -22,4 +23,12 @@ public String getExpression() { public List getArgValues() { return this.argValues; } + + public void setArgumentsPaths(String[] argumentsPath) { + this.argumentsPath = argumentsPath; + } + + public String[] getArgumentsPath() { + return argumentsPath; + } } diff --git a/java/view-templates/src/main/java/dev/sunbirdrc/views/IViewFunctionProvider.java b/java/view-templates/src/main/java/dev/sunbirdrc/views/IViewFunctionProvider.java index b84c0f993..6df419a8c 100644 --- a/java/view-templates/src/main/java/dev/sunbirdrc/views/IViewFunctionProvider.java +++ b/java/view-templates/src/main/java/dev/sunbirdrc/views/IViewFunctionProvider.java @@ -3,13 +3,14 @@ import java.util.List; public interface IViewFunctionProvider { - + /** - * A list of objects (ordered) are passed to the function. arg1 will appear first in the list, followed by arg2 and so on. - * The adopter can choose to implement the transformation. + * A list of objects (ordered) are passed to the function. arg1 will appear first in the list, followed by arg2 and so on. + * The adopter can choose to implement the transformation. * @param values * @return */ - public abstract T doAction(List values); + T doAction(List values); + T doAction(List values, String[] paths); } diff --git a/java/view-templates/src/main/java/dev/sunbirdrc/views/ProviderEvaluator.java b/java/view-templates/src/main/java/dev/sunbirdrc/views/ProviderEvaluator.java index bbb6d5c9e..c588de160 100644 --- a/java/view-templates/src/main/java/dev/sunbirdrc/views/ProviderEvaluator.java +++ b/java/view-templates/src/main/java/dev/sunbirdrc/views/ProviderEvaluator.java @@ -11,7 +11,7 @@ public ProviderEvaluator(FieldFunction function) { @Override public Object evaluate() { IViewFunctionProvider viewFuntionProvider = getInstance(function.getExpression()); - Object result = viewFuntionProvider.doAction(function.getArgValues()); + Object result = viewFuntionProvider.doAction(function.getArgValues(), function.getArgumentsPath()); return result; } diff --git a/java/view-templates/src/main/java/dev/sunbirdrc/views/ViewTransformer.java b/java/view-templates/src/main/java/dev/sunbirdrc/views/ViewTransformer.java index 517bb6012..db75fe151 100644 --- a/java/view-templates/src/main/java/dev/sunbirdrc/views/ViewTransformer.java +++ b/java/view-templates/src/main/java/dev/sunbirdrc/views/ViewTransformer.java @@ -14,7 +14,7 @@ public class ViewTransformer { /** * transforms a given JsonNode to representation of view templates * view template indicates any new field or mask fields for transformation - * + * * @param viewTemplate * @param node * @return @@ -35,7 +35,7 @@ public JsonNode transform(ViewTemplate viewTemplate, JsonNode node) throws Excep ArrayNode resultArray = JsonNodeFactory.instance.arrayNode(); for (int i = 0; i < nodeAttrs.size(); i++) { - + JsonNode tNode = transformNode(viewTemplate, nodeAttrs.get(i)); resultArray.add(tNode); } @@ -43,7 +43,7 @@ public JsonNode transform(ViewTemplate viewTemplate, JsonNode node) throws Excep } else if (nodeAttrs.isObject()) { resultNode = transformNode(viewTemplate, nodeAttrs); - + } else { throw new IllegalArgumentException("Not a valid node for transformation, must be a object node or array node"); } @@ -56,7 +56,7 @@ public JsonNode transform(ViewTemplate viewTemplate, JsonNode node) throws Excep /** * Transforms a single node for given view template - * + * * @param viewTemplate * @param nodeAttrs * @return @@ -70,8 +70,8 @@ private JsonNode transformNode(ViewTemplate viewTemplate, JsonNode nodeAttrs) th if (functionStr != null) { String fdName = field.getFunctioName(); - FunctionDefinition funcDef = viewTemplate.getFunctionDefinition(fdName); - + FunctionDefinition funcDef = viewTemplate.getFunctionDefinition(fdName); + List actualValues = new ArrayList<>(); for (String oneArg : field.getArgNames()) { // Cut off the $ @@ -79,8 +79,8 @@ private JsonNode transformNode(ViewTemplate viewTemplate, JsonNode nodeAttrs) th actualValues.add(ValueType.getValue(nodeAttrs.get(oneArg.substring(1)))); } } - - IEvaluator evaluator = EvaluatorFactory.getInstance(funcDef, actualValues, new String[0]); + + IEvaluator evaluator = EvaluatorFactory.getInstance(funcDef, actualValues, field.getArgNames()); if (field.getDisplay()) { Object evaluatedValue = evaluator.evaluate(); if(evaluatedValue instanceof String){ @@ -89,16 +89,16 @@ private JsonNode transformNode(ViewTemplate viewTemplate, JsonNode nodeAttrs) th result.set(field.getTitle(), JsonNodeFactory.instance.pojoNode(evaluatedValue)); } } - // if display is set, show up the field in result + // if display is set, show up the field in result } else if (field.getDisplay()) { result.set(field.getTitle(), nodeAttrs.get(field.getName())); } } - - appendSignatures(viewTemplate, nodeAttrs,result); + + appendSignatures(viewTemplate, nodeAttrs,result); return result; } - + /** * Appends the signature array to the result as per the fields specified in view template file. * @@ -109,21 +109,21 @@ private JsonNode transformNode(ViewTemplate viewTemplate, JsonNode nodeAttrs) th * */ private JsonNode appendSignatures(ViewTemplate viewTemplate, JsonNode nodeAttrs, JsonNode resultNode) throws Exception { if(nodeAttrs.get("signatures")!=null) { - + ArrayNode sigArray = new ObjectMapper().createArrayNode(); - + for (JsonNode sigNode : nodeAttrs.get("signatures")) { JsonNode signatureField = sigNode.get("signatureFor"); - + if(signatureField != null){ //Adds the signatures of fields specified in view template file for (Field fieldTemp : viewTemplate.getFields()) { - if(signatureField.asText().endsWith("/"+fieldTemp.getName()) && fieldTemp.getDisplay()){ + if(signatureField.asText().endsWith("/"+fieldTemp.getName()) && fieldTemp.getDisplay()){ sigArray.add(sigNode); } } //Adds the subject signature - if(signatureField.asText().endsWith("/"+viewTemplate.getSubject())){ + if(signatureField.asText().endsWith("/"+viewTemplate.getSubject())){ sigArray.add(sigNode); } } @@ -134,4 +134,4 @@ private JsonNode appendSignatures(ViewTemplate viewTemplate, JsonNode nodeAttrs, } return resultNode; } -} \ No newline at end of file +} diff --git a/java/view-templates/src/test/java/dev/sunbirdrc/provider/RemovePathFunctionProviderTest.java b/java/view-templates/src/test/java/dev/sunbirdrc/provider/RemovePathFunctionProviderTest.java new file mode 100644 index 000000000..4f0d69f92 --- /dev/null +++ b/java/view-templates/src/test/java/dev/sunbirdrc/provider/RemovePathFunctionProviderTest.java @@ -0,0 +1,39 @@ +package dev.sunbirdrc.provider; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +public class RemovePathFunctionProviderTest { + + RemovePathFunctionProvider removePathFunctionProvider; + + @Before + public void setUp() throws Exception { + removePathFunctionProvider = new RemovePathFunctionProvider(); + } + + @Test + public void shouldRemovePathsFromJsonNode() throws JsonProcessingException { + JsonNode objectNode = new ObjectMapper().readTree("{\n" + + " \"details\": {\n" + + " \"name\": \"Test\",\n" + + " \"age\": 10\n" + + " }\n" + + "}"); + List actualValues = new ArrayList<>(); + actualValues.add(objectNode.get("details")); + + JsonNode updatedNode = removePathFunctionProvider.doAction(actualValues, new String[]{"$.details", "$.age"}); + JsonNode expected = new ObjectMapper().readTree("{\n" + + " \"name\": \"Test\"\n" + + "}"); + Assert.assertEquals(expected, updatedNode); + } +} diff --git a/java/view-templates/src/test/java/dev/sunbirdrc/views/FunctionExecutorTest.java b/java/view-templates/src/test/java/dev/sunbirdrc/views/FunctionExecutorTest.java index c69648120..188260c41 100644 --- a/java/view-templates/src/test/java/dev/sunbirdrc/views/FunctionExecutorTest.java +++ b/java/view-templates/src/test/java/dev/sunbirdrc/views/FunctionExecutorTest.java @@ -56,7 +56,7 @@ public void shouldGenerateRandomUUIDNumber() throws JsonProcessingException { "}", JsonNode.class); String functionCallStr = "#/functionDefinitions/mathOperation"; FunctionDefinition functionDefinition = new FunctionDefinition(); - functionDefinition.setProvider("org.example.provider.UUIDFunctionProvider"); + functionDefinition.setProvider("dev.sunbirdrc.provider.UUIDFunctionProvider"); JsonNode updatedNode = functionExecutor.execute(functionCallStr, functionDefinition, jsonNode); System.out.println(updatedNode); Assert.assertTrue(isValidUUID(updatedNode.get("output").asText())); @@ -71,4 +71,4 @@ boolean isValidUUID(String str) { } return UUID_REGEX_PATTERN.matcher(str).matches(); } -} \ No newline at end of file +} diff --git a/java/view-templates/src/test/java/dev/sunbirdrc/views/TestSampleProvider.java b/java/view-templates/src/test/java/dev/sunbirdrc/views/TestSampleProvider.java index 7b20d0641..19a6cbf80 100644 --- a/java/view-templates/src/test/java/dev/sunbirdrc/views/TestSampleProvider.java +++ b/java/view-templates/src/test/java/dev/sunbirdrc/views/TestSampleProvider.java @@ -19,6 +19,11 @@ public String doAction(List values) { return concat(values); } + @Override + public String doAction(List values, String[] paths) { + return doAction(values); + } + /** * simple concat for the values as string and comma(',') as seperator * @@ -36,4 +41,4 @@ public String concat(List args) { } } -} \ No newline at end of file +} diff --git a/java/view-templates/src/test/java/dev/sunbirdrc/views/ViewTransformerTest.java b/java/view-templates/src/test/java/dev/sunbirdrc/views/ViewTransformerTest.java index 9c2f3dbd2..3fbd9a2a8 100644 --- a/java/view-templates/src/test/java/dev/sunbirdrc/views/ViewTransformerTest.java +++ b/java/view-templates/src/test/java/dev/sunbirdrc/views/ViewTransformerTest.java @@ -16,14 +16,14 @@ public class ViewTransformerTest { private ViewTransformer transformer = new ViewTransformer(); - + @Test public void testTransformForPersonFunction() throws Exception{ ObjectNode personNode = getPerson(); ViewTemplate viewTemplate = getViewTemplatePerson("person_vt.json"); - JsonNode actualnode = transformer.transform(viewTemplate, personNode); + JsonNode actualnode = transformer.transform(viewTemplate, personNode); JsonNode expectedNode = new ObjectMapper().readTree("{\"Person\":{\"NAME\":\"Ram\",\"lastName\":\"Moorthy\",\"Name in passport\":\"Moorthy, Ram\",\"Name as in DL\":\"Ram : Moorthy\"}}"); assertEquals(expectedNode, actualnode); @@ -38,19 +38,19 @@ public void testTransformForMathVT() throws Exception{ ObjectNode node = (ObjectNode) new ObjectMapper().readTree(mathProblem); ViewTemplate viewTemplate = getViewTemplatePerson("mathVT1.json"); - JsonNode actualnode = transformer.transform(viewTemplate, node); - JsonNode expectedNode = new ObjectMapper().readTree("{\"Math\":{\"addend_A\":5,\"addend_B\":2,\"SUM\":7}}"); + JsonNode actualnode = transformer.transform(viewTemplate, node); + JsonNode expectedNode = new ObjectMapper().readTree("{\"Math\":{\"addend_A\":5,\"addend_B\":2,\"SUM\":7}}"); assertEquals(expectedNode.toString(), actualnode.toString()); } - - + + private ViewTemplate getViewTemplatePerson(String personJsonFileName) throws JsonProcessingException, IOException{ String viewTemplateJson = readFileContent(personJsonFileName); return new ObjectMapper().readValue(viewTemplateJson, ViewTemplate.class); } - + private ObjectNode getPerson() throws JsonProcessingException, IOException{ String personJson = "{\"Person\": " + " {\"nationalIdentifier\":\"nid823\"," + @@ -60,7 +60,7 @@ private ObjectNode getPerson() throws JsonProcessingException, IOException{ " \"dob\":\"1990-12-10\"}}"; return (ObjectNode) new ObjectMapper().readTree(personJson); } - + private static String readFileContent(String fileName) { InputStream in; try { @@ -82,4 +82,32 @@ private static String readFileContent(String fileName) { } return null; } + + @Test + public void shouldRemovePathUsingRemovePathProvider() throws Exception { + String personJson = "{\n" + + " \"Person\": " + + "{\n" + + " \"nationalIdentifier\": \"nid823\"," + + "\n" + + " \"name\": \"Ram\",\n" + + " \"lastName\": \"Moorthy\"," + + "\n" + + " \"gender\": \"MALE\"," + + "\n" + + " \"dob\": \"1990-12-10\",\n" + + " \"address\": {\n" + + " \"line\": \"1st stree\",\n" + + " \"city\": \"bangalore\"\n" + + " }\n" + + " }\n" + + "}"; + ObjectNode personNode = (ObjectNode) new ObjectMapper().readTree(personJson); + ViewTemplate viewTemplate = getViewTemplatePerson("90986382-4745-11ea-b77f-2e728ce88124.json"); + + JsonNode actualnode = transformer.transform(viewTemplate, personNode); + JsonNode expectedNode = new ObjectMapper().readTree("{\"Person\":{\"name\":\"Ram\",\"address\":{\"city\":\"bangalore\"}}}"); + + assertEquals(expectedNode.toPrettyString(), actualnode.toPrettyString()); + } } diff --git a/java/view-templates/src/test/resources/90986382-4745-11ea-b77f-2e728ce88124.json b/java/view-templates/src/test/resources/90986382-4745-11ea-b77f-2e728ce88124.json new file mode 100644 index 000000000..b4eda3019 --- /dev/null +++ b/java/view-templates/src/test/resources/90986382-4745-11ea-b77f-2e728ce88124.json @@ -0,0 +1,22 @@ +{ + "subject": "Reporter", + "fields": [ + { + "name": "name", + "title": "name", + "display": true + }, + { + "name": "address", + "title": "address", + "function": "#/functionDefinitions/removePath($address, $.line)", + "display": true + } + ], + "functionDefinitions": [ + { + "name" : "removePath", + "provider": "dev.sunbirdrc.provider.RemovePathFunctionProvider" + } + ] +} diff --git a/java/view-templates/src/test/resources/person_vt.json b/java/view-templates/src/test/resources/person_vt.json index 3c96d6a7b..b7d988917 100644 --- a/java/view-templates/src/test/resources/person_vt.json +++ b/java/view-templates/src/test/resources/person_vt.json @@ -43,7 +43,7 @@ }, { "name" : "userDefinedConcat", - "provider": "org.example.provider.SampleViewFunctionProvider", + "provider": "dev.sunbirdrc.provider.SampleViewFunctionProvider", "$comment" : "Complex operations that cannot be expressed easily in an in-line function definition can be implemented as a class. " }, { @@ -52,4 +52,4 @@ "$comment": "The functions defined in another template can be reused" } ] -} \ No newline at end of file +}