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

Delete BreadcrumbTree rows when parent Model is deleted #351

Merged
merged 5 commits into from
Aug 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -42,6 +42,8 @@ import java.util.concurrent.TimeUnit
@SuppressFBWarnings('DCN_NULLPOINTER_EXCEPTION')
class Utils {

public static final int UUID_CHARACTER_LENGTH = 36

private Utils() {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- before mdm-core version 5.2.0, child BreadcrumbTrees were not deleted when parent models were deleted
-- clean up orphaned BreadcrumbTrees

CREATE TEMPORARY TABLE valid_breadcrumb_trees_temp AS
WITH RECURSIVE valid_breadcrumb_trees AS MATERIALIZED (
SELECT *
FROM core.breadcrumb_tree
WHERE top_breadcrumb_tree = TRUE
UNION ALL
SELECT c.*
FROM core.breadcrumb_tree c
INNER JOIN valid_breadcrumb_trees v
ON c.parent_id = v.id
)
SELECT *
FROM valid_breadcrumb_trees;

ALTER TABLE core.breadcrumb_tree DISABLE TRIGGER ALL;
DELETE FROM core.breadcrumb_tree;
ALTER TABLE core.breadcrumb_tree ENABLE TRIGGER ALL;

INSERT INTO core.breadcrumb_tree
SELECT *
FROM valid_breadcrumb_trees_temp;

CREATE INDEX breadcrumb_tree_tree_string_idx ON core.breadcrumb_tree (tree_string text_pattern_ops);
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,16 @@ class BreadcrumbTreeService {
breadcrumbTree.save()
log.trace('BT finalisation took {}', Utils.timeTaken(start))
}

void deleteAllByDomainIds(Set<UUID> domainIds) {
List<String> idPrefixesToDelete = domainIds.collect {it.toString() + '|'}

sessionFactory.currentSession
.createSQLQuery('DELETE FROM core.breadcrumb_tree WHERE SUBSTR(tree_string, 1, :id_length+1) IN :id_prefixes')
.setParameter('id_length', Utils.UUID_CHARACTER_LENGTH)
.setParameterList('id_prefixes', idPrefixesToDelete)
.executeUpdate()

log.trace('BreadcrumbTrees removed')
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -488,12 +488,8 @@ class DataModelService extends ModelService<DataModel> implements SummaryMetadat

log.trace('DataModels removed')

sessionFactory.currentSession
.createSQLQuery('DELETE FROM core.breadcrumb_tree WHERE domain_id IN :ids')
.setParameter('ids', idsToDelete)
.executeUpdate()

log.trace('Breadcrumb trees removed')
log.trace('Removing BreadcrumbTrees')
breadcrumbTreeService.deleteAllByDomainIds(idsToDelete)

GormUtils.enableDatabaseConstraints(sessionFactory as SessionFactoryImplementor)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import uk.ac.ox.softeng.maurodatamapper.core.async.AsyncJobService
import uk.ac.ox.softeng.maurodatamapper.core.container.Classifier
import uk.ac.ox.softeng.maurodatamapper.core.container.Folder
import uk.ac.ox.softeng.maurodatamapper.core.container.VersionedFolder
import uk.ac.ox.softeng.maurodatamapper.core.facet.BreadcrumbTree
import uk.ac.ox.softeng.maurodatamapper.core.facet.SemanticLink
import uk.ac.ox.softeng.maurodatamapper.core.facet.SemanticLinkType
import uk.ac.ox.softeng.maurodatamapper.core.facet.VersionLinkType
Expand All @@ -33,6 +34,7 @@ import uk.ac.ox.softeng.maurodatamapper.test.xml.XmlComparer
import uk.ac.ox.softeng.maurodatamapper.util.Utils
import uk.ac.ox.softeng.maurodatamapper.version.Version

import grails.gorm.transactions.Rollback
import grails.gorm.transactions.Transactional
import grails.testing.mixin.integration.Integration
import grails.testing.spock.RunOnce
Expand Down Expand Up @@ -3873,7 +3875,7 @@ class DataModelFunctionalSpec extends ResourceFunctionalSpec<DataModel> implemen
fileType : MimeType.JSON_API.name,
fileContents: loadTestFile('complexDataModel').toList()
],
asynchronous: true
asynchronous : true
])

then:
Expand Down Expand Up @@ -3972,6 +3974,40 @@ class DataModelFunctionalSpec extends ResourceFunctionalSpec<DataModel> implemen
verifyResponse NO_CONTENT, response
}

@Rollback
void 'DB : test permanent delete removes BreadcrumbTree'() {
given:
int breadcrumbTreeCount = BreadcrumbTree.count()
String id = createNewItem(validJson)
POST("$id/dataClasses", [label: 'Functional Test Data Class'])
verifyResponse CREATED, response
String dcId = responseBody().id
POST("$id/dataTypes", [label: 'Functional Test Data Type', domainType: 'PrimitiveType'])
verifyResponse CREATED, response
String dtId = responseBody().id
POST("$id/dataClasses/$dcId/dataElements", [label: 'Functional Test Data Element', dataType: dtId])
verifyResponse CREATED, response
String deId = responseBody().id

expect:
BreadcrumbTree.count() == breadcrumbTreeCount + 4
BreadcrumbTree.findByDomainId(id)
BreadcrumbTree.findByDomainId(dcId)
BreadcrumbTree.findByDomainId(dtId)
BreadcrumbTree.findByDomainId(deId)

when:
DELETE("$id?permanent=true")
verifyResponse NO_CONTENT, response

then:
BreadcrumbTree.count() == breadcrumbTreeCount
!BreadcrumbTree.findByDomainId(id)
!BreadcrumbTree.findByDomainId(dcId)
!BreadcrumbTree.findByDomainId(dtId)
!BreadcrumbTree.findByDomainId(deId)
}

void 'H01 : test getting simple DataModel hierarchy'() {
given:
POST('import/uk.ac.ox.softeng.maurodatamapper.datamodel.provider.importer/DataModelJsonImporterService/3.2', [
Expand Down Expand Up @@ -5499,7 +5535,7 @@ class DataModelFunctionalSpec extends ResourceFunctionalSpec<DataModel> implemen
when: 'Get the intersectionMany between complex and [target, target2]'
POST("/${source.dataModelId}/intersectsMany", [
targetDataModelIds: [target.dataModelId, target2.dataModelId],
dataElementIds: [source.contentClass.ele1.id, source.contentClass.element2.id, source.parentClass.childClass.grandchild.id]
dataElementIds : [source.contentClass.ele1.id, source.contentClass.element2.id, source.parentClass.childClass.grandchild.id]
])

then: 'The response is OK with two results'
Expand Down Expand Up @@ -5554,7 +5590,7 @@ class DataModelFunctionalSpec extends ResourceFunctionalSpec<DataModel> implemen
when: 'Get the intersectionMany between complex and [target, target2]'
POST("/${source.dataModelId}/intersectsMany", [
targetDataModelIds: [target.dataModelId, target2.dataModelId],
dataElementIds: [source.contentClass.ele1.id, source.contentClass.element2.id, source.parentClass.childClass.grandchild.id]
dataElementIds : [source.contentClass.ele1.id, source.contentClass.element2.id, source.parentClass.childClass.grandchild.id]
])

then: 'The response is OK with two results'
Expand Down Expand Up @@ -5643,7 +5679,7 @@ class DataModelFunctionalSpec extends ResourceFunctionalSpec<DataModel> implemen
when: 'Get the intersectionMany between complex and [target, target2]'
POST("/${source.dataModelId}/intersectsMany", [
targetDataModelIds: [target.dataModelId, target2.dataModelId],
dataElementIds: [source.contentClass.ele1.id, source.contentClass.element2.id, source.parentClass.childClass.grandchild.id]
dataElementIds : [source.contentClass.ele1.id, source.contentClass.element2.id, source.parentClass.childClass.grandchild.id]
])

then: 'The response is OK with two results'
Expand Down Expand Up @@ -5672,7 +5708,7 @@ class DataModelFunctionalSpec extends ResourceFunctionalSpec<DataModel> implemen
when: 'Get the intersectionMany between complex and [target, target2]'
POST("/${source.dataModelId}/intersectsMany", [
targetDataModelIds: [target.dataModelId, target2.dataModelId],
dataElementIds: [source.contentClass.ele1.id, source.contentClass.element2.id, source.parentClass.childClass.grandchild.id]
dataElementIds : [source.contentClass.ele1.id, source.contentClass.element2.id, source.parentClass.childClass.grandchild.id]
])

then: 'The response is OK with two results'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,12 +364,8 @@ class TerminologyService extends ModelService<Terminology> {

log.trace('Terminologies removed')

sessionFactory.currentSession
.createSQLQuery('DELETE FROM core.breadcrumb_tree WHERE domain_id IN :ids')
.setParameter('ids', idsToDelete)
.executeUpdate()

log.trace('Breadcrumb trees removed')
log.trace('Removing BreadcrumbTrees')
breadcrumbTreeService.deleteAllByDomainIds(idsToDelete)

GormUtils.enableDatabaseConstraints(sessionFactory as SessionFactoryImplementor)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package uk.ac.ox.softeng.maurodatamapper.terminology
import uk.ac.ox.softeng.maurodatamapper.core.bootstrap.StandardEmailAddress
import uk.ac.ox.softeng.maurodatamapper.core.container.Classifier
import uk.ac.ox.softeng.maurodatamapper.core.container.Folder
import uk.ac.ox.softeng.maurodatamapper.core.facet.BreadcrumbTree
import uk.ac.ox.softeng.maurodatamapper.core.facet.SemanticLinkType
import uk.ac.ox.softeng.maurodatamapper.core.facet.VersionLinkType
import uk.ac.ox.softeng.maurodatamapper.core.gorm.constraint.callable.VersionAwareConstraints
Expand All @@ -33,6 +34,7 @@ import uk.ac.ox.softeng.maurodatamapper.test.functional.merge.TestMergeData
import uk.ac.ox.softeng.maurodatamapper.test.xml.XmlComparer
import uk.ac.ox.softeng.maurodatamapper.version.Version

import grails.gorm.transactions.Rollback
import grails.gorm.transactions.Transactional
import grails.testing.mixin.integration.Integration
import grails.testing.spock.RunOnce
Expand Down Expand Up @@ -1355,6 +1357,30 @@ class TerminologyFunctionalSpec extends ResourceFunctionalSpec<Terminology> impl
verifyResponse NO_CONTENT, response
}

@Rollback
void 'DB : test permanent delete removes BreadcrumbTree'() {
given:
int breadcrumbTreeCount = BreadcrumbTree.count()
String id = createNewItem(validJson)
POST("$id/terms", [code: 'Test DB', description: 'Functional Test Description', definition: 'Functional Test Definition'])
verifyResponse CREATED, response
String termId = responseBody().id

expect:
BreadcrumbTree.count() == breadcrumbTreeCount + 2
BreadcrumbTree.findByDomainId(id)
BreadcrumbTree.findByDomainId(termId)

when:
DELETE("$id?permanent=true")
verifyResponse NO_CONTENT, response

then:
BreadcrumbTree.count() == breadcrumbTreeCount
!BreadcrumbTree.findByDomainId(id)
!BreadcrumbTree.findByDomainId(termId)
}

void 'EX01 : test getting Terminology exporters'() {
when:
GET('providers/exporters', STRING_ARG)
Expand Down