diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java index dfb4bbb0..810939ae 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3ProgramStageFhirRestAppTest.java @@ -380,4 +380,54 @@ public void updateObservation() throws Exception Assert.assertNotEquals( Boolean.TRUE, methodOutcome.getCreated() ); Assert.assertEquals( "http://localhost:" + localPort + "/fhir/dstu3/default/Observation/ps-deR4kl4mnf7-097d9ee0bdb344aeb9613b4584bad1db", methodOutcome.getId().toString() ); } + + @Test( expected = AuthenticationException.class ) + public void deleteObservationWithoutAuthorization() throws Exception + { + expectProgramStageMetadataRequests(); + + final IGenericClient client = createGenericClient(); + client.delete().resourceById( "Observation", "ps-deR4kl4mnf7-097d9ee0bdb344aeb9613b4584bad1db" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void deleteObservationInvalidAuthorization() throws Exception + { + expectProgramStageMetadataRequests(); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7.json?" + + "fields=deleted,event,orgUnit,program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) + .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.delete().resourceById( "Observation", "ps-deR4kl4mnf7-097d9ee0bdb344aeb9613b4584bad1db" ).execute(); + + userDhis2Server.verify(); + } + + @Test + public void deleteObservation() throws Exception + { + expectProgramStageMetadataRequests(); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7.json?" + + "fields=deleted,event,orgUnit,program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) + .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-event-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/ieR4nl4mufa.json" ) ) + .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-enrollment-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7/BnplxU2jGvX.json?mergeMode=MERGE" ) ).andExpect( method( HttpMethod.PUT ) ) + .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) + .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-70-update-delete.json", StandardCharsets.UTF_8 ) ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-70-update-response.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) + .headers( createDefaultHeaders() ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.delete().resourceById( "Observation", "ps-deR4kl4mnf7-097d9ee0bdb344aeb9613b4584bad1db" ).execute(); + + userDhis2Server.verify(); + } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3TrackedEntityInstanceFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3TrackedEntityInstanceFhirRestAppTest.java index 4cb8ae4c..d0a8eebb 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3TrackedEntityInstanceFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/dstu3/Dstu3TrackedEntityInstanceFhirRestAppTest.java @@ -421,4 +421,43 @@ public void updatePatient() throws Exception Assert.assertNotEquals( Boolean.TRUE, methodOutcome.getCreated() ); Assert.assertEquals( "http://localhost:" + localPort + "/fhir/dstu3/default/Patient/te-JeR2Ul4mZfx-5f9ebdc9852e4c8387ca795946aabc35", methodOutcome.getId().toString() ); } + + @Test( expected = AuthenticationException.class ) + public void deletePatientWithoutAuthorization() throws Exception + { + expectTrackedEntityMetadataRequests(); + + final IGenericClient client = createGenericClient(); + client.delete().resourceById( "Patient", "te-JeR2Ul4mZfx-5f9ebdc9852e4c8387ca795946aabc35" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void deletePatientInvalidAuthorization() throws Exception + { + expectTrackedEntityMetadataRequests(); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx" ) ) + .andExpect( method( HttpMethod.DELETE ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.delete().resourceById( "Patient", "te-JeR2Ul4mZfx-5f9ebdc9852e4c8387ca795946aabc35" ).execute(); + + userDhis2Server.verify(); + } + + @Test + public void deletePatient() throws Exception + { + expectTrackedEntityMetadataRequests(); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx" ) ) + .andExpect( method( HttpMethod.DELETE ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.delete().resourceById( "Patient", "te-JeR2Ul4mZfx-5f9ebdc9852e4c8387ca795946aabc35" ).execute(); + + userDhis2Server.verify(); + } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java index 5635be4c..2e6ba448 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4ProgramStageFhirRestAppTest.java @@ -413,4 +413,93 @@ public void updateObservation() throws Exception Assert.assertNotEquals( Boolean.TRUE, methodOutcome.getCreated() ); Assert.assertEquals( "http://localhost:" + localPort + "/fhir/r4/default/Observation/ps-deR4kl4mnf7-097d9ee0bdb344aeb9613b4584bad1db", methodOutcome.getId().toString() ); } + + @Test( expected = AuthenticationException.class ) + public void deleteObservationWithoutAuthorization() throws Exception + { + expectProgramStageMetadataRequests(); + + final IGenericClient client = createGenericClient(); + client.delete().resourceById( "Observation", "ps-deR4kl4mnf7-097d9ee0bdb344aeb9613b4584bad1db" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void deleteObservationInvalidAuthorization() throws Exception + { + expectProgramStageMetadataRequests(); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7.json?" + + "fields=deleted,event,orgUnit,program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) + .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.delete().resourceById( "Observation", "ps-deR4kl4mnf7-097d9ee0bdb344aeb9613b4584bad1db" ).execute(); + + userDhis2Server.verify(); + } + + @Test + public void deleteObservation() throws Exception + { + expectProgramStageMetadataRequests(); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7.json?" + + "fields=deleted,event,orgUnit,program,enrollment,trackedEntityInstance,programStage,status,eventDate,dueDate,coordinate,lastUpdated,dataValues%5BdataElement,value,providedElsewhere,lastUpdated,storedBy%5D" ) ) + .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-event-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/enrollments/ieR4nl4mufa.json" ) ) + .andExpect( method( HttpMethod.GET ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-enrollment-70-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7/BnplxU2jGvX.json?mergeMode=MERGE" ) ).andExpect( method( HttpMethod.PUT ) ) + .andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andExpect( content().contentTypeCompatibleWith( MediaType.APPLICATION_JSON ) ) + .andExpect( content().json( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-70-update-delete.json", StandardCharsets.UTF_8 ) ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/default-event-70-update-response.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) + .headers( createDefaultHeaders() ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.delete().resourceById( "Observation", "ps-deR4kl4mnf7-097d9ee0bdb344aeb9613b4584bad1db" ).execute(); + + userDhis2Server.verify(); + } + + @Test( expected = AuthenticationException.class ) + public void deleteEncounterWithoutAuthorization() throws Exception + { + expectProgramStageMetadataRequests(); + + final IGenericClient client = createGenericClient(); + client.delete().resourceById( "Encounter", "ps-deR4kl4mnf7-9d342f13aec146299d654f03fd0e848c" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void deleteEncounterInvalidAuthorization() throws Exception + { + expectProgramStageMetadataRequests(); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7" ) ) + .andExpect( method( HttpMethod.DELETE ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.delete().resourceById( "Encounter", "ps-deR4kl4mnf7-9d342f13aec146299d654f03fd0e848c" ).execute(); + + userDhis2Server.verify(); + } + + @Test + public void deleteEncounter() throws Exception + { + expectProgramStageMetadataRequests(); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/events/deR4kl4mnf7" ) ) + .andExpect( method( HttpMethod.DELETE ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.delete().resourceById( "Encounter", "ps-deR4kl4mnf7-9d342f13aec146299d654f03fd0e848c" ).execute(); + + userDhis2Server.verify(); + } } diff --git a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4TrackedEntityInstanceFhirRestAppTest.java b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4TrackedEntityInstanceFhirRestAppTest.java index 148a2593..d48d11c5 100644 --- a/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4TrackedEntityInstanceFhirRestAppTest.java +++ b/app/src/test/java/org/dhis2/fhir/adapter/fhir/server/r4/R4TrackedEntityInstanceFhirRestAppTest.java @@ -421,4 +421,43 @@ public void updatePatient() throws Exception Assert.assertNotEquals( Boolean.TRUE, methodOutcome.getCreated() ); Assert.assertEquals( "http://localhost:" + localPort + "/fhir/r4/default/Patient/te-JeR2Ul4mZfx-5f9ebdc9852e4c8387ca795946aabc35", methodOutcome.getId().toString() ); } + + @Test( expected = AuthenticationException.class ) + public void deletePatientWithoutAuthorization() throws Exception + { + expectTrackedEntityMetadataRequests(); + + final IGenericClient client = createGenericClient(); + client.delete().resourceById( "Patient", "te-JeR2Ul4mZfx-5f9ebdc9852e4c8387ca795946aabc35" ).execute(); + } + + @Test( expected = AuthenticationException.class ) + public void deletePatientInvalidAuthorization() throws Exception + { + expectTrackedEntityMetadataRequests(); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx" ) ) + .andExpect( method( HttpMethod.DELETE ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6aW52YWxpZF8x" ) ) + .andRespond( withStatus( HttpStatus.UNAUTHORIZED ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "invalid_1" ) ); + client.delete().resourceById( "Patient", "te-JeR2Ul4mZfx-5f9ebdc9852e4c8387ca795946aabc35" ).execute(); + + userDhis2Server.verify(); + } + + @Test + public void deletePatient() throws Exception + { + expectTrackedEntityMetadataRequests(); + userDhis2Server.expect( ExpectedCount.once(), requestTo( dhis2BaseUrl + "/api/" + dhis2ApiVersion + "/trackedEntityInstances/JeR2Ul4mZfx" ) ) + .andExpect( method( HttpMethod.DELETE ) ).andExpect( header( "Authorization", "Basic Zmhpcl9jbGllbnQ6Zmhpcl9jbGllbnRfMQ==" ) ) + .andRespond( withSuccess( IOUtils.resourceToString( "/org/dhis2/fhir/adapter/dhis/test/single-tei-15-get.json", StandardCharsets.UTF_8 ), MediaType.APPLICATION_JSON ) ); + + final IGenericClient client = createGenericClient(); + client.registerInterceptor( new BasicAuthInterceptor( "fhir_client", "fhir_client_1" ) ); + client.delete().resourceById( "Patient", "te-JeR2Ul4mZfx-5f9ebdc9852e4c8387ca795946aabc35" ).execute(); + + userDhis2Server.verify(); + } } diff --git a/app/src/test/resources/data.sql b/app/src/test/resources/data.sql index ca335953..1dfff467 100644 --- a/app/src/test/resources/data.sql +++ b/app/src/test/resources/data.sql @@ -1806,7 +1806,7 @@ UPDATE fhir_code_set_value SET preferred_export=true WHERE code_id='d308a6acad84 -- Encounter Child Programme, Birth INSERT INTO fhir_rule (id, version, name, description, enabled, evaluation_order, fhir_resource_type, dhis_resource_type, imp_enabled, transform_exp_script_id, fhir_create_enabled, fhir_update_enabled, exp_enabled, grouping) -VALUES ('9d342f13aec146299d654f03fd0e848c', 0, 'Child Programme Birth Encounter', NULL, TRUE, 1000, 'ENCOUNTER', 'PROGRAM_STAGE_EVENT', FALSE, '5eab76a90ff443b0a7d05a6e726ca80e', TRUE, TRUE, TRUE, TRUE); +VALUES ('9d342f13aec146299d654f03fd0e848c', 0, 'Child Programme Birth Encounter', NULL, TRUE, 1000, 'ENCOUNTER', 'PROGRAM_STAGE_EVENT', TRUE, '5eab76a90ff443b0a7d05a6e726ca80e', TRUE, TRUE, TRUE, TRUE); INSERT INTO fhir_program_stage_rule (id, program_stage_id) VALUES ('9d342f13aec146299d654f03fd0e848c', '4c074c85be494b9d89739e16b9615dad'); diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-70-update-delete.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-70-update-delete.json new file mode 100644 index 00000000..eb61d1fe --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-70-update-delete.json @@ -0,0 +1,14 @@ +{ + "status": "ACTIVE", + "dataValues": [ + { + "value": null, + "providedElsewhere": false, + "dataElement": "BnplxU2jGvX" + } + ], + "orgUnit": "ldXIdLNUNEp", + "program": "EPDyQuoRnXk", + "trackedEntityInstance": "JeR2Ul4mZfx", + "programStage": "MsWxkiY6tMS" +} \ No newline at end of file diff --git a/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-70-update-response.json b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-70-update-response.json new file mode 100644 index 00000000..fb4ea45f --- /dev/null +++ b/app/src/test/resources/org/dhis2/fhir/adapter/dhis/test/default-event-70-update-response.json @@ -0,0 +1,11 @@ +{ + "status": "OK", + "response": { + "importSummaries": [ + { + "status": "SUCCESS", + "reference": "deR4kl4mnf7" + } + ] + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/DhisResourceRepository.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/DhisResourceRepository.java index 06083408..5382123e 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/DhisResourceRepository.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/DhisResourceRepository.java @@ -52,4 +52,6 @@ public interface DhisResourceRepository @Nonnull DhisResource save( @Nonnull DhisResource resource ); + + boolean delete( @Nonnull DhisResource resource ); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java index 71ce24b6..7a9195da 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java @@ -148,6 +148,22 @@ public DhisResource save( @Nonnull DhisResource resource ) return resource; } + @Override + public boolean delete( @Nonnull DhisResource resource ) + { + switch ( resource.getResourceType() ) + { + case TRACKED_ENTITY: + return trackedEntityService.delete( resource.getId() ); + case ENROLLMENT: + throw new UnsupportedOperationException( "Deleting DHIS enrollment resources is nut supported currently." ); + case PROGRAM_STAGE_EVENT: + return eventService.delete( resource.getId() ); + default: + throw new AssertionError( "Unhandled DHIS resource type: " + resource.getResourceType() ); + } + } + private boolean saveTrackedEntityInstance( @Nonnull TrackedEntityInstance trackedEntityInstance ) { if ( trackedEntityInstance.isNewResource() || trackedEntityInstance.isModified() ) diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java index 1a4da3fb..a9f82764 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/Event.java @@ -109,6 +109,11 @@ public Event() super(); } + public Event( @Nonnull String id ) + { + this.id = id; + } + public Event( boolean newResource ) { this.newResource = newResource; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java index 95294695..64523bc2 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/EventService.java @@ -66,6 +66,8 @@ List find( @Nonnull String programId, @Nonnull String programStageId, @Nonnull Event createOrMinimalUpdate( @Nonnull Event event ); + boolean delete( @Nonnull String eventId ); + @Nonnull DhisResourceResult find( @Nonnull String programId, @Nonnull String programStageId, @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java index f31ed4ca..63f96a59 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/program/impl/EventServiceImpl.java @@ -114,6 +114,26 @@ public EventServiceImpl( @Nonnull @Qualifier( "userDhis2RestTemplate" ) RestTemp this.polledProgramRetriever = polledProgramRetriever; } + @HystrixCommand( ignoreExceptions = { DhisConflictException.class, UnauthorizedException.class } ) + @Override + public boolean delete( @Nonnull String eventId ) + { + Event instance; + try + { + restTemplate.delete( "/events/{id}", eventId ); + } + catch ( HttpClientErrorException e ) + { + if ( RestTemplateUtils.isNotFound( e ) ) + { + return false; + } + throw e; + } + return true; + } + @HystrixCommand( ignoreExceptions = { DhisConflictException.class, UnauthorizedException.class } ) @Nonnull @Override diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java index 052ec5c4..fd7709a0 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java @@ -82,6 +82,11 @@ public TrackedEntityInstance() super(); } + public TrackedEntityInstance( @Nonnull String id ) + { + this.id = id; + } + public TrackedEntityInstance( @Nonnull TrackedEntityType type, @Nullable String id, boolean newResource ) { this.typeId = type.getId(); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java index 4bca2f52..5f2e8f28 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityService.java @@ -68,6 +68,8 @@ Collection findByAttrValue( @Nonnull String typeId, @Nonnull TrackedEntityInstance createOrUpdate( @Nonnull TrackedEntityInstance trackedEntityInstance ); + boolean delete( @Nonnull String teiId ); + @Nonnull DhisResourceResult find( @Nonnull String trackedEntityTypeId, @Nonnull UriFilterApplier uriFilterApplier, int from, int max ); diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java index 59b6c051..c982388c 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityServiceImpl.java @@ -44,6 +44,7 @@ import org.dhis2.fhir.adapter.dhis.model.UriFilterApplier; import org.dhis2.fhir.adapter.dhis.sync.DhisLastUpdated; import org.dhis2.fhir.adapter.dhis.sync.StoredDhisResourceService; +import org.dhis2.fhir.adapter.dhis.tracker.program.Event; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.RequiredValueType; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; @@ -232,6 +233,26 @@ public TrackedEntityInstance createOrUpdate( @Nonnull TrackedEntityInstance trac return trackedEntityInstance.isNewResource() ? create( trackedEntityInstance ) : update( trackedEntityInstance ); } + @HystrixCommand( ignoreExceptions = { DhisConflictException.class, UnauthorizedException.class } ) + @Override + public boolean delete( @Nonnull String eventId ) + { + Event instance; + try + { + restTemplate.delete( "/trackedEntityInstances/{id}", eventId ); + } + catch ( HttpClientErrorException e ) + { + if ( RestTemplateUtils.isNotFound( e ) ) + { + return false; + } + throw e; + } + return true; + } + @HystrixCommand( ignoreExceptions = UnauthorizedException.class ) @Nonnull @Override diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java index 0fec2b88..80135ee5 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java @@ -60,6 +60,7 @@ import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; +import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisDeleteTransformOutcome; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformOutcome; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerRequest; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerService; @@ -174,8 +175,21 @@ public FhirRepositoryOperationOutcome save( @Nonnull FhirClientResource fhirClie @Transactional( propagation = Propagation.NOT_SUPPORTED ) public boolean delete( @Nonnull FhirClientResource fhirClientResource, @Nonnull DhisFhirResourceId dhisFhirResourceId ) { - // TODO implement deletion - return false; + final FhirToDhisDeleteTransformOutcome outcome = fhirToDhisTransformerService.delete( fhirClientResource, dhisFhirResourceId ); + + if ( outcome == null ) + { + return false; + } + + if ( outcome.isDelete() ) + { + return dhisResourceRepository.delete( outcome.getResource() ); + } + + dhisResourceRepository.save( outcome.getResource() ); + + return true; } @Nonnull @@ -185,6 +199,7 @@ protected Authorization createAuthorization( @Nonnull FhirClient fhirClient ) { throw new FatalTransformerException( "Unhandled DHIS2 authentication method: " + fhirClient.getDhisEndpoint().getAuthenticationMethod() ); } + return new Authorization( "Basic " + Base64.getEncoder().encodeToString( (fhirClient.getDhisEndpoint().getUsername() + ":" + fhirClient.getDhisEndpoint().getPassword()).getBytes( StandardCharsets.UTF_8 ) ) ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/FhirToDhisTransformerServiceImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/FhirToDhisTransformerServiceImpl.java index b23a78ff..663618c3 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/FhirToDhisTransformerServiceImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/FhirToDhisTransformerServiceImpl.java @@ -167,8 +167,25 @@ public FhirToDhisTransformerRequest createTransformerRequest( @Nonnull FhirReque @Override public FhirToDhisDeleteTransformOutcome delete( @Nonnull FhirClientResource fhirClientResource, @Nonnull DhisFhirResourceId dhisFhirResourceId ) throws TransformerException { - // TODO must be implemented - return null; + final RuleInfo ruleInfo = ruleRepository.findOneImpByDhisFhirInputData( + Objects.requireNonNull( fhirClientResource.getFhirResourceType() ), Objects.requireNonNull( dhisFhirResourceId.getType() ), dhisFhirResourceId.getRuleId() ).orElse( null ); + + if ( ruleInfo == null ) + { + return null; + } + + final FhirToDhisTransformer transformer = this.transformers.get( + new FhirVersionedValue<>( fhirClientResource.getFhirClient().getFhirVersion(), ruleInfo.getRule().getDhisResourceType() ) ); + + if ( transformer == null ) + { + throw new TransformerMappingException( "No transformer can be found for FHIR version " + + fhirClientResource.getFhirClient().getFhirVersion() + + " mapping of DHIS resource type " + ruleInfo.getRule().getDhisResourceType() ); + } + + return transformer.transformDeletionCasted( fhirClientResource, ruleInfo, dhisFhirResourceId ); } @Nullable diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java index fb57e9f2..84c77e7a 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/program/FhirToProgramStageTransformer.java @@ -33,6 +33,7 @@ import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.model.ReferenceType; +import org.dhis2.fhir.adapter.dhis.model.WritableDataValue; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnit; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.program.Enrollment; @@ -65,6 +66,7 @@ import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; import org.dhis2.fhir.adapter.fhir.transform.DhisDataExistsException; import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; +import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisDeleteTransformOutcome; @@ -262,7 +264,44 @@ public FhirToDhisTransformOutcome transform( @Nonnull FhirClientResource @Override public FhirToDhisDeleteTransformOutcome transformDeletion( @Nonnull FhirClientResource fhirClientResource, @Nonnull RuleInfo ruleInfo, @Nonnull DhisFhirResourceId dhisFhirResourceId ) throws TransformerException { - // TODO must be implemented + if ( ruleInfo.getRule().isGrouping() && ruleInfo.getDhisDataReferences().isEmpty() ) + { + return new FhirToDhisDeleteTransformOutcome<>( + ruleInfo.getRule(), new Event( dhisFhirResourceId.getId() ), true ); + } + else if ( !ruleInfo.getDhisDataReferences().isEmpty() ) + { + final Event event = getResourceById( dhisFhirResourceId.getId() ).orElse( null ); + if ( event != null ) + { + final Program program = programMetadataService.findProgramByReference( new Reference( event.getProgramId(), ReferenceType.ID ) ) + .orElseThrow( () -> new TransformerDataException( "Program " + event.getProgramId() + " of event " + event.getId() + " could not be found." ) ); + final ProgramStage programStage = program.getOptionalStage( ruleInfo.getRule().getProgramStage().getProgramStageReference() ).orElseThrow( () -> new TransformerMappingException( "Rule " + ruleInfo + " requires program stage \"" + + ruleInfo.getRule().getProgramStage().getProgramStageReference() + "\" that is not included in program \"" + ruleInfo.getRule().getProgramStage().getProgram().getName() + "\"." ) ); + boolean anyModified = false; + + for ( RuleDhisDataReference dataReference : ruleInfo.getDhisDataReferences() ) + { + final ProgramStageDataElement dataElement = programStage.getOptionalDataElement( dataReference.getDataReference() ).orElseThrow( () -> + new TransformerMappingException( "Event of program stage \"" + programStage.getName() + "\" of program \"" + + program.getName() + "\" does not include data element: " + dataReference ) ); + final WritableDataValue dataValue = event.getDataValue( dataElement.getElementId() ); + + if ( dataValue.getValue() != null ) + { + dataValue.setValue( null ); + dataValue.setModified(); + anyModified = true; + } + } + + if ( anyModified ) + { + return new FhirToDhisDeleteTransformOutcome<>( ruleInfo.getRule(), event, false ); + } + } + } + return null; } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java index 27ffefa6..97ffbfda 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/trackedentity/FhirToTrackedEntityTransformer.java @@ -177,8 +177,8 @@ public FhirToDhisTransformOutcome transform( @Nonnull Fhi @Override public FhirToDhisDeleteTransformOutcome transformDeletion( @Nonnull FhirClientResource fhirClientResource, @Nonnull RuleInfo ruleInfo, @Nonnull DhisFhirResourceId dhisFhirResourceId ) throws TransformerException { - // TODO must be implemented - return null; + return new FhirToDhisDeleteTransformOutcome<>( + ruleInfo.getRule(), new TrackedEntityInstance( dhisFhirResourceId.getId() ), true ); } protected boolean addScriptVariables( @Nonnull Map variables, @Nonnull RuleInfo ruleInfo ) throws TransformerException