diff --git a/mdm-core/grails-app/utils/uk/ac/ox/softeng/maurodatamapper/core/rest/transport/model/CopyInformation.groovy b/mdm-core/grails-app/utils/uk/ac/ox/softeng/maurodatamapper/core/rest/transport/model/CopyInformation.groovy index 132d8c21eb..ef267de2a9 100644 --- a/mdm-core/grails-app/utils/uk/ac/ox/softeng/maurodatamapper/core/rest/transport/model/CopyInformation.groovy +++ b/mdm-core/grails-app/utils/uk/ac/ox/softeng/maurodatamapper/core/rest/transport/model/CopyInformation.groovy @@ -26,6 +26,7 @@ import groovy.util.logging.Slf4j class CopyInformation implements Validateable { String copyLabel + boolean copyIndex = false Map>> preloadedFacets = [:] diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/model/ModelItemService.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/model/ModelItemService.groovy index 66e3ed41c5..74831ee705 100644 --- a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/model/ModelItemService.groovy +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/model/ModelItemService.groovy @@ -20,6 +20,8 @@ package uk.ac.ox.softeng.maurodatamapper.core.model import uk.ac.ox.softeng.maurodatamapper.api.exception.ApiNotYetImplementedException import uk.ac.ox.softeng.maurodatamapper.core.container.Classifier import uk.ac.ox.softeng.maurodatamapper.core.facet.Metadata +import uk.ac.ox.softeng.maurodatamapper.core.rest.transport.model.CopyInformation +import uk.ac.ox.softeng.maurodatamapper.security.User import uk.ac.ox.softeng.maurodatamapper.security.UserSecurityPolicyManager import uk.ac.ox.softeng.maurodatamapper.util.Utils @@ -50,6 +52,15 @@ abstract class ModelItemService extends CatalogueItemServic throw new ApiNotYetImplementedException('MIS03', "copy [for ModelItem ${getDomainClass().simpleName}]") } + K copyModelItemInformation(K original, K copy, User copier, UserSecurityPolicyManager userSecurityPolicyManager, + CopyInformation copyInformation = null) { + copy = super.copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) + if (copyInformation?.copyIndex) { + copy.idx = original.idx + } + copy + } + boolean isModelItemInSameModelOrInFinalisedModel(K modelItem, K otherModelItem) { otherModelItem.model.id == modelItem.model.id || modelItem.model.finalised } diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/traits/domain/IndexedSiblingAware.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/traits/domain/IndexedSiblingAware.groovy index fbe1555719..c6d075c926 100644 --- a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/traits/domain/IndexedSiblingAware.groovy +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/traits/domain/IndexedSiblingAware.groovy @@ -123,7 +123,7 @@ trait IndexedSiblingAware { if (mi.hasChanged('idx') && mi.id) { mi.save(flush: false, validate: false) } - log.trace('After >> EV {} has idx {} (Dirty: {})', mi.label, mi.idx, mi.isDirty()) + log.trace('After >> MI {} has idx {} (Dirty: {})', mi.label, mi.idx, mi.isDirty()) } } diff --git a/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelService.groovy b/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelService.groovy index d83a83ab7f..c273d81c9a 100644 --- a/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelService.groovy +++ b/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelService.groovy @@ -735,16 +735,16 @@ class DataModelService extends ModelService implements SummaryMetadat List dataTypes = DataType.byDataModelId(original.id).join('classifiers').list() List rootDataClasses = DataClass.byRootDataClassOfDataModelId(original.id).join('classifiers').list() - CopyInformation dataClassCache = cacheFacetInformationForCopy(rootDataClasses.collect {it.id}) - CopyInformation dataTypeCache = cacheFacetInformationForCopy(dataTypes.collect {it.id}) + CopyInformation dataClassCache = cacheFacetInformationForCopy(rootDataClasses.collect {it.id}, new CopyInformation(copyIndex: true)) + CopyInformation dataTypeCache = cacheFacetInformationForCopy(dataTypes.collect {it.id}, new CopyInformation(copyIndex: true)) // Copy all the datatypes - dataTypes.each {dt -> + dataTypes.sort().each {dt -> dataTypeService.copyDataType(copy, dt, copier, userSecurityPolicyManager, copySummaryMetadata, dataTypeCache) } // Copy all the dataclasses (this will also match up the reference types) - rootDataClasses.each {dc -> + rootDataClasses.sort().each {dc -> dataClassService.copyDataClass(copy, dc, copier, userSecurityPolicyManager, null, copySummaryMetadata, dataClassCache) } log.debug('Copy of datamodel took {}', Utils.timeTaken(start)) diff --git a/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassService.groovy b/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassService.groovy index ad5a21f5c1..68a58957b7 100644 --- a/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassService.groovy +++ b/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassService.groovy @@ -97,21 +97,21 @@ class DataClassService extends ModelItemService implements SummaryMet saveDataTypesUsedInDataClass(domain) } } - if(args.deepSave){ + if (args.deepSave) { this.updateDataClassHierarchyAfterInsert(args, domain) } super.save(args, domain) } - void updateDataClassHierarchyAfterInsert(Map args, DataClass dataClass){ - if(dataClass.dataElements){ - dataClass.dataElements.each{de -> + void updateDataClassHierarchyAfterInsert(Map args, DataClass dataClass) { + if (dataClass.dataElements) { + dataClass.dataElements.each {de -> dataElementService.save(de) } } - if(dataClass.dataClasses){ - dataClass.dataClasses.each{dc -> + if (dataClass.dataClasses) { + dataClass.dataClasses.each {dc -> updateDataClassHierarchyAfterInsert(args, dc) } } @@ -122,13 +122,13 @@ class DataClassService extends ModelItemService implements SummaryMet Set dataTypes = extractAllUsedNewOrDirtyDataTypes(dataClass) log.debug('{} new or dirty used datatypes inside dataclass', dataTypes.size()) // Validation should have already been done - dataTypes.each { it.skipValidation(true) } + dataTypes.each {it.skipValidation(true)} dataTypeService.saveAll(dataTypes, false) } Set extractAllUsedNewOrDirtyDataTypes(DataClass dataClass) { - Set dataTypes = dataClass.dataElements.collect { it.dataType }.findAll { it.isDirty() || !it.ident() }.toSet() - dataTypes.addAll(dataClass.dataClasses.collect { extractAllUsedNewOrDirtyDataTypes(it) }.flatten().toSet() as Collection) + Set dataTypes = dataClass.dataElements.collect {it.dataType}.findAll {it.isDirty() || !it.ident()}.toSet() + dataTypes.addAll(dataClass.dataClasses.collect {extractAllUsedNewOrDirtyDataTypes(it)}.flatten().toSet() as Collection) dataTypes } @@ -152,7 +152,7 @@ class DataClassService extends ModelItemService implements SummaryMet dataClass.save(flush: false, validate: false) // Recurse through the hierarchy - dataClasses?.each { dc -> + dataClasses?.each {dc -> saveDataClassHierarchy(dc) } @@ -229,10 +229,10 @@ class DataClassService extends ModelItemService implements SummaryMet DataClass checkFacetsAfterImportingCatalogueItem(DataClass catalogueItem) { super.checkFacetsAfterImportingCatalogueItem(catalogueItem) if (catalogueItem.summaryMetadata) { - catalogueItem.summaryMetadata.each { sm -> + catalogueItem.summaryMetadata.each {sm -> sm.multiFacetAwareItemId = catalogueItem.id sm.createdBy = sm.createdBy ?: catalogueItem.createdBy - sm.summaryMetadataReports.each { smr -> + sm.summaryMetadataReports.each {smr -> smr.createdBy = catalogueItem.createdBy } } @@ -249,16 +249,16 @@ class DataClassService extends ModelItemService implements SummaryMet } Collection saveAllAndGetDataElements(Collection dataClasses) { - if(!dataClasses) return [] + if (!dataClasses) return [] - List classifiers = dataClasses.collectMany { it.classifiers ?: [] } as List + List classifiers = dataClasses.collectMany {it.classifiers ?: []} as List if (classifiers) { log.trace('Saving {} classifiers') classifierService.saveAll(classifiers) } - Collection alreadySaved = dataClasses.findAll { it.ident() && it.isDirty() } - Collection notSaved = dataClasses.findAll { !it.ident() } + Collection alreadySaved = dataClasses.findAll {it.ident() && it.isDirty()} + Collection notSaved = dataClasses.findAll {!it.ident()} Collection dataElements = [] @@ -273,10 +273,10 @@ class DataClassService extends ModelItemService implements SummaryMet int count = 0 // Find all DCs which are either top level or have their parent DC already saved - Collection parentIsSaved = notSaved.findAll { !it.parentDataClass || it.parentDataClass.id } + Collection parentIsSaved = notSaved.findAll {!it.parentDataClass || it.parentDataClass.id} log.trace('Ready to save on first run {}', parentIsSaved.size()) while (parentIsSaved) { - parentIsSaved.each { dc -> + parentIsSaved.each {dc -> dataElements.addAll dc.dataElements ?: [] dc.dataClasses?.clear() @@ -296,7 +296,7 @@ class DataClassService extends ModelItemService implements SummaryMet batch.clear() // Find all DCs which have a saved parent DC notSaved.removeAll(parentIsSaved) - parentIsSaved = notSaved.findAll { it.parentDataClass && it.parentDataClass.id } + parentIsSaved = notSaved.findAll {it.parentDataClass && it.parentDataClass.id} log.trace('Ready to save on subsequent run {}', parentIsSaved.size()) } } @@ -308,29 +308,29 @@ class DataClassService extends ModelItemService implements SummaryMet removeReferenceTypes(dataClass) dataClass.breadcrumbTree.removeFromParent() dataClass.dataModel.removeFromDataClasses(dataClass) - dataClass.dataClasses?.each { removeAssociations(it) } + dataClass.dataClasses?.each {removeAssociations(it)} } private void removeSemanticLinks(DataClass dataClass) { List semanticLinks = semanticLinkService.findAllByMultiFacetAwareItemId(dataClass.id) - semanticLinks.each { semanticLinkService.delete(it) } + semanticLinks.each {semanticLinkService.delete(it)} } private void removeReferenceTypes(DataClass dataClass) { List referenceTypes = new ArrayList<>(dataClass.referenceTypes.findAll()) - referenceTypes.each { dataTypeService.delete(it) } + referenceTypes.each {dataTypeService.delete(it)} } private void removeAllDataElementsWithNoLabel(DataClass dataClass) { - List dataElements = new ArrayList<>(dataClass.dataElements.findAll { !it.label }) - dataElements.each { dataElementService.delete(it) } + List dataElements = new ArrayList<>(dataClass.dataElements.findAll {!it.label}) + dataElements.each {dataElementService.delete(it)} } private void removeAllDataElementsWithSameLabel(DataClass dataClass) { if (dataClass.dataElements) { - Map> identicalDataElements = dataClass.dataElements.groupBy { it.label }.findAll { it.value.size() > 1 } - identicalDataElements.each { label, dataElements -> + Map> identicalDataElements = dataClass.dataElements.groupBy {it.label}.findAll {it.value.size() > 1} + identicalDataElements.each {label, dataElements -> for (int i = 1; i < dataElements.size(); i++) { dataElementService.delete(dataElements[i]) } @@ -340,8 +340,8 @@ class DataClassService extends ModelItemService implements SummaryMet private void ensureChildDataClassesHaveUniqueNames(DataClass dataClass) { if (dataClass.dataClasses) { - dataClass.dataClasses.groupBy { it.label }.findAll { it.value.size() > 1 }.each { label, dataClasses -> - dataClasses.eachWithIndex { DataClass child, int i -> + dataClass.dataClasses.groupBy {it.label}.findAll {it.value.size() > 1}.each {label, dataClasses -> + dataClasses.eachWithIndex {DataClass child, int i -> child.label = "${child.label}-$i" } } @@ -351,10 +351,10 @@ class DataClassService extends ModelItemService implements SummaryMet private void collapseReferenceTypes(DataClass dataClass) { if (!dataClass.referenceTypes || dataClass.referenceTypes.size() == 1) return DataModel dataModel = dataClass.dataModel - Map> labelGroupedReferenceTypes = dataClass.referenceTypes.groupBy { it.label } + Map> labelGroupedReferenceTypes = dataClass.referenceTypes.groupBy {it.label} - labelGroupedReferenceTypes.findAll { it.value.size() > 1 }.each { label, labelReferenceTypes -> - Map> dmGrouped = labelReferenceTypes.groupBy { it.dataModel ? 'dataModel' : 'noDataModel' } + labelGroupedReferenceTypes.findAll {it.value.size() > 1}.each {label, labelReferenceTypes -> + Map> dmGrouped = labelReferenceTypes.groupBy {it.dataModel ? 'dataModel' : 'noDataModel'} // There will be only 1 datamodel owned type as we've already merged datamodel owned datatypes if (dmGrouped.dataModel) { @@ -371,11 +371,11 @@ class DataClassService extends ModelItemService implements SummaryMet private void setCreatedBy(User creator, DataClass dataClass) { dataClass.createdBy = creator.emailAddress - dataClass.dataClasses?.each { dc -> + dataClass.dataClasses?.each {dc -> setCreatedBy(creator, dc) } - dataClass.dataElements?.each { de -> + dataClass.dataElements?.each {de -> de.createdBy = creator.emailAddress } } @@ -387,13 +387,13 @@ class DataClassService extends ModelItemService implements SummaryMet checkFacetsAfterImportingCatalogueItem(dataClass) if (dataClass.dataClasses) { dataClass.fullSortOfChildren(dataClass.dataClasses) - dataClass.dataClasses.each { dc -> + dataClass.dataClasses.each {dc -> checkImportedDataClassAssociations(importingUser, dataModel, dc, matchDataTypes) } } if (dataClass.dataElements) { dataClass.fullSortOfChildren(dataClass.dataElements) - dataClass.dataElements.each { de -> + dataClass.dataElements.each {de -> de.createdBy = importingUser.emailAddress de.checkPath() dataElementService.checkFacetsAfterImportingCatalogueItem(de) @@ -403,7 +403,7 @@ class DataClassService extends ModelItemService implements SummaryMet } DataClass findSameLabelTree(DataModel dataModel, DataClass searchFor) { - dataModel.dataClasses.find { hasSameLabelTree(it, searchFor) } + dataModel.dataClasses.find {hasSameLabelTree(it, searchFor)} } private boolean hasSameLabelTree(DataClass left, DataClass right) { @@ -638,11 +638,11 @@ WHERE } DataClass findDataClass(DataModel dataModel, String label) { - dataModel.dataClasses.find { !it.parentDataClass && it.label == label.trim() } + dataModel.dataClasses.find {!it.parentDataClass && it.label == label.trim()} } DataClass findDataClass(DataClass parentDataClass, String label) { - parentDataClass.dataClasses.find { it.label == label.trim() } + parentDataClass.dataClasses.find {it.label == label.trim()} } /** @@ -680,7 +680,7 @@ WHERE maxMultiplicity: sourceDataClass.maxMultiplicity ) - targetDataClass = copyCatalogueItemInformation(sourceDataClass, targetDataClass, user, userSecurityPolicyManager, false, null) + targetDataClass = copyModelItemInformation(sourceDataClass, targetDataClass, user, userSecurityPolicyManager, false, null) targetDataModel.addToDataClasses(targetDataClass) if (parentDataClassInTarget) { @@ -771,7 +771,7 @@ WHERE maxMultiplicity: original.maxMultiplicity ) - copy = copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, copySummaryMetadata, copyInformation) + copy = copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copySummaryMetadata, copyInformation) setCatalogueItemRefinesCatalogueItem(copy, original, copier) copiedDataModel.addToDataClasses(copy) @@ -785,8 +785,8 @@ WHERE List referenceTypes = ReferenceType.by().eq('referenceClass.id', original.id).list() - referenceTypes.each { refType -> - ReferenceType referenceType = copiedDataModel.referenceTypes.find { it.label == refType.label } + referenceTypes.sort().each {refType -> + ReferenceType referenceType = copiedDataModel.referenceTypes.find {it.label == refType.label} if (!referenceType) { referenceType = new ReferenceType(createdBy: copier.emailAddress, label: refType.label) copiedDataModel.addToDataTypes(referenceType) @@ -797,15 +797,15 @@ WHERE copy.dataClasses = [] List dataClasses = DataClass.byParentDataClassId(original.id).join('classifiers').list() - CopyInformation dataClassCache = cacheFacetInformationForCopy(dataClasses.collect { it.id }) - dataClasses.each { child -> + CopyInformation dataClassCache = cacheFacetInformationForCopy(dataClasses.collect {it.id}, new CopyInformation(copyIndex: true)) + dataClasses.sort().each {child -> copyDataClass(copiedDataModel, child, copier, userSecurityPolicyManager, copy, copySummaryMetadata, dataClassCache) } copy.dataElements = [] List dataElements = DataElement.byDataClassId(original.id).join('classifiers').list() - CopyInformation dataElementCache = cacheFacetInformationForCopy(dataElements.collect { it.id }) - dataElements.each { element -> + CopyInformation dataElementCache = cacheFacetInformationForCopy(dataElements.collect {it.id}, new CopyInformation(copyIndex: true)) + dataElements.sort().each {element -> copy.addToDataElements( dataElementService .copyDataElement(copiedDataModel, element, copier, userSecurityPolicyManager, copySummaryMetadata, dataElementCache)) @@ -814,13 +814,13 @@ WHERE copy } - DataClass copyCatalogueItemInformation(DataClass original, - DataClass copy, - User copier, - UserSecurityPolicyManager userSecurityPolicyManager, - boolean copySummaryMetadata, CopyInformation copyInformation) { + DataClass copyModelItemInformation(DataClass original, + DataClass copy, + User copier, + UserSecurityPolicyManager userSecurityPolicyManager, + boolean copySummaryMetadata, CopyInformation copyInformation) { - copy = super.copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) + copy = super.copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) if (copySummaryMetadata) { copy = copySummaryMetadataFromOriginal(original, copy, copier, copyInformation) } @@ -828,11 +828,11 @@ WHERE } @Override - DataClass copyCatalogueItemInformation(DataClass original, - DataClass copy, - User copier, - UserSecurityPolicyManager userSecurityPolicyManager, CopyInformation copyInformation = null) { - copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, false, copyInformation) + DataClass copyModelItemInformation(DataClass original, + DataClass copy, + User copier, + UserSecurityPolicyManager userSecurityPolicyManager, CopyInformation copyInformation = null) { + copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, false, copyInformation) } void matchUpAndAddMissingReferenceTypeClasses(DataModel copiedDataModel, DataModel originalDataModel, User copier, @@ -841,7 +841,7 @@ WHERE if (!emptyReferenceTypes) return log.debug('Found {} empty reference types', emptyReferenceTypes.size()) // Copy all the missing reference classes - emptyReferenceTypes.each { rt -> + emptyReferenceTypes.each {rt -> ReferenceType ort = originalDataModel.findDataTypeByLabel(rt.label) as ReferenceType String originalDataClassPath = buildPath(ort.referenceClass) @@ -859,7 +859,7 @@ WHERE } private Set findAllEmptyReferenceTypes(DataModel dataModel) { - dataModel.referenceTypes.findAll { !(it as ReferenceType).referenceClass } as Set + dataModel.referenceTypes.findAll {!(it as ReferenceType).referenceClass} as Set } String buildPath(DataClass dataClass) { @@ -904,7 +904,7 @@ WHERE @Override List findAllReadableByClassifier(UserSecurityPolicyManager userSecurityPolicyManager, Classifier classifier) { - findAllByClassifier(classifier).findAll { userSecurityPolicyManager.userCanReadSecuredResourceId(DataModel, it.model.id) } + findAllByClassifier(classifier).findAll {userSecurityPolicyManager.userCanReadSecuredResourceId(DataModel, it.model.id)} } @Override @@ -944,10 +944,10 @@ WHERE List alreadyExistingLinks = semanticLinkService.findAllBySourceMultiFacetAwareItemIdInListAndTargetMultiFacetAwareItemIdInListAndLinkType( dataClasses*.id, fromDataClasses*.id, SemanticLinkType.IS_FROM) - dataClasses.each { de -> - fromDataClasses.each { fde -> + dataClasses.each {de -> + fromDataClasses.each {fde -> // If no link already exists then add a new one - if (!alreadyExistingLinks.any { it.multiFacetAwareItemId == de.id && it.targetMultiFacetAwareItemId == fde.id }) { + if (!alreadyExistingLinks.any {it.multiFacetAwareItemId == de.id && it.targetMultiFacetAwareItemId == fde.id}) { setDataClassIsFromDataClass(de, fde, user) } } @@ -1021,27 +1021,27 @@ WHERE @Override void propagateContentsInformation(DataClass catalogueItem, DataClass previousVersionCatalogueItem) { - previousVersionCatalogueItem.dataClasses.each { previousChildDataClass -> - DataClass childDataClass = catalogueItem.dataClasses.find { it.label == previousChildDataClass.label } + previousVersionCatalogueItem.dataClasses.each {previousChildDataClass -> + DataClass childDataClass = catalogueItem.dataClasses.find {it.label == previousChildDataClass.label} if (childDataClass) { propagateDataFromPreviousVersion(childDataClass, previousChildDataClass) } } - previousVersionCatalogueItem.dataElements.each { previousDataElement -> - DataElement dataElement = catalogueItem.dataElements.find { it.label == previousDataElement.label } + previousVersionCatalogueItem.dataElements.each {previousDataElement -> + DataElement dataElement = catalogueItem.dataElements.find {it.label == previousDataElement.label} if (dataElement) { dataElementService.propagateDataFromPreviousVersion(dataElement, previousDataElement) } } - previousVersionCatalogueItem.summaryMetadata.each { previousSummaryMetadata -> - if (catalogueItem.summaryMetadata.any { it.label == previousSummaryMetadata.label }) return + previousVersionCatalogueItem.summaryMetadata.each {previousSummaryMetadata -> + if (catalogueItem.summaryMetadata.any {it.label == previousSummaryMetadata.label}) return SummaryMetadata summaryMetadata = new SummaryMetadata(label: previousSummaryMetadata.label, description: previousSummaryMetadata.description, summaryMetadataType: previousSummaryMetadata.summaryMetadataType) - previousSummaryMetadata.summaryMetadataReports.each { previousSummaryMetadataReport -> + previousSummaryMetadata.summaryMetadataReports.each {previousSummaryMetadataReport -> summaryMetadata.addToSummaryMetadataReports(reportDate: previousSummaryMetadataReport.reportDate, reportValue: previousSummaryMetadataReport.reportValue, createdBy: previousSummaryMetadataReport.createdBy @@ -1086,8 +1086,8 @@ WHERE throw new ApiInternalException("DCS04", "Source Data Class does not exist") DataClass targetDataClass = parentDataClassInTarget ? - parentDataClassInTarget.getDataClasses().find { it.label == dataClassNode.identifier } : - targetDataModel.getChildDataClasses().find { it.label == dataClassNode.identifier } + parentDataClassInTarget.getDataClasses().find {it.label == dataClassNode.identifier} : + targetDataModel.getChildDataClasses().find {it.label == dataClassNode.identifier} if (!targetDataClass) { //Create it @@ -1096,8 +1096,7 @@ WHERE maxMultiplicity: sourceDataClass.maxMultiplicity ) - targetDataClass = copyCatalogueItemInformation(sourceDataClass, targetDataClass, userSecurityPolicyManager.user, - userSecurityPolicyManager, false, null) + targetDataClass = copyModelItemInformation(sourceDataClass, targetDataClass, userSecurityPolicyManager.user, userSecurityPolicyManager) targetDataModel.addToDataClasses(targetDataClass) if (parentDataClassInTarget) { diff --git a/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataElementService.groovy b/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataElementService.groovy index 40929b8f1a..5cc7a08444 100644 --- a/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataElementService.groovy +++ b/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataElementService.groovy @@ -380,7 +380,7 @@ WHERE (de.dataClass.id = :dataClassId OR idc.id = :dataClassId)''', 'de', filter DataElement copy = new DataElement(minMultiplicity: original.minMultiplicity, maxMultiplicity: original.maxMultiplicity) - copy = copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, copySummaryMetadata, copyInformation) + copy = copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copySummaryMetadata, copyInformation) setCatalogueItemRefinesCatalogueItem(copy, original, copier) DataType dataType = copiedDataModel.findDataTypeByLabel(original.dataType.label) @@ -396,13 +396,13 @@ WHERE (de.dataClass.id = :dataClassId OR idc.id = :dataClassId)''', 'de', filter copy } - DataElement copyCatalogueItemInformation(DataElement original, + DataElement copyModelItemInformation(DataElement original, DataElement copy, User copier, UserSecurityPolicyManager userSecurityPolicyManager, boolean copySummaryMetadata, CopyInformation copyInformation) { - copy = super.copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) + copy = super.copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) if (copySummaryMetadata) { copySummaryMetadataFromOriginal(original, copy, copier, copyInformation) } @@ -410,12 +410,12 @@ WHERE (de.dataClass.id = :dataClassId OR idc.id = :dataClassId)''', 'de', filter } @Override - DataElement copyCatalogueItemInformation(DataElement original, + DataElement copyModelItemInformation(DataElement original, DataElement copy, User copier, UserSecurityPolicyManager userSecurityPolicyManager, CopyInformation copyInformation) { - copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, false, copyInformation) + copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, false, copyInformation) } DataElementSimilarityResult findAllSimilarDataElementsInDataModel(DataModel dataModelToSearch, DataElement dataElementToCompare, maxResults = 5) { diff --git a/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/DataTypeService.groovy b/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/DataTypeService.groovy index c333e5f6e1..83a7b75b22 100644 --- a/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/DataTypeService.groovy +++ b/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/DataTypeService.groovy @@ -97,7 +97,7 @@ class DataTypeService extends ModelItemService implements DefaultDataT dataType.breadcrumbTree.removeFromParent() List dataElements = dataElementService.findAllByDataType(dataType) - dataElements.each { dataElementService.delete(it) } + dataElements.each {dataElementService.delete(it)} switch (dataType.domainType) { case DataType.PRIMITIVE_DOMAIN_TYPE: @@ -189,13 +189,13 @@ class DataTypeService extends ModelItemService implements DefaultDataT enumerationTypeService.propagateContentsInformation(catalogueItem as EnumerationType, previousVersionCatalogueItem as EnumerationType) } - previousVersionCatalogueItem.summaryMetadata.each { previousSummaryMetadata -> - if (catalogueItem.summaryMetadata.any { it.label == previousSummaryMetadata.label }) return + previousVersionCatalogueItem.summaryMetadata.each {previousSummaryMetadata -> + if (catalogueItem.summaryMetadata.any {it.label == previousSummaryMetadata.label}) return SummaryMetadata summaryMetadata = new SummaryMetadata(label: previousSummaryMetadata.label, description: previousSummaryMetadata.description, summaryMetadataType: previousSummaryMetadata.summaryMetadataType) - previousSummaryMetadata.summaryMetadataReports.each { previousSummaryMetadataReport -> + previousSummaryMetadata.summaryMetadataReports.each {previousSummaryMetadataReport -> summaryMetadata.addToSummaryMetadataReports(reportDate: previousSummaryMetadataReport.reportDate, reportValue: previousSummaryMetadataReport.reportValue, createdBy: previousSummaryMetadataReport.createdBy @@ -234,7 +234,7 @@ class DataTypeService extends ModelItemService implements DefaultDataT new PrimitiveType(label: 'Timestamp', description: 'A timestamp'), new PrimitiveType(label: 'Boolean', description: 'A true or false value'), new PrimitiveType(label: 'Duration', description: 'A time period in arbitrary units') - ].collect { new DefaultDataType(it) } + ].collect {new DefaultDataType(it)} } @Override @@ -273,7 +273,7 @@ class DataTypeService extends ModelItemService implements DefaultDataT super.checkBreadcrumbTreeAfterSavingCatalogueItem(dataType) if (dataType.instanceOf(EnumerationType)) { - dataType.enumerationValues.each { enumerationValue -> + dataType.enumerationValues.each {enumerationValue -> super.checkBreadcrumbTreeAfterSavingCatalogueItem(enumerationValue) } } @@ -282,16 +282,16 @@ class DataTypeService extends ModelItemService implements DefaultDataT @Override void checkBreadcrumbTreeAfterSavingCatalogueItems(Collection dataTypes) { super.checkBreadcrumbTreeAfterSavingCatalogueItems(dataTypes) - super.checkBreadcrumbTreeAfterSavingCatalogueItems(dataTypes.findAll{it.instanceOf(EnumerationType)}.collectMany {it.enumerationValues}) + super.checkBreadcrumbTreeAfterSavingCatalogueItems(dataTypes.findAll {it.instanceOf(EnumerationType)}.collectMany {it.enumerationValues}) } @Override DataType checkFacetsAfterImportingCatalogueItem(DataType catalogueItem) { if (catalogueItem.summaryMetadata) { - catalogueItem.summaryMetadata.each { sm -> + catalogueItem.summaryMetadata.each {sm -> sm.multiFacetAwareItemId = catalogueItem.id sm.createdBy = sm.createdBy ?: catalogueItem.createdBy - sm.summaryMetadataReports.each { smr -> + sm.summaryMetadataReports.each {smr -> smr.createdBy = catalogueItem.createdBy } } @@ -314,7 +314,7 @@ class DataTypeService extends ModelItemService implements DefaultDataT if (dataType.instanceOf(EnumerationType)) { EnumerationType enumerationType = (dataType as EnumerationType) enumerationType.fullSortOfChildren(enumerationType.enumerationValues) - enumerationType.enumerationValues.each { ev -> + enumerationType.enumerationValues.each {ev -> ev.createdBy = importingUser.emailAddress ev.checkPath() } @@ -339,8 +339,8 @@ class DataTypeService extends ModelItemService implements DefaultDataT } void matchReferenceClasses(DataModel dataModel, Collection referenceTypes, Collection bindingMaps = []) { - referenceTypes.sort { it.label }.each { rdt -> - Map dataTypeBindingMap = bindingMaps.find { it.label == rdt.label } ?: [:] + referenceTypes.sort {it.label}.each {rdt -> + Map dataTypeBindingMap = bindingMaps.find {it.label == rdt.label} ?: [:] Map refClassBindingMap = dataTypeBindingMap.referenceClass ?: [:] matchReferenceClass(dataModel, rdt, refClassBindingMap) } @@ -359,7 +359,7 @@ class DataTypeService extends ModelItemService implements DefaultDataT else { log. trace('No referenceClass could be found to match label tree for {}, attempting no label tree', referenceType.referenceClass.label) - def possibles = dataModel.dataClasses.findAll { it.label == referenceType.referenceClass.label } + def possibles = dataModel.dataClasses.findAll {it.label == referenceType.referenceClass.label} if (possibles.size() == 1) { log.trace('Single possible referenceClass found, safely using') possibles.first().addToReferenceTypes(referenceType) @@ -373,7 +373,7 @@ class DataTypeService extends ModelItemService implements DefaultDataT } } else { log.trace('Making best guess for matching reference class as no path nor bound class') - DataClass dataClass = dataModel.dataClasses.find { it.label == bindingMap.referenceClass.label } + DataClass dataClass = dataModel.dataClasses.find {it.label == bindingMap.referenceClass.label} if (dataClass) dataClass.addToReferenceTypes(referenceType) } } @@ -386,9 +386,9 @@ class DataTypeService extends ModelItemService implements DefaultDataT DataType copyDataType(DataModel copiedDataModel, DataType original, User copier, UserSecurityPolicyManager userSecurityPolicyManager, boolean copySummaryMetadata = false, CopyInformation copyInformation = new CopyInformation()) { - DataType copy = createNewDataTypeFromOriginal(original) + DataType copy = createNewDataTypeFromOriginal(copiedDataModel, original, userSecurityPolicyManager) - copy = copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, copySummaryMetadata, copyInformation) + copy = copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copySummaryMetadata, copyInformation) setCatalogueItemRefinesCatalogueItem(copy, original, copier) copiedDataModel.addToDataTypes(copy) @@ -396,7 +396,7 @@ class DataTypeService extends ModelItemService implements DefaultDataT copy } - DataType createNewDataTypeFromOriginal(DataType original) { + DataType createNewDataTypeFromOriginal(DataModel copiedDataModel, DataType original, UserSecurityPolicyManager userSecurityPolicyManager) { DataType copy String domainType = original.domainType @@ -406,8 +406,10 @@ class DataTypeService extends ModelItemService implements DefaultDataT break case DataType.ENUMERATION_DOMAIN_TYPE: copy = new EnumerationType() - original.enumerationValues.each { ev -> - copy.addToEnumerationValues(key: ev.key, value: ev.value, category: ev.category) + CopyInformation copyInformation = new CopyInformation(copyIndex: true) + original.enumerationValues.sort().each {ev -> + copy.addToEnumerationValues( + enumerationValueService.copyEnumerationValue(copiedDataModel, ev, copy, userSecurityPolicyManager.user, userSecurityPolicyManager, copyInformation)) } break case DataType.REFERENCE_DOMAIN_TYPE: @@ -424,13 +426,13 @@ class DataTypeService extends ModelItemService implements DefaultDataT copy } - DataType copyCatalogueItemInformation(DataType original, - DataType copy, - User copier, - UserSecurityPolicyManager userSecurityPolicyManager, - boolean copySummaryMetadata, - copyInformation) { - copy = super.copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) + DataType copyModelItemInformation(DataType original, + DataType copy, + User copier, + UserSecurityPolicyManager userSecurityPolicyManager, + boolean copySummaryMetadata, + copyInformation) { + copy = super.copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) if (copySummaryMetadata) { copy = copySummaryMetadataFromOriginal(original, copy, copier, copyInformation) } @@ -438,11 +440,11 @@ class DataTypeService extends ModelItemService implements DefaultDataT } @Override - DataType copyCatalogueItemInformation(DataType original, - DataType copy, - User copier, - UserSecurityPolicyManager userSecurityPolicyManager, CopyInformation copyInformation) { - copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, false, copyInformation) + DataType copyModelItemInformation(DataType original, + DataType copy, + User copier, + UserSecurityPolicyManager userSecurityPolicyManager, CopyInformation copyInformation) { + copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, false, copyInformation) } DataModel addDefaultListOfDataTypesToDataModel(DataModel dataModel, List defaultDataTypes) { @@ -479,7 +481,7 @@ class DataTypeService extends ModelItemService implements DefaultDataT } void mergeDataTypes(DataType keep, DataType replace) { - replace.dataElements?.each { de -> + replace.dataElements?.each {de -> keep.addToDataElements(de) } List mds = [] diff --git a/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/enumeration/EnumerationValueService.groovy b/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/enumeration/EnumerationValueService.groovy index 8b69c907ce..624e4b3306 100644 --- a/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/enumeration/EnumerationValueService.groovy +++ b/mdm-plugin-datamodel/grails-app/services/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/enumeration/EnumerationValueService.groovy @@ -21,6 +21,7 @@ import uk.ac.ox.softeng.maurodatamapper.core.container.Classifier import uk.ac.ox.softeng.maurodatamapper.core.model.CatalogueItem import uk.ac.ox.softeng.maurodatamapper.core.model.Model import uk.ac.ox.softeng.maurodatamapper.core.model.ModelItemService +import uk.ac.ox.softeng.maurodatamapper.core.rest.transport.model.CopyInformation import uk.ac.ox.softeng.maurodatamapper.datamodel.DataModel import uk.ac.ox.softeng.maurodatamapper.datamodel.DataModelService import uk.ac.ox.softeng.maurodatamapper.datamodel.facet.SummaryMetadataService @@ -181,18 +182,15 @@ class EnumerationValueService extends ModelItemService impleme } EnumerationValue copyEnumerationValue(DataModel copiedDataModel, EnumerationValue original, EnumerationType enumerationTypeToCopyInto, - User copier, - UserSecurityPolicyManager userSecurityPolicyManager) { - EnumerationValue copy = new EnumerationValue(key: original.key, - value: original.value) + User copier, UserSecurityPolicyManager userSecurityPolicyManager, CopyInformation copyInformation = null) { + EnumerationValue copy = new EnumerationValue(key: original.key, value: original.value, category: original.category) - copy = copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager) + copy = copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) setCatalogueItemRefinesCatalogueItem(copy, original, copier) EnumerationType enumerationType = enumerationTypeToCopyInto ?: copiedDataModel.findEnumerationTypeByLabel(original.enumerationType.label) enumerationType.addToEnumerationValues(copy) copy - } List findAllByDataModelId(Serializable dataModelId) { diff --git a/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelFunctionalSpec.groovy b/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelFunctionalSpec.groovy index 168bf5d7a8..5194b4ddf4 100644 --- a/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelFunctionalSpec.groovy +++ b/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelFunctionalSpec.groovy @@ -908,7 +908,147 @@ class DataModelFunctionalSpec extends ResourceFunctionalSpec implemen cleanUpData() } - void 'VB05 : test finding common ancestor of two datamodels'() { + void 'VB05 : test creating a main branch model version of a DataModel with ordered DataClasses, DataElements and EnumerationValues'() { + given: + String originalModelId = createNewItem(validJson) + + for (int i in 1..5) { + POST("$originalModelId/dataClasses", [label: 'Root Data Class ' + i, idx: i - 1]) + verifyResponse CREATED, response + } + String originalParentClassId = responseBody().id + + for (int i in 1..5) { + POST("$originalModelId/dataClasses/$originalParentClassId/dataClasses", [label: 'Child Data Class ' + i, idx: i]) + verifyResponse CREATED, response + } + + Map enumerationTypeData = [ + label : 'Functional Test Enumeration Type', + domainType : 'EnumerationType', + enumerationValues: (1..5).collect {i -> + [ + key : 'Key ' + i, + value: 'Value ' + i, + index: i - 1 + ] + } + ] + POST("$originalModelId/dataTypes", enumerationTypeData) + verifyResponse CREATED, response + String originalEnumerationTypeId = responseBody().id + + for (int i in 1..5) { + POST("$originalModelId/dataClasses/$originalParentClassId/dataElements", + [label: 'Data Element ' + i, dataType: originalEnumerationTypeId, idx: i - 1]) + verifyResponse CREATED, response + } + + when: + GET("$originalModelId/dataClasses") + + then: + verifyResponse OK, response + responseBody().items.size() == 5 + responseBody().items.eachWithIndex{dc, i -> + assert dc.domainType == 'DataClass' + assert dc.label.startsWith('Root Data Class') && dc.label.endsWith((i + 1).toString()) + } + + when: + GET("$originalModelId/dataClasses/$originalParentClassId/dataClasses") + + then: + verifyResponse OK, response + responseBody().items.size() == 5 + responseBody().items.eachWithIndex {dc, i -> + assert dc.domainType == 'DataClass' + assert dc.label.startsWith('Child Data Class') && dc.label.endsWith((i + 1).toString()) + } + + when: + GET("$originalModelId/dataClasses/$originalParentClassId/dataElements") + + then: + verifyResponse OK, response + responseBody().items.eachWithIndex {de, i -> + assert de.domainType == 'DataElement' + assert de.label.startsWith('Data Element') && de.label.endsWith((i + 1).toString()) + } + + when: + GET("$originalModelId/dataTypes/$originalEnumerationTypeId") + + then: + verifyResponse OK, response + responseBody().domainType == 'EnumerationType' + responseBody().enumerationValues.every {ev -> + ev.key.startsWith('Key') && ev.key.endsWith((ev.index + 1).toString()) && + ev.value.startsWith('Value') && ev.value.endsWith((ev.index + 1).toString()) + } + + when: + PUT("$originalModelId/finalise", [versionChangeType: 'Major']) + verifyResponse OK, response + PUT("$originalModelId/newBranchModelVersion", [:]) + + then: + verifyResponse CREATED, response + String newModelId = responseBody().id + + when: + GET("$newModelId/dataClasses") + + then: + verifyResponse OK, response + responseBody().items.size() == 5 + responseBody().items.eachWithIndex{dc, i -> + assert dc.domainType == 'DataClass' + assert dc.label.startsWith('Root Data Class') && dc.label.endsWith((i + 1).toString()) + } + String newParentClassId = responseBody().items.last().id + + when: + GET("$newModelId/dataClasses/$newParentClassId/dataClasses") + + then: + verifyResponse OK, response + responseBody().items.size() == 5 + responseBody().items.eachWithIndex {dc, i -> + assert dc.domainType == 'DataClass' + assert dc.label.startsWith('Child Data Class') && dc.label.endsWith((i + 1).toString()) + } + + when: + GET("$newModelId/dataClasses/$newParentClassId/dataElements") + + then: + verifyResponse OK, response + responseBody().items.eachWithIndex {de, i -> + assert de.domainType == 'DataElement' + assert de.label.startsWith('Data Element') && de.label.endsWith((i + 1).toString()) + } + + when: + GET("$newModelId/dataTypes") + verifyResponse OK, response + String newEnumerationTypeId = responseBody().items[0].id + GET("$newModelId/dataTypes/$newEnumerationTypeId") + + then: + verifyResponse OK, response + responseBody().domainType == 'EnumerationType' + responseBody().enumerationValues.every {ev -> + ev.key.startsWith('Key') && ev.key.endsWith((ev.index + 1).toString()) && + ev.value.startsWith('Value') && ev.value.endsWith((ev.index + 1).toString()) + } + + cleanup: + cleanUpData(originalModelId) + cleanUpData(newModelId) + } + + void 'VB06 : test finding common ancestor of two datamodels'() { given: String id = createNewItem(validJson) PUT("$id/finalise", [versionChangeType: 'Major']) @@ -986,7 +1126,7 @@ class DataModelFunctionalSpec extends ResourceFunctionalSpec implemen cleanUpData(id) } - void 'VB06 : test finding latest finalised model of a datamodel'() { + void 'VB07 : test finding latest finalised model of a datamodel'() { /* id (finalised) -- expectedId (finalised) -- latestDraftId (draft) \_ newBranchId (draft) @@ -1032,7 +1172,7 @@ class DataModelFunctionalSpec extends ResourceFunctionalSpec implemen cleanUpData(id) } - void 'VB07 : test finding latest model version of a datamodel'() { + void 'VB08 : test finding latest model version of a datamodel'() { /* id (finalised) -- expectedId (finalised) -- latestDraftId (draft) \_ newBranchId (draft) diff --git a/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelServiceIntegrationSpec.groovy b/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelServiceIntegrationSpec.groovy index 60cb1f09a4..f8a20af6a6 100644 --- a/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelServiceIntegrationSpec.groovy +++ b/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/DataModelServiceIntegrationSpec.groovy @@ -37,8 +37,10 @@ import uk.ac.ox.softeng.maurodatamapper.datamodel.item.DataClass import uk.ac.ox.softeng.maurodatamapper.datamodel.item.DataClassService import uk.ac.ox.softeng.maurodatamapper.datamodel.item.DataElement import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.DataType +import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.EnumerationType import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.PrimitiveType import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.ReferenceType +import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.enumeration.EnumerationValue import uk.ac.ox.softeng.maurodatamapper.datamodel.similarity.DataElementSimilarityResult import uk.ac.ox.softeng.maurodatamapper.datamodel.test.BaseDataModelIntegrationSpec import uk.ac.ox.softeng.maurodatamapper.path.Path @@ -95,14 +97,15 @@ class DataModelServiceIntegrationSpec extends BaseDataModelIntegrationSpec { } void setupDataModelWithSummaryMetadata() { - DataModel dataModel = new DataModel(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Data Model having Summary Metadata', type: DataModelType.DATA_ASSET, folder: testFolder, - authority: testAuthority) + DataModel dataModel = new DataModel(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Data Model having Summary Metadata', type: DataModelType.DATA_ASSET, + folder: testFolder, + authority: testAuthority) checkAndSave(dataModel) // Add summary metadata with report to data model SummaryMetadata dataModelSummaryMetadata = new SummaryMetadata(label: 'Data Model Summary Metadata', createdBy: editor.emailAddress, - summaryMetadataType: SummaryMetadataType.MAP) + summaryMetadataType: SummaryMetadataType.MAP) SummaryMetadataReport dataModelSummaryMetadataReport = new SummaryMetadataReport( reportDate: OffsetDateTime.now(), reportValue: new JsonBuilder([A: 1, B: 2]).toString(), @@ -116,7 +119,7 @@ class DataModelServiceIntegrationSpec extends BaseDataModelIntegrationSpec { dataModel.addToDataTypes(dataType) checkAndSave(dataModel) SummaryMetadata dataTypeSummaryMetadata = new SummaryMetadata(label: 'Data Type Summary Metadata', createdBy: editor.emailAddress, - summaryMetadataType: SummaryMetadataType.MAP) + summaryMetadataType: SummaryMetadataType.MAP) SummaryMetadataReport dataTypeSummaryMetadataReport = new SummaryMetadataReport( reportDate: OffsetDateTime.now(), reportValue: new JsonBuilder([A: 1, B: 2]).toString(), @@ -129,7 +132,7 @@ class DataModelServiceIntegrationSpec extends BaseDataModelIntegrationSpec { DataClass dataClass = new DataClass(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Data Class having Summary Metadata', dataModel: dataModel) checkAndSave(dataClass) SummaryMetadata dataClassSummaryMetadata = new SummaryMetadata(label: 'Data Class Summary Metadata', createdBy: editor.emailAddress, - summaryMetadataType: SummaryMetadataType.MAP) + summaryMetadataType: SummaryMetadataType.MAP) SummaryMetadataReport dataClassSummaryMetadataReport = new SummaryMetadataReport( reportDate: OffsetDateTime.now(), reportValue: new JsonBuilder([A: 1, B: 2]).toString(), @@ -147,7 +150,7 @@ class DataModelServiceIntegrationSpec extends BaseDataModelIntegrationSpec { checkAndSave(dataElement) SummaryMetadata dataElementSummaryMetadata = new SummaryMetadata(label: 'Data Element Summary Metadata', createdBy: editor.emailAddress, - summaryMetadataType: SummaryMetadataType.MAP) + summaryMetadataType: SummaryMetadataType.MAP) SummaryMetadataReport dataElementSummaryMetadataReport = new SummaryMetadataReport( reportDate: OffsetDateTime.now(), reportValue: new JsonBuilder([A: 1, B: 2]).toString(), @@ -159,6 +162,36 @@ class DataModelServiceIntegrationSpec extends BaseDataModelIntegrationSpec { checkAndSave(dataModel) } + void setupDataModelWithMultipleDataClassesAndDataElementsAndEnumerationValues() { + DataModel dataModel = new DataModel(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Data Model with Multiple Data Classes and Data Elements', + type: DataModelType.DATA_ASSET, folder: testFolder, + authority: testAuthority) + + DataClass dataClass + for (int i in 1..5) { + dataClass = new DataClass(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Root Data Class ' + i, dataModel: dataModel, idx: i - 1) + dataModel.addToDataClasses(dataClass) + } + + for (int i in 1..5) { + DataClass childDataClass = new DataClass(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Child Data Class ' + i, dataModel: dataModel, idx: i - 1) + dataClass.addToDataClasses(childDataClass) + } + + DataType enumerationType = new EnumerationType(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Enumeration Type') + for (int i in 1..5) { + enumerationType.addToEnumerationValues(new EnumerationValue(createdBy: StandardEmailAddress.INTEGRATION_TEST, key: 'Key ' + i, value: 'Value ' + i, idx: i - 1)) + } + dataModel.addToDataTypes(enumerationType) + + for (int i in 1..5) { + DataElement dataElement = new DataElement(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Data Element ' + i, dataType: enumerationType, idx: i - 1) + dataClass.addToDataElements(dataElement) + } + + checkAndSave(dataModel) + } + protected DataModel checkAndSaveNewVersion(DataModel dataModel) { check(dataModel) dataModelService.saveModelWithContent(dataModel) @@ -850,6 +883,86 @@ class DataModelServiceIntegrationSpec extends BaseDataModelIntegrationSpec { result.errors.allErrors.find {it.code == 'version.aware.label.branch.name.already.exists'} } + void 'DMSC14 : test copying a model with ordered dataclasses, dataelements and enumerationvalues'() { + given: + setupData() + setupDataModelWithMultipleDataClassesAndDataElementsAndEnumerationValues() + + when: + DataModel dataModel = dataModelService.findByLabel('Data Model with Multiple Data Classes and Data Elements') + + then: + dataModel.childDataClasses.size() == 5 + List rootDataClasses = dataModel.childDataClasses + rootDataClasses.every {dc -> + dc.label.startsWith('Root Data Class') && dc.label.endsWith((dc.idx + 1).toString()) + } + + when: + DataClass parentClass = rootDataClasses.sort().last() + + then: + parentClass.dataClasses.size() == 5 + parentClass.dataClasses.every {dc -> + dc.label.startsWith('Child Data Class') && dc.label.endsWith((dc.idx + 1).toString()) + } + + and: + parentClass.dataElements.size() == 5 + parentClass.dataElements.every {dc -> + dc.label.startsWith('Data Element') && dc.label.endsWith((dc.idx + 1).toString()) + } + + when: + DataElement dataElement = parentClass.dataElements.sort().first() + EnumerationType enumerationType = dataElement.dataType + + then: + enumerationType.enumerationValues.every {ev -> + ev.key.startsWith('Key') && ev.key.endsWith((ev.idx + 1).toString()) && + ev.value.startsWith('Value') && ev.value.endsWith((ev.idx + 1).toString()) + } + + when: + DataModel copiedDataModel = + dataModelService + .copyModel(dataModel, testFolder, editor, false, dataModel.label + ' copy', Version.from('1'), dataModel.branchName, true, + editorSecurityPolicyManager) + + then: + copiedDataModel.childDataClasses.size() == 5 + List copiedRootDataClasses = copiedDataModel.childDataClasses + copiedRootDataClasses.every {dc -> + dc.label.startsWith('Root Data Class') && dc.label.endsWith((dc.idx + 1).toString()) + } + + when: + DataClass copiedParentClass = copiedRootDataClasses.sort().last() + + then: + copiedParentClass.dataClasses.size() == 5 + copiedParentClass.dataClasses.every {dc -> + dc.label.startsWith('Child Data Class') && dc.label.endsWith((dc.idx + 1).toString()) + } + + and: + copiedParentClass.dataElements.size() == 5 + copiedParentClass.dataElements.every {dc -> + dc.label.startsWith('Data Element') && dc.label.endsWith((dc.idx + 1).toString()) + } + + when: + DataElement copiedDataElement = copiedParentClass.dataElements.sort().first() + EnumerationType copiedEnumerationType = copiedDataElement.dataType + + then: + copiedEnumerationType.domainType == 'EnumerationType' + copiedEnumerationType.enumerationValues.every {ev -> + ev.key.startsWith('Key') && ev.key.endsWith((ev.idx + 1).toString()) && + ev.value.startsWith('Value') && ev.value.endsWith((ev.idx + 1).toString()) + } + } + void 'DMSF01 : test finding common ancestor of two datamodels'() { given: setupData() @@ -1940,17 +2053,17 @@ class DataModelServiceIntegrationSpec extends BaseDataModelIntegrationSpec { foundDataModelSummaryMetadata.summaryMetadataReports.size() == 1 and: 'there is summary metadata on the Data Class' - DataClass dc = newBranch.childDataClasses.find{it.label =='Data Class having Summary Metadata'} + DataClass dc = newBranch.childDataClasses.find {it.label == 'Data Class having Summary Metadata'} SummaryMetadata foundDataClassSummaryMetadata = dc.summaryMetadata.find {it.label == 'Data Class Summary Metadata'} foundDataClassSummaryMetadata.summaryMetadataReports.size() == 1 and: 'there is summary metadata on the Data Type' - PrimitiveType dt = newBranch.dataTypes.find{it.label =='Data Type having Summary Metadata'} + PrimitiveType dt = newBranch.dataTypes.find {it.label == 'Data Type having Summary Metadata'} SummaryMetadata foundDataTypeSummaryMetadata = dt.summaryMetadata.find {it.label == 'Data Type Summary Metadata'} foundDataTypeSummaryMetadata.summaryMetadataReports.size() == 1 and: 'there is summary metadata on the Data Element' - DataElement de = dc.dataElements.find{it.label =='Data Element having Summary Metadata'} + DataElement de = dc.dataElements.find {it.label == 'Data Element having Summary Metadata'} SummaryMetadata foundDataElementSummaryMetadata = de.summaryMetadata.find {it.label == 'Data Element Summary Metadata'} foundDataElementSummaryMetadata.summaryMetadataReports.size() == 1 } diff --git a/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassFunctionalSpec.groovy b/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassFunctionalSpec.groovy index e9c9b7c7fd..e8cda9c70e 100644 --- a/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassFunctionalSpec.groovy +++ b/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassFunctionalSpec.groovy @@ -40,6 +40,7 @@ import static uk.ac.ox.softeng.maurodatamapper.core.bootstrap.StandardEmailAddre import static io.micronaut.http.HttpStatus.CREATED import static io.micronaut.http.HttpStatus.NOT_FOUND +import static io.micronaut.http.HttpStatus.NO_CONTENT import static io.micronaut.http.HttpStatus.OK import static io.micronaut.http.HttpStatus.UNPROCESSABLE_ENTITY @@ -493,6 +494,167 @@ class DataClassFunctionalSpec extends OrderedResourceFunctionalSpec { responseBody().items.any { it.label == 'string' } } + void 'CC06 : test copying a dataclass with ordered dataclasses, dataelements and enumerationvalues'() { + given: + POST('', validJson) + verifyResponse CREATED, response + String id = responseBody().id + + for (int i in 1..5) { + POST("$id/dataClasses", [label: 'Child Data Class ' + i, idx: i]) + verifyResponse CREATED, response + } + + Map enumerationTypeData = [ + label : 'Functional Test Enumeration Type', + domainType : 'EnumerationType', + enumerationValues: (1..5).collect {i -> + [ + key : 'Key ' + i, + value: 'Value ' + i, + index: i - 1 + ] + } + ] + POST("dataModels/$dataModelId/dataTypes", enumerationTypeData, MAP_ARG, true) + verifyResponse CREATED, response + String enumerationTypeId = responseBody().id + + for (int i in 1..5) { + POST("$id/dataElements", + [label: 'Data Element ' + i, dataType: enumerationTypeId, idx: i - 1]) + verifyResponse CREATED, response + } + + when: + GET("$id/dataClasses?sort=idx") + + then: + verifyResponse OK, response + responseBody().items.size() == 5 + responseBody().items.eachWithIndex {dc, i -> + assert dc.domainType == 'DataClass' + assert dc.label.startsWith('Child Data Class') && dc.label.endsWith((i + 1).toString()) + } + + when: + GET("$id/dataElements") + + then: + verifyResponse OK, response + responseBody().items.eachWithIndex {de, i -> + assert de.domainType == 'DataElement' + assert de.label.startsWith('Data Element') && de.label.endsWith((i + 1).toString()) + } + + when: + GET("dataModels/$dataModelId/dataTypes/$enumerationTypeId", MAP_ARG, true) + + then: + verifyResponse OK, response + responseBody().domainType == 'EnumerationType' + responseBody().enumerationValues.every {ev -> + ev.key.startsWith('Key') && ev.key.endsWith((ev.index + 1).toString()) && + ev.value.startsWith('Value') && ev.value.endsWith((ev.index + 1).toString()) + } + + when: + POST("${getResourcePath(otherDataModelId)}/$dataModelId/$id", [:], MAP_ARG, true) + + then: + verifyResponse CREATED, response + String copyId = responseBody().id + + when: + GET("${getResourcePath(otherDataModelId)}/$copyId/dataClasses?sort=idx", MAP_ARG, true) + + then: + verifyResponse OK, response + responseBody().items.size() == 5 + responseBody().items.eachWithIndex {dc, i -> + assert dc.domainType == 'DataClass' + assert dc.label.startsWith('Child Data Class') && dc.label.endsWith((i + 1).toString()) + } + + when: + GET("${getResourcePath(otherDataModelId)}/$copyId/dataElements", MAP_ARG, true) + + then: + verifyResponse OK, response + responseBody().items.eachWithIndex {de, i -> + assert de.domainType == 'DataElement' + assert de.label.startsWith('Data Element') && de.label.endsWith((i + 1).toString()) + } + String copyEnumerationTypeId = responseBody().items[0].dataType.id + + when: + GET("dataModels/$otherDataModelId/dataTypes/$copyEnumerationTypeId", MAP_ARG, true) + + then: + verifyResponse OK, response + responseBody().domainType == 'EnumerationType' + responseBody().enumerationValues.every {ev -> + ev.key.startsWith('Key') && ev.key.endsWith((ev.index + 1).toString()) && + ev.value.startsWith('Value') && ev.value.endsWith((ev.index + 1).toString()) + } + + cleanup: + cleanUpData() + DELETE("dataModels/$dataModelId/dataTypes/$enumerationTypeId", MAP_ARG, true) + verifyResponse NO_CONTENT, response + DELETE("dataModels/$otherDataModelId/dataTypes/$copyEnumerationTypeId", MAP_ARG, true) + verifyResponse NO_CONTENT, response + } + + void 'CC07 : test copying a dataclass without index order'() { + given: + POST('', validJson) + verifyResponse CREATED, response + String id = responseBody().id + + for (int i in 1..5) { + POST("$id/dataClasses", [label: 'Child Data Class ' + i, idx: i]) + verifyResponse CREATED, response + } + + when: + GET("$id/dataClasses?sort=idx") + + then: + verifyResponse OK, response + responseBody().items.size() == 5 + responseBody().items.eachWithIndex {dc, i -> + assert dc.domainType == 'DataClass' + assert dc.label.startsWith('Child Data Class') && dc.label.endsWith((i + 1).toString()) + } + + when: + String originalId = responseBody().items[0].id + POST("$id/dataClasses/$dataModelId/$originalId", [copyLabel: 'Child Copied Class']) + + then: + verifyResponse CREATED, response + responseBody().label == 'Child Copied Class' + + when: + GET("$id/dataClasses?sort=idx") + + then: + verifyResponse OK, response + responseBody().items.size() == 6 + responseBody().items.take(5).eachWithIndex {dc, i -> + assert dc.domainType == 'DataClass' + assert dc.label.startsWith('Child Data Class') && dc.label.endsWith((i + 1).toString()) + } + + and: + responseBody().items[5].domainType == 'DataClass' + responseBody().items[5].label == 'Child Copied Class' + + cleanup: + cleanUpData() + } + @Rollback void 'test searching for metadata "mdk1" in content dataclass'() { given: diff --git a/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassServiceIntegrationSpec.groovy b/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassServiceIntegrationSpec.groovy index 5bc50017a5..afedcba41b 100644 --- a/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassServiceIntegrationSpec.groovy +++ b/mdm-plugin-datamodel/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/DataClassServiceIntegrationSpec.groovy @@ -21,12 +21,18 @@ import uk.ac.ox.softeng.maurodatamapper.core.bootstrap.StandardEmailAddress import uk.ac.ox.softeng.maurodatamapper.core.facet.Metadata 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.rest.transport.model.CopyInformation import uk.ac.ox.softeng.maurodatamapper.datamodel.DataModel import uk.ac.ox.softeng.maurodatamapper.datamodel.DataModelService +import uk.ac.ox.softeng.maurodatamapper.datamodel.DataModelType +import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.DataType +import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.EnumerationType import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.PrimitiveType import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.ReferenceType +import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.enumeration.EnumerationValue import uk.ac.ox.softeng.maurodatamapper.datamodel.test.BaseDataModelIntegrationSpec import uk.ac.ox.softeng.maurodatamapper.security.UserSecurityPolicyManager +import uk.ac.ox.softeng.maurodatamapper.security.basic.PublicAccessSecurityPolicyManager import grails.gorm.PagedResultList import grails.gorm.transactions.Rollback @@ -95,6 +101,31 @@ class DataClassServiceIntegrationSpec extends BaseDataModelIntegrationSpec { checkAndSave(link) id = parent.id + + userSecurityPolicyManager = PublicAccessSecurityPolicyManager.instance + } + + void setupDataModelWithMultipleDataClassesAndDataElementsAndEnumerationValues() { + DataClass dataClass = new DataClass(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Root Data Class', dataModel: dataModel) + dataModel.addToDataClasses(dataClass) + + for (int i in 1..5) { + DataClass childDataClass = new DataClass(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Child Data Class ' + i, dataModel: dataModel, idx: i - 1) + dataClass.addToDataClasses(childDataClass) + } + + DataType enumerationType = new EnumerationType(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Enumeration Type') + for (int i in 1..5) { + enumerationType.addToEnumerationValues(new EnumerationValue(createdBy: StandardEmailAddress.INTEGRATION_TEST, key: 'Key ' + i, value: 'Value ' + i, idx: i - 1)) + } + dataModel.addToDataTypes(enumerationType) + + for (int i in 1..5) { + DataElement dataElement = new DataElement(createdBy: StandardEmailAddress.INTEGRATION_TEST, label: 'Data Element ' + i, dataType: enumerationType, idx: i - 1) + dataClass.addToDataElements(dataElement) + } + + checkAndSave(dataModel) } void "test get"() { @@ -486,6 +517,109 @@ class DataClassServiceIntegrationSpec extends BaseDataModelIntegrationSpec { copiedElement.dataType == referenceType } + void 'test copying dataclass with ordered dataclasses, dataelements and enumerationvalues'() { + given: + setupData() + setupDataModelWithMultipleDataClassesAndDataElementsAndEnumerationValues() + DataModel copyModel = new DataModel(label: 'copy', createdBy: StandardEmailAddress.INTEGRATION_TEST, folder: testFolder, authority: testAuthority) + checkAndSave(copyModel) + + when: + DataClass parentClass = dataModel.childDataClasses.find {it.label == 'Root Data Class'} + + then: + parentClass.dataClasses.size() == 5 + parentClass.dataClasses.every {dc -> + dc.label.startsWith('Child Data Class') && dc.label.endsWith((dc.idx + 1).toString()) + } + + and: + parentClass.dataElements.size() == 5 + parentClass.dataElements.every {dc -> + dc.label.startsWith('Data Element') && dc.label.endsWith((dc.idx + 1).toString()) + } + + when: + DataElement dataElement = parentClass.dataElements.sort().first() + EnumerationType enumerationType = dataElement.dataType + + then: + enumerationType.enumerationValues.every {ev -> + ev.key.startsWith('Key') && ev.key.endsWith((ev.idx + 1).toString()) && + ev.value.startsWith('Value') && ev.value.endsWith((ev.idx + 1).toString()) + } + + when: + DataClass original = dataClassService.get(parentClass.id) + dataClassService.copyDataClass(copyModel, original, editor, userSecurityPolicyManager) + + then: + checkAndSave(copyModel) + + when: + DataClass copiedParentClass = copyModel.childDataClasses.find {it.label == 'Root Data Class'} + + then: + copiedParentClass.dataClasses.size() == 5 + copiedParentClass.dataClasses.every {dc -> + dc.label.startsWith('Child Data Class') && dc.label.endsWith((dc.idx + 1).toString()) + } + + and: + copiedParentClass.dataElements.size() == 5 + copiedParentClass.dataElements.every {dc -> + dc.label.startsWith('Data Element') && dc.label.endsWith((dc.idx + 1).toString()) + } + + when: + DataElement copiedDataElement = copiedParentClass.dataElements.sort().first() + EnumerationType copiedEnumerationType = copiedDataElement.dataType + + then: + copiedEnumerationType.domainType == 'EnumerationType' + copiedEnumerationType.enumerationValues.every {ev -> + ev.key.startsWith('Key') && ev.key.endsWith((ev.idx + 1).toString()) && + ev.value.startsWith('Value') && ev.value.endsWith((ev.idx + 1).toString()) + } + } + + void 'test copying dataclass without index order'() { + given: + setupData() + setupDataModelWithMultipleDataClassesAndDataElementsAndEnumerationValues() + + when: + DataClass parentClass = dataModel.childDataClasses.find {it.label == 'Root Data Class'} + + then: + parentClass.dataClasses.size() == 5 + parentClass.dataClasses.every {dc -> + dc.label.startsWith('Child Data Class') && dc.label.endsWith((dc.idx + 1).toString()) + } + + when: + DataClass original = parentClass.dataClasses.sort().first() + dataClassService.copyDataClass(dataModel, original, editor, userSecurityPolicyManager, parentClass, false, new CopyInformation(copyLabel: 'Child Copied Class')) + + then: + original.idx == 0 + checkAndSave(dataModel) + + when: + DataClass copied = parentClass.dataClasses.find {it.label == 'Child Copied Class'} + List dataClasses = parentClass.dataClasses.sort() + + then: + copied.label == 'Child Copied Class' + copied.idx != original.idx + copied.idx == 5 + dataClasses.size() == 6 + dataClasses.take(5).every {dc -> + dc.label.startsWith('Child Data Class') && dc.label.endsWith((dc.idx + 1).toString()) + } + dataClasses.last() == copied + } + void 'LIST01 : test getting all DataClasses inside a DataClass with importing involved'() { // This addresses the issue gh-226 where we were getting the correct data for DC with no imported DEs and a DC with only imported DEs but incorrect // when a DCs DEs were imported into other DEs. The join was causing non-distinct results. diff --git a/mdm-plugin-datamodel/src/test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/DataTypeServiceSpec.groovy b/mdm-plugin-datamodel/src/test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/DataTypeServiceSpec.groovy index 679295ce7f..14d7eeb8d8 100644 --- a/mdm-plugin-datamodel/src/test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/DataTypeServiceSpec.groovy +++ b/mdm-plugin-datamodel/src/test/groovy/uk/ac/ox/softeng/maurodatamapper/datamodel/item/datatype/DataTypeServiceSpec.groovy @@ -26,6 +26,7 @@ import uk.ac.ox.softeng.maurodatamapper.datamodel.item.DataClass import uk.ac.ox.softeng.maurodatamapper.datamodel.item.DataElement import uk.ac.ox.softeng.maurodatamapper.datamodel.item.DataElementService import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.enumeration.EnumerationValue +import uk.ac.ox.softeng.maurodatamapper.datamodel.item.datatype.enumeration.EnumerationValueService import uk.ac.ox.softeng.maurodatamapper.test.unit.service.CatalogueItemServiceSpec import grails.testing.services.ServiceUnitTest @@ -44,6 +45,7 @@ class DataTypeServiceSpec extends CatalogueItemServiceSpec implements ServiceUni mockArtefact(ReferenceTypeService) mockArtefact(PrimitiveTypeService) mockArtefact(EnumerationTypeService) + mockArtefact(EnumerationValueService) mockArtefact(SummaryMetadataService) mockDomains(DataModel, DataClass, DataType, PrimitiveType, ReferenceType, EnumerationType, EnumerationValue, DataElement) diff --git a/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/ReferenceDataModelService.groovy b/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/ReferenceDataModelService.groovy index 9108bac178..ed5a2e1c3d 100644 --- a/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/ReferenceDataModelService.groovy +++ b/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/ReferenceDataModelService.groovy @@ -35,6 +35,7 @@ import uk.ac.ox.softeng.maurodatamapper.core.model.ModelService import uk.ac.ox.softeng.maurodatamapper.core.provider.dataloader.DataLoaderProviderService import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.ModelImporterProviderService import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.ModelImporterProviderServiceParameters +import uk.ac.ox.softeng.maurodatamapper.core.rest.transport.model.CopyInformation import uk.ac.ox.softeng.maurodatamapper.core.traits.domain.MultiFacetItemAware import uk.ac.ox.softeng.maurodatamapper.path.Path import uk.ac.ox.softeng.maurodatamapper.referencedata.facet.ReferenceSummaryMetadata @@ -457,18 +458,20 @@ class ReferenceDataModelService extends ModelService impleme copy.trackChanges() + CopyInformation referenceDataTypeCopyInformation = new CopyInformation(copyIndex: true) if (original.referenceDataTypes) { // Copy all the referencedatatypes - original.referenceDataTypes.each { dt -> - referenceDataTypeService.copyReferenceDataType(copy, dt, copier, userSecurityPolicyManager) + original.referenceDataTypes.sort().each { dt -> + referenceDataTypeService.copyReferenceDataType(copy, dt, copier, userSecurityPolicyManager, copySummaryMetadata, referenceDataTypeCopyInformation) } } + CopyInformation referenceDataElementCopyInformation = new CopyInformation(copyIndex: true) if (original.referenceDataElements) { // Copy all the referencedataelements - original.referenceDataElements.each { de -> + original.referenceDataElements.sort().each { de -> log.debug("copy element ${de}") - referenceDataElementService.copyReferenceDataElement(copy, de, copier, userSecurityPolicyManager) + referenceDataElementService.copyReferenceDataElement(copy, de, copier, userSecurityPolicyManager, copySummaryMetadata, referenceDataElementCopyInformation) } } diff --git a/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/ReferenceDataElementService.groovy b/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/ReferenceDataElementService.groovy index d095add2a9..570f29f519 100644 --- a/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/ReferenceDataElementService.groovy +++ b/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/ReferenceDataElementService.groovy @@ -26,6 +26,7 @@ import uk.ac.ox.softeng.maurodatamapper.core.model.Model import uk.ac.ox.softeng.maurodatamapper.core.model.ModelItemService import uk.ac.ox.softeng.maurodatamapper.core.path.PathService import uk.ac.ox.softeng.maurodatamapper.core.rest.transport.merge.FieldPatchData +import uk.ac.ox.softeng.maurodatamapper.core.rest.transport.model.CopyInformation import uk.ac.ox.softeng.maurodatamapper.core.similarity.SimilarityPair import uk.ac.ox.softeng.maurodatamapper.hibernate.search.engine.search.predicate.FilterFactory import uk.ac.ox.softeng.maurodatamapper.hibernate.search.engine.search.predicate.IdPathSecureFilterFactory @@ -257,16 +258,18 @@ class ReferenceDataElementService extends ModelItemService } @Override - ReferenceDataElement copy(Model copiedReferenceDataModel, ReferenceDataElement original, CatalogueItem nonModelParent, UserSecurityPolicyManager userSecurityPolicyManager) { + ReferenceDataElement copy(Model copiedReferenceDataModel, ReferenceDataElement original, CatalogueItem nonModelParent, + UserSecurityPolicyManager userSecurityPolicyManager) { copyReferenceDataElement(copiedReferenceDataModel as ReferenceDataModel, original, userSecurityPolicyManager.user, userSecurityPolicyManager) } ReferenceDataElement copyReferenceDataElement(ReferenceDataModel copiedReferenceDataModel, ReferenceDataElement original, User copier, - UserSecurityPolicyManager userSecurityPolicyManager) { + UserSecurityPolicyManager userSecurityPolicyManager, boolean copySummaryMetadata = false, + CopyInformation copyInformation = null) { ReferenceDataElement copy = new ReferenceDataElement(minMultiplicity: original.minMultiplicity, maxMultiplicity: original.maxMultiplicity) - copy = copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager) + copy = copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copySummaryMetadata, copyInformation) setCatalogueItemRefinesCatalogueItem(copy, original, copier) ReferenceDataType referenceDataType = copiedReferenceDataModel.findReferenceDataTypeByLabel(original.referenceDataType.label) @@ -285,12 +288,12 @@ class ReferenceDataElementService extends ModelItemService } @Override - ReferenceDataElement copyCatalogueItemInformation(ReferenceDataElement original, - ReferenceDataElement copy, - User copier, - UserSecurityPolicyManager userSecurityPolicyManager, - boolean copySummaryMetadata = false) { - copy = super.copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager) + ReferenceDataElement copyModelItemInformation(ReferenceDataElement original, + ReferenceDataElement copy, + User copier, + UserSecurityPolicyManager userSecurityPolicyManager, + boolean copySummaryMetadata = false, CopyInformation copyInformation = null) { + copy = super.copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) if (copySummaryMetadata) { referenceSummaryMetadataService.findAllByMultiFacetAwareItemId(original.id).each { copy.addToReferenceSummaryMetadata(label: it.label, summaryMetadataType: it.summaryMetadataType, createdBy: copier.emailAddress) @@ -341,7 +344,7 @@ class ReferenceDataElementService extends ModelItemService .where {lsf -> BooleanPredicateClausesStep boolStep = lsf .bool() - .filter(IdPathSecureFilterFactory.createFilter(lsf, [referenceDataModelToSearch.id], [referenceDataModelToSearch.path.last()])) + .filter(IdPathSecureFilterFactory.createFilter(lsf, [referenceDataModelToSearch.id], [referenceDataModelToSearch.path.last()])) .filter(FilterFactory.mustNot(lsf, lsf.id().matching(referenceDataElementToCompare.id))) moreLikeThisQueries.each {mlt -> diff --git a/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/ReferenceDataTypeService.groovy b/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/ReferenceDataTypeService.groovy index 251a5858ad..d6ce7118f4 100644 --- a/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/ReferenceDataTypeService.groovy +++ b/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/ReferenceDataTypeService.groovy @@ -23,11 +23,13 @@ import uk.ac.ox.softeng.maurodatamapper.core.facet.Metadata import uk.ac.ox.softeng.maurodatamapper.core.model.CatalogueItem import uk.ac.ox.softeng.maurodatamapper.core.model.Model import uk.ac.ox.softeng.maurodatamapper.core.model.ModelItemService +import uk.ac.ox.softeng.maurodatamapper.core.rest.transport.model.CopyInformation import uk.ac.ox.softeng.maurodatamapper.referencedata.ReferenceDataModel import uk.ac.ox.softeng.maurodatamapper.referencedata.ReferenceDataModelService import uk.ac.ox.softeng.maurodatamapper.referencedata.facet.ReferenceSummaryMetadataService import uk.ac.ox.softeng.maurodatamapper.referencedata.item.ReferenceDataElement import uk.ac.ox.softeng.maurodatamapper.referencedata.item.ReferenceDataElementService +import uk.ac.ox.softeng.maurodatamapper.referencedata.item.datatype.enumeration.ReferenceEnumerationValueService import uk.ac.ox.softeng.maurodatamapper.referencedata.provider.DefaultReferenceDataTypeProvider import uk.ac.ox.softeng.maurodatamapper.referencedata.rest.transport.DefaultReferenceDataType import uk.ac.ox.softeng.maurodatamapper.referencedata.traits.service.ReferenceSummaryMetadataAwareService @@ -49,6 +51,7 @@ class ReferenceDataTypeService extends ModelItemService imple ReferenceEnumerationTypeService referenceEnumerationTypeService ReferenceSummaryMetadataService referenceSummaryMetadataService ReferenceDataModelService referenceDataModelService + ReferenceEnumerationValueService referenceEnumerationValueService @Override ReferenceDataType get(Serializable id) { @@ -66,7 +69,7 @@ class ReferenceDataTypeService extends ModelItemService imple } @Override - List list(Map args=[:]) { + List list(Map args = [:]) { ReferenceDataType.list(args) } @@ -86,7 +89,7 @@ class ReferenceDataTypeService extends ModelItemService imple referenceDataType.breadcrumbTree.removeFromParent() List referenceDataElements = referenceDataElementService.findAllByReferenceDataType(referenceDataType) - referenceDataElements.each { referenceDataElementService.delete(it) } + referenceDataElements.each {referenceDataElementService.delete(it)} switch (referenceDataType.domainType) { case ReferenceDataType.PRIMITIVE_DOMAIN_TYPE: @@ -154,7 +157,7 @@ class ReferenceDataTypeService extends ModelItemService imple new ReferencePrimitiveType(label: 'Timestamp', description: 'A timestamp'), new ReferencePrimitiveType(label: 'Boolean', description: 'A true or false value'), new ReferencePrimitiveType(label: 'Duration', description: 'A time period in arbitrary units') - ].collect { new DefaultReferenceDataType(it) } + ].collect {new DefaultReferenceDataType(it)} } @Override @@ -210,9 +213,7 @@ class ReferenceDataTypeService extends ModelItemService imple } ReferenceDataType copyReferenceDataType(ReferenceDataModel copiedReferenceDataModel, ReferenceDataType original, User copier, - UserSecurityPolicyManager userSecurityPolicyManager, - boolean copySummaryMetadata = false) { - + UserSecurityPolicyManager userSecurityPolicyManager, boolean copySummaryMetadata = false, CopyInformation copyInformation = null) { ReferenceDataType copy String domainType = original.domainType @@ -222,15 +223,19 @@ class ReferenceDataTypeService extends ModelItemService imple break case ReferenceDataType.ENUMERATION_DOMAIN_TYPE: copy = new ReferenceEnumerationType() - original.referenceEnumerationValues.each { ev -> - copy.addToReferenceEnumerationValues(key: ev.key, value: ev.value, category: ev.category) + CopyInformation referenceEnumerationTypeCopyInformation = new CopyInformation(copyIndex: true) + original.referenceEnumerationValues.sort().each {ev -> + copy.addToReferenceEnumerationValues( + referenceEnumerationValueService + .copyReferenceEnumerationValue(copiedReferenceDataModel, ev, copy, userSecurityPolicyManager.user, userSecurityPolicyManager, + referenceEnumerationTypeCopyInformation)) } break default: throw new ApiInternalException('DTSXX', 'DataType domain type is unknown and therefore cannot be copied') } - copy = copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager, copySummaryMetadata) + copy = copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copySummaryMetadata, copyInformation) setCatalogueItemRefinesCatalogueItem(copy, original, copier) copiedReferenceDataModel.addToReferenceDataTypes(copy) @@ -239,12 +244,12 @@ class ReferenceDataTypeService extends ModelItemService imple } @Override - ReferenceDataType copyCatalogueItemInformation(ReferenceDataType original, - ReferenceDataType copy, - User copier, - UserSecurityPolicyManager userSecurityPolicyManager, - boolean copySummaryMetadata = false) { - copy = super.copyCatalogueItemInformation(original, copy, copier, userSecurityPolicyManager) + ReferenceDataType copyModelItemInformation(ReferenceDataType original, + ReferenceDataType copy, + User copier, + UserSecurityPolicyManager userSecurityPolicyManager, + boolean copySummaryMetadata = false, CopyInformation copyInformation) { + copy = super.copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) if (copySummaryMetadata) { referenceSummaryMetadataService.findAllByMultiFacetAwareItemId(original.id).each { copy.addToReferenceSummaryMetadata(label: it.label, summaryMetadataType: it.summaryMetadataType, createdBy: copier.emailAddress) @@ -288,12 +293,12 @@ class ReferenceDataTypeService extends ModelItemService imple } private void mergeDataTypes(ReferenceDataType keep, ReferenceDataType replace) { - replace.referenceDataElements?.each { de -> + replace.referenceDataElements?.each {de -> keep.addToReferenceDataElements(de) } List mds = [] mds += replace.metadata ?: [] - mds.findAll { !keep.findMetadataByNamespaceAndKey(it.namespace, it.key) }.each { md -> + mds.findAll {!keep.findMetadataByNamespaceAndKey(it.namespace, it.key)}.each {md -> replace.removeFromMetadata(md) keep.addToMetadata(md.namespace, md.key, md.value, md.createdBy) } diff --git a/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/enumeration/ReferenceEnumerationValueService.groovy b/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/enumeration/ReferenceEnumerationValueService.groovy index 0c9ad42198..5ae6b4db16 100644 --- a/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/enumeration/ReferenceEnumerationValueService.groovy +++ b/mdm-plugin-referencedata/grails-app/services/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/enumeration/ReferenceEnumerationValueService.groovy @@ -18,11 +18,16 @@ package uk.ac.ox.softeng.maurodatamapper.referencedata.item.datatype.enumeration import uk.ac.ox.softeng.maurodatamapper.core.container.Classifier +import uk.ac.ox.softeng.maurodatamapper.core.model.CatalogueItem +import uk.ac.ox.softeng.maurodatamapper.core.model.Model import uk.ac.ox.softeng.maurodatamapper.core.model.ModelItemService +import uk.ac.ox.softeng.maurodatamapper.core.rest.transport.model.CopyInformation import uk.ac.ox.softeng.maurodatamapper.referencedata.ReferenceDataModel import uk.ac.ox.softeng.maurodatamapper.referencedata.ReferenceDataModelService import uk.ac.ox.softeng.maurodatamapper.referencedata.facet.ReferenceSummaryMetadataService +import uk.ac.ox.softeng.maurodatamapper.referencedata.item.datatype.ReferenceEnumerationType import uk.ac.ox.softeng.maurodatamapper.referencedata.traits.service.ReferenceSummaryMetadataAwareService +import uk.ac.ox.softeng.maurodatamapper.security.User import uk.ac.ox.softeng.maurodatamapper.security.UserSecurityPolicyManager import uk.ac.ox.softeng.maurodatamapper.util.Utils @@ -129,8 +134,30 @@ class ReferenceEnumerationValueService extends ModelItemService findAllByReferenceDataModelId(Serializable dataModelId) { ReferenceEnumerationValue.byReferenceDataModelId(dataModelId).list() } + + @Override + ReferenceEnumerationValue copy(Model copiedModel, ReferenceEnumerationValue original, CatalogueItem referenceEnumerationTypeToCopyInto, + UserSecurityPolicyManager userSecurityPolicyManager) { + copyReferenceEnumerationValue(copiedModel as ReferenceDataModel, original, referenceEnumerationTypeToCopyInto as ReferenceEnumerationType, + userSecurityPolicyManager.user, + userSecurityPolicyManager) + } + + ReferenceEnumerationValue copyReferenceEnumerationValue(ReferenceDataModel copiedReferenceDataModel, ReferenceEnumerationValue original, + ReferenceEnumerationType referenceEnumerationTypeToCopyInto, + User copier, UserSecurityPolicyManager userSecurityPolicyManager, CopyInformation copyInformation = null) { + ReferenceEnumerationValue copy = new ReferenceEnumerationValue(key: original.key, value: original.value, category: original.category) + + copy = copyModelItemInformation(original, copy, copier, userSecurityPolicyManager, copyInformation) + setCatalogueItemRefinesCatalogueItem(copy, original, copier) + + ReferenceEnumerationType referenceEnumerationType = referenceEnumerationTypeToCopyInto ?: + copiedReferenceDataModel.findReferenceDataTypeByLabelAndType(original.referenceEnumerationType.label, + ReferenceEnumerationType.simpleName) + referenceEnumerationType.addToReferenceEnumerationValues(copy) + copy + } } \ No newline at end of file diff --git a/mdm-plugin-referencedata/src/test/groovy/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/ReferenceDataTypeServiceSpec.groovy b/mdm-plugin-referencedata/src/test/groovy/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/ReferenceDataTypeServiceSpec.groovy index d1cf2acea4..27d25a2eaa 100644 --- a/mdm-plugin-referencedata/src/test/groovy/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/ReferenceDataTypeServiceSpec.groovy +++ b/mdm-plugin-referencedata/src/test/groovy/uk/ac/ox/softeng/maurodatamapper/referencedata/item/datatype/ReferenceDataTypeServiceSpec.groovy @@ -25,6 +25,7 @@ import uk.ac.ox.softeng.maurodatamapper.referencedata.item.ReferenceDataElement import uk.ac.ox.softeng.maurodatamapper.referencedata.item.ReferenceDataElementService import uk.ac.ox.softeng.maurodatamapper.referencedata.item.datatype.ReferenceDataType import uk.ac.ox.softeng.maurodatamapper.referencedata.item.datatype.enumeration.ReferenceEnumerationValue +import uk.ac.ox.softeng.maurodatamapper.referencedata.item.datatype.enumeration.ReferenceEnumerationValueService import uk.ac.ox.softeng.maurodatamapper.test.unit.service.CatalogueItemServiceSpec import grails.testing.services.ServiceUnitTest @@ -43,6 +44,7 @@ class ReferenceDataTypeServiceSpec extends CatalogueItemServiceSpec implements S mockArtefact(ReferencePrimitiveTypeService) mockArtefact(ReferenceEnumerationTypeService) mockArtefact(ReferenceSummaryMetadataService) + mockArtefact(ReferenceEnumerationValueService) mockDomains(ReferenceDataModel, ReferenceDataType, ReferencePrimitiveType, ReferenceDataType, ReferenceEnumerationType, ReferenceEnumerationValue, ReferenceDataElement) @@ -125,9 +127,9 @@ class ReferenceDataTypeServiceSpec extends CatalogueItemServiceSpec implements S ReferenceDataType.count() == 5 and: - !ReferenceDataType.findByLabel('varchar') + !ReferenceDataType.findByLabel('varchar') } - + void "test save"() {