Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: VSDSPUB-349: Add Gone status to soft deleted fragments #300

Merged
merged 25 commits into from
Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ce38427
Feature: VSDSPUB-349: Add reference from member to fragment
WLefever-Cegeka Nov 9, 2022
d7811ee
Feature: VSDSPUB-349: Add immutable timestamp
WLefever-Cegeka Nov 9, 2022
e779611
Feature: VSDSPUB-349: Add reference from member to fragment
WLefever-Cegeka Nov 9, 2022
49f7568
Feature: VSDSPUB-349: Add TimeBasedRetentionPolicy
WLefever-Cegeka Nov 9, 2022
a30941f
Feature: VSDSPUB-349: Add immutable timestamp
WLefever-Cegeka Nov 9, 2022
61c3dec
Feature: VSDSPUB-349: Add RetentionPolicyConfig
WLefever-Cegeka Nov 9, 2022
c89b29d
Feature: VSDSPUB-349: Add reference from member to fragment
WLefever-Cegeka Nov 9, 2022
cd8e9b9
Feature: VSDSPUB-349: Add TimeBasedRetentionPolicy
WLefever-Cegeka Nov 9, 2022
fd9e5c3
Feature: VSDSPUB-349: Add immutable timestamp
WLefever-Cegeka Nov 9, 2022
4f7cca3
Feature: VSDSPUB-349: Add RetentionPolicyConfig
WLefever-Cegeka Nov 9, 2022
7febd53
Feature: VSDSPUB-349: Add TreeNodeRemoverImpl
WLefever-Cegeka Nov 9, 2022
e8ad9bb
Feature: VSDSPUB-349: Add reference from member to fragment
WLefever-Cegeka Nov 9, 2022
e8774bd
Feature: VSDSPUB-349: Add TimeBasedRetentionPolicy
WLefever-Cegeka Nov 9, 2022
7b02e34
Feature: VSDSPUB-349: Add RetentionPolicyConfig
WLefever-Cegeka Nov 9, 2022
792fc59
Feature: VSDSPUB-349: Add TreeMemberRemoverImpl
WLefever-Cegeka Nov 14, 2022
5b0c9a9
Feature: VSDSPUB-349: Add reference from member to fragment
WLefever-Cegeka Nov 9, 2022
6fb05a8
Feature: VSDSPUB-349: Add TimeBasedRetentionPolicy
WLefever-Cegeka Nov 9, 2022
1a849cb
Feature: VSDSPUB-349: Add RetentionPolicyConfig
WLefever-Cegeka Nov 9, 2022
268d4bf
Feature: VSDSPUB-349: Add TreeMemberRemoverImpl
WLefever-Cegeka Nov 14, 2022
0c65d40
Feature: VSDSPUB-349: Add ParentUpdaterImpl
WLefever-Cegeka Nov 14, 2022
3805e65
Feature: VSDSPUB-349: Add reference from member to fragment
WLefever-Cegeka Nov 9, 2022
122b2b1
Feature: VSDSPUB-349: Add TimeBasedRetentionPolicy
WLefever-Cegeka Nov 9, 2022
2aabf79
Feature: VSDSPUB-349: Add RetentionPolicyConfig
WLefever-Cegeka Nov 9, 2022
1ccef6e
Feature: VSDSPUB-349: Add TreeMemberRemoverImpl
WLefever-Cegeka Nov 14, 2022
4ac3880
Feature: VSDSPUB-349: Add Gone status to soft deleted fragments
WLefever-Cegeka Nov 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package be.vlaanderen.informatievlaanderen.ldes.server.domain.exceptions;

public class DeletedFragmentException extends RuntimeException {
private final String fragmentId;

public DeletedFragmentException(String fragmentId) {
super();
this.fragmentId = fragmentId;
}

@Override
public String getMessage() {
return "Fragment with following identifier has been deleted: " + fragmentId;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,8 @@ public LdesFragment createChild(FragmentPair fragmentPair) {
public void removeRelation(TreeRelation treeRelation) {
relations.remove(treeRelation);
}

public boolean isSoftDeleted() {
return this.getFragmentInfo().getSoftDeleted();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Optional<LdesFragment> retrieveMutableFragment(String viewName,

Optional<LdesFragment> retrieveRootFragment(String viewName);

Stream<LdesFragment> retrieveImmutableFragmentsOfView(String viewName);
Stream<LdesFragment> retrieveNonDeletedImmutableFragmentsOfView(String viewName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got to say I find this rename more confusing.. I'd keep it at retrieveImmutableFragmentsOfView


Optional<LdesFragment> retrieveNonDeletedChildFragment(String viewName,
List<FragmentPair> fragmentPairList);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package be.vlaanderen.informatievlaanderen.ldes.server.domain.ldesfragment.services;

import be.vlaanderen.informatievlaanderen.ldes.server.domain.config.LdesConfig;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.exceptions.DeletedFragmentException;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.exceptions.MissingFragmentException;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.ldesfragment.entities.LdesFragment;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.ldesfragment.repository.LdesFragmentRepository;
Expand All @@ -22,12 +23,16 @@ public FragmentFetchServiceImpl(LdesConfig ldesConfig,

@Override
public LdesFragment getFragment(LdesFragmentRequest ldesFragmentRequest) {
return ldesFragmentRepository
LdesFragment ldesFragment = ldesFragmentRepository
.retrieveFragment(ldesFragmentRequest)
.orElseThrow(
() -> new MissingFragmentException(

ldesConfig.getHostName() + new FragmentInfo(ldesFragmentRequest.viewName(),
ldesFragmentRequest.fragmentPairs()).generateFragmentId()));
if (ldesFragment.isSoftDeleted())
throw new DeletedFragmentException(
ldesConfig.getHostName() + new FragmentInfo(ldesFragmentRequest.viewName(),
ldesFragmentRequest.fragmentPairs()).generateFragmentId());
return ldesFragment;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public TreeNodeRemoverImpl(LdesFragmentRepository ldesFragmentRepository,
public void removeTreeNodes() {
retentionPolicyMap.forEach((view, retentionPolicies) -> {
List<LdesFragment> ldesFragments = ldesFragmentRepository
.retrieveImmutableFragmentsOfView(view)
.retrieveNonDeletedImmutableFragmentsOfView(view)
.filter(ldesFragment -> retentionPolicies
.stream()
.allMatch(retentionPolicy -> retentionPolicy.matchesPolicy(ldesFragment)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public TreeMemberRemoverImpl(MemberReferencesRepository memberReferencesReposito
public void tryRemovingMember(String memberId) {
if (!memberReferencesRepository.hasMemberReferences(memberId)) {
memberRepository.deleteMember(memberId);
memberReferencesRepository.deleteMemberReference(memberId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public interface MemberReferencesRepository {

boolean hasMemberReferences(final String memberId);

void deleteMemberReference(String memberId);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package be.vlaanderen.informatievlaanderen.ldes.server.domain.ldesfragment.services;

import be.vlaanderen.informatievlaanderen.ldes.server.domain.config.LdesConfig;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.exceptions.DeletedFragmentException;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.exceptions.MissingFragmentException;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.ldesfragment.entities.LdesFragment;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.ldesfragment.repository.LdesFragmentRepository;
Expand All @@ -20,24 +21,18 @@

class FragmentFetchServiceImplTest {

private static final String COLLECTION_NAME = "exampleData";

private static final String VIEW_NAME = "view";

private static final String HOSTNAME = "http://localhost:8089/";
private static final String FRAGMENTATION_VALUE_1 = "2020-12-28T09:36:09.72Z";
private static final String FRAGMENT_ID_1 = HOSTNAME + "/" + COLLECTION_NAME + "/" + VIEW_NAME + "?generatedAtTime="
+
FRAGMENTATION_VALUE_1;
private static final FragmentInfo FRAGMENT_INFO = new FragmentInfo(
VIEW_NAME, List.of(new FragmentPair(GENERATED_AT_TIME, FRAGMENTATION_VALUE_1)));

private final LdesFragmentRepository ldesFragmentRepository = mock(LdesFragmentRepository.class);
private static FragmentInfo FRAGMENT_INFO;

private LdesFragmentRepository ldesFragmentRepository;
private FragmentFetchService fragmentFetchService;

@BeforeEach
void setUp() {
FRAGMENT_INFO = new FragmentInfo(
VIEW_NAME, List.of(new FragmentPair(GENERATED_AT_TIME, FRAGMENTATION_VALUE_1)));
ldesFragmentRepository = mock(LdesFragmentRepository.class);
LdesConfig ldesConfig = new LdesConfig();
ldesConfig.setHostName("http://localhost:8089");
fragmentFetchService = new FragmentFetchServiceImpl(ldesConfig,
Expand All @@ -58,6 +53,21 @@ void when_getFragment_WhenNoFragmentExists_ThenMissingFragmentExceptionIsThrown(
missingFragmentException.getMessage());
}

@Test
void when_getFragment_WhenFragmentIsDeleted_ThenDeletedFragmentExceptionIsThrown() {
LdesFragment ldesFragment = new LdesFragment(FRAGMENT_INFO);
ldesFragment.setSoftDeleted(true);
LdesFragmentRequest ldesFragmentRequest = new LdesFragmentRequest(VIEW_NAME,
List.of(new FragmentPair(GENERATED_AT_TIME, FRAGMENTATION_VALUE_1)));
when(ldesFragmentRepository.retrieveFragment(ldesFragmentRequest)).thenReturn(Optional.of(ldesFragment));

DeletedFragmentException deletedFragmentException = assertThrows(DeletedFragmentException.class,
() -> fragmentFetchService.getFragment(ldesFragmentRequest));
assertEquals(
"Fragment with following identifier has been deleted: http://localhost:8089/view?generatedAtTime=2020-12-28T09:36:09.72Z",
deletedFragmentException.getMessage());
}

@Test
void when_getFragment_WhenExactFragmentExists_ThenReturnThatFragment() {
LdesFragment ldesFragment = new LdesFragment(FRAGMENT_INFO);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ class TreeNodeRemoverImplTest {
void when_NodeIsImmutableAndSatisfiesRetentionPoliciesOfView_NodeCanBeSoftDeleted() {
LdesFragment notReadyToDeleteFragment = notReadyToDeleteFragment();
LdesFragment readyToDeleteFragment = readyToDeleteFragment();
when(fragmentRepository.retrieveImmutableFragmentsOfView("view"))
when(fragmentRepository.retrieveNonDeletedImmutableFragmentsOfView("view"))
.thenReturn(Stream.of(notReadyToDeleteFragment, readyToDeleteFragment));

treeNodeRemover.removeTreeNodes();

verify(fragmentRepository, times(1)).retrieveImmutableFragmentsOfView("view");
verify(fragmentRepository, times(1)).retrieveNonDeletedImmutableFragmentsOfView("view");
verify(fragmentRepository, times(1)).saveFragment(readyToDeleteFragment);
assertTrue(readyToDeleteFragment.getFragmentInfo().getSoftDeleted());
verifyNoMoreInteractions(fragmentRepository);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ void when_memberHasNoReferences_ItCanBeDeleted() {
treeMemberRemover.tryRemovingMember("memberId");

verify(memberRepository, times(1)).deleteMember("memberId");
verify(memberReferencesRepository, times(1)).deleteMemberReference("memberId");
}

@Test
Expand All @@ -32,6 +33,8 @@ void when_memberHasReferences_ItCanNotBeDeleted() {
treeMemberRemover.tryRemovingMember("memberId");

verifyNoInteractions(memberRepository);
verify(memberReferencesRepository, times(1)).hasMemberReferences("memberId");
verifyNoMoreInteractions(memberReferencesRepository);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ public Optional<LdesFragment> retrieveRootFragment(String viewName) {
}

@Override
public Stream<LdesFragment> retrieveImmutableFragmentsOfView(String viewName) {
public Stream<LdesFragment> retrieveNonDeletedImmutableFragmentsOfView(String viewName) {
return repository
.findAllByImmutableAndViewName(true, viewName)
.findAllByImmutableAndSoftDeletedAndViewName(true, false, viewName)
.stream()
.map(LdesFragmentEntity::toLdesFragment);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ public synchronized boolean hasMemberReferences(String memberId) {
.map(MemberReferencesEntity::hasMemberReferences)
.orElseThrow(() -> new MemberNotFoundException(memberId));
}

@Override
public synchronized void deleteMemberReference(String memberId) {
repository.deleteById(memberId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ Optional<LdesFragmentEntity> findLdesFragmentEntityByRootAndViewName(
List<LdesFragmentEntity> findAllByImmutableAndViewName(
Boolean immutable, String viewName);

List<LdesFragmentEntity> findAllByImmutableAndSoftDeletedAndViewName(Boolean immutable, Boolean softDeleted,
String viewName);

List<LdesFragmentEntity> findAllBySoftDeletedAndViewName(Boolean softDeleted, String viewName);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package be.vlaanderen.informatievlaanderen.ldes.server.rest.exceptionhandling;

import be.vlaanderen.informatievlaanderen.ldes.server.domain.exceptions.DeletedFragmentException;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.exceptions.MissingFragmentException;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.exceptions.RdfFormatException;
import org.springframework.http.HttpHeaders;
Expand All @@ -22,6 +23,14 @@ protected ResponseEntity<Object> handleMissingFragmentException(
new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}

@ExceptionHandler(value = { DeletedFragmentException.class })
protected ResponseEntity<Object> handleDeletedFragmentException(
RuntimeException ex, WebRequest request) {
String bodyOfResponse = ex.getMessage();
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.GONE, request);
}

@ExceptionHandler(value = { RdfFormatException.class })
protected ResponseEntity<Object> handleRdfFormatException(
RuntimeException ex, WebRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import be.vlaanderen.informatievlaanderen.ldes.server.domain.constants.RdfConstants;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.converter.PrefixAdder;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.converter.PrefixAdderImpl;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.exceptions.DeletedFragmentException;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.exceptions.MissingFragmentException;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.ldesfragment.entities.LdesFragment;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.ldesfragment.services.FragmentFetchService;
Expand Down Expand Up @@ -138,6 +139,21 @@ void when_GETRequestButMissingFragmentExceptionIsThrown_NotFoundIsReturned()
resultActions.andReturn().getResponse().getContentAsString());
}

@Test
void when_GETRequestButDeletedFragmentExceptionIsThrown_NotFoundIsReturned()
throws Exception {

LdesFragmentRequest ldesFragmentRequest = new LdesFragmentRequest(VIEW_NAME,
List.of());
when(fragmentFetchService.getFragment(ldesFragmentRequest))
.thenThrow(new DeletedFragmentException("fragmentId"));

ResultActions resultActions = mockMvc.perform(get("/view").accept("application/n-quads")).andDo(print())
.andExpect(status().isGone());
assertEquals("Fragment with following identifier has been deleted: fragmentId",
resultActions.andReturn().getResponse().getContentAsString());
}

@Test
@DisplayName("Requesting using another collection name returns 404")
void when_GETRequestIsPerformedOnOtherCollectionName_ResponseIs404() throws Exception {
Expand Down