From 391ba5e08aec6ecc452fa4f2df513f46e7157f80 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Fri, 10 Dec 2021 15:28:03 +0000 Subject: [PATCH 01/17] mc-9714 Create FolderImporterProviderService * Create ContainerImporterProviderService --- .../ContainerImporterProviderService.groovy | 63 +++++++++++++++++++ .../FolderImporterProviderService.groovy | 47 ++++++++++++++ ...erImporterProviderServiceParameters.groovy | 26 ++++++++ 3 files changed, 136 insertions(+) create mode 100644 mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/ContainerImporterProviderService.groovy create mode 100644 mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderImporterProviderService.groovy create mode 100644 mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/ContainerImporterProviderService.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/ContainerImporterProviderService.groovy new file mode 100644 index 0000000000..d96a7ba7b0 --- /dev/null +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/ContainerImporterProviderService.groovy @@ -0,0 +1,63 @@ +/* + * Copyright 2020 University of Oxford and Health and Social Care Information Centre, also known as NHS Digital + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer + +import uk.ac.ox.softeng.maurodatamapper.api.exception.ApiInternalException +import uk.ac.ox.softeng.maurodatamapper.core.model.Container +import uk.ac.ox.softeng.maurodatamapper.core.model.ContainerService +import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.ImporterProviderServiceParameters +import uk.ac.ox.softeng.maurodatamapper.provider.MauroDataMapperService +import uk.ac.ox.softeng.maurodatamapper.security.User + +import groovy.transform.CompileStatic +import org.springframework.core.GenericTypeResolver + +@CompileStatic +abstract class ContainerImporterProviderService extends MauroDataMapperService { + + abstract ContainerService getContainerService() + + abstract Boolean canImportMultipleDomains() + + abstract C importDomain(User currentUser, P params) + + abstract List importDomains(User currentUser, P params) + + Class

getImporterProviderServiceParametersClass() { + (Class

) GenericTypeResolver.resolveTypeArguments(getClass(), ContainerImporterProviderService).last() + } + + /** + * Returns a new instance object of the defined ImporterProviderServiceParameters parameter. + * + * The method makes use of Class.newInstance(), this was deprecated in Java9 however is still part of the Groovy extension. + * Due to the fact Groovy auto adds empty constructors to all its classes the Java9 method of getDeclaredConstructor.getNewInstance() fails + * as there is no declared constructor. + * + * @return + * @throws ApiInternalException + */ + @SuppressWarnings('GrDeprecatedAPIUsage') + P createNewImporterProviderServiceParameters() throws ApiInternalException { + try { + return importerProviderServiceParametersClass.newInstance() + } catch (InstantiationException | IllegalAccessException ex) { + throw new ApiInternalException('IP01', 'Cannot create new import params instance', ex) + } + } +} diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderImporterProviderService.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderImporterProviderService.groovy new file mode 100644 index 0000000000..f5c044bff4 --- /dev/null +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderImporterProviderService.groovy @@ -0,0 +1,47 @@ +/* + * Copyright 2020 University of Oxford and Health and Social Care Information Centre, also known as NHS Digital + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer + +import uk.ac.ox.softeng.maurodatamapper.core.authority.AuthorityService +import uk.ac.ox.softeng.maurodatamapper.core.container.Folder +import uk.ac.ox.softeng.maurodatamapper.core.container.FolderService +import uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.parameter.FolderImporterProviderServiceParameters +import uk.ac.ox.softeng.maurodatamapper.core.provider.ProviderType + +import groovy.transform.CompileStatic +import org.springframework.beans.factory.annotation.Autowired + +@CompileStatic +abstract class FolderImporterProviderService

extends ContainerImporterProviderService { + + @Autowired + AuthorityService authorityService + + @Autowired + FolderService folderService + + @Override + FolderService getContainerService() { + folderService + } + + @Override + String getProviderType() { + "Folder${ProviderType.IMPORTER.name}" + } +} diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy new file mode 100644 index 0000000000..245be17eec --- /dev/null +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy @@ -0,0 +1,26 @@ +/* + * Copyright 2020 University of Oxford and Health and Social Care Information Centre, also known as NHS Digital + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.parameter + +import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.ImporterProviderServiceParameters + +import groovy.transform.CompileStatic + +@CompileStatic +class FolderImporterProviderServiceParameters implements ImporterProviderServiceParameters { +} From ee8cf07ed9bd3723244639ba1b8c9501dc86b1f0 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Fri, 10 Dec 2021 15:27:03 +0000 Subject: [PATCH 02/17] mc-9714 Create FolderJsonImporterService --- .../importer/FolderJsonImporterService.groovy | 57 ++++++++++++++++++ ...taBindFolderImporterProviderService.groovy | 60 +++++++++++++++++++ ...leImporterProviderServiceParameters.groovy | 39 ++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterService.groovy create mode 100644 mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy create mode 100644 mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderFileImporterProviderServiceParameters.groovy diff --git a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterService.groovy b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterService.groovy new file mode 100644 index 0000000000..d0453c9943 --- /dev/null +++ b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterService.groovy @@ -0,0 +1,57 @@ +/* + * Copyright 2020 University of Oxford and Health and Social Care Information Centre, also known as NHS Digital + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer + +import uk.ac.ox.softeng.maurodatamapper.api.exception.ApiBadRequestException +import uk.ac.ox.softeng.maurodatamapper.core.container.Folder +import uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.parameter.FolderFileImporterProviderServiceParameters +import uk.ac.ox.softeng.maurodatamapper.core.traits.provider.importer.JsonImportMapping +import uk.ac.ox.softeng.maurodatamapper.security.User + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j + +@Slf4j +@CompileStatic +class FolderJsonImporterService extends DataBindFolderImporterProviderService implements JsonImportMapping { + + @Override + String getDisplayName() { + 'JSON Folder Importer' + } + + @Override + String getVersion() { + '1.0' + } + + @Override + Boolean canImportMultipleDomains() { + false + } + + @Override + Folder importFolder(User currentUser, byte[] content) { + null + } + + @Override + List importFolders(User currentUser, byte[] content) { + throw new ApiBadRequestException('FBIP04', "${name} cannot import multiple Folders") + } +} diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy new file mode 100644 index 0000000000..04d4edfef1 --- /dev/null +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy @@ -0,0 +1,60 @@ +/* + * Copyright 2020 University of Oxford and Health and Social Care Information Centre, also known as NHS Digital + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer + +import uk.ac.ox.softeng.maurodatamapper.api.exception.ApiBadRequestException +import uk.ac.ox.softeng.maurodatamapper.api.exception.ApiUnauthorizedException +import uk.ac.ox.softeng.maurodatamapper.core.container.Folder +import uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.parameter.FolderFileImporterProviderServiceParameters +import uk.ac.ox.softeng.maurodatamapper.security.User + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.springframework.core.GenericTypeResolver + +@Slf4j +@CompileStatic +abstract class DataBindFolderImporterProviderService

extends FolderImporterProviderService

{ + + abstract Folder importFolder(User currentUser, byte[] content) + + abstract List importFolders(User currentUser, byte[] content) + + @Override + Class

getImporterProviderServiceParametersClass() { + (Class

) GenericTypeResolver.resolveTypeArgument(getClass(), DataBindFolderImporterProviderService) + } + + @Override + Folder importDomain(User currentUser, FolderFileImporterProviderServiceParameters params) { + checkImportParams(currentUser, params) + importFolder(currentUser, params.importFile.fileContents) + } + + @Override + List importDomains(User currentUser, FolderFileImporterProviderServiceParameters params) { + checkImportParams(currentUser, params) + importFolders(currentUser, params.importFile.fileContents) + } + + private void checkImportParams(User currentUser, FolderFileImporterProviderServiceParameters params) { + if (!currentUser) throw new ApiUnauthorizedException('FBIP01', 'User must be logged in to import folder') + if (params.importFile.fileContents.size() == 0) throw new ApiBadRequestException('FBIP02', 'Cannot import empty file') + log.info("Importing ${params.importFile.fileName} as ${currentUser.emailAddress}") + } +} diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderFileImporterProviderServiceParameters.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderFileImporterProviderServiceParameters.groovy new file mode 100644 index 0000000000..79293359d9 --- /dev/null +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderFileImporterProviderServiceParameters.groovy @@ -0,0 +1,39 @@ +/* + * Copyright 2020 University of Oxford and Health and Social Care Information Centre, also known as NHS Digital + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.parameter + +import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.FileParameter +import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.config.ImportGroupConfig +import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.config.ImportParameterConfig + +import groovy.transform.CompileStatic + +@CompileStatic +class FolderFileImporterProviderServiceParameters extends FolderImporterProviderServiceParameters { + + @ImportParameterConfig( + displayName = 'File', + description = 'The file containing the data to be imported', + order = -1, + group = @ImportGroupConfig( + name = 'Source', + order = -1 + ) + ) + FileParameter importFile +} From 413974dada72dbf96678ce16e7aa52bc2a83b442 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Thu, 3 Feb 2022 12:48:13 +0000 Subject: [PATCH 03/17] mc-9714 Create FolderJsonImporterServiceSpec --- .../FolderJsonImporterServiceSpec.groovy | 42 +++++++++ .../BaseFolderImporterServiceSpec.groovy | 90 +++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy create mode 100644 mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy new file mode 100644 index 0000000000..e31a752f0c --- /dev/null +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy @@ -0,0 +1,42 @@ +/* + * Copyright 2020 University of Oxford and Health and Social Care Information Centre, also known as NHS Digital + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer + +import uk.ac.ox.softeng.maurodatamapper.core.container.test.provider.BaseFolderImporterServiceSpec + +import grails.gorm.transactions.Rollback +import grails.testing.mixin.integration.Integration + +@Integration +@Rollback +class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { + + FolderJsonImporterService folderJsonImporterService + + @Override + FolderImporterProviderService getImporterService() { + folderJsonImporterService + } + + @Override + String getImportType() { + 'json' + } + + // TODO: Test import null and invalid Folders +} diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy new file mode 100644 index 0000000000..52e3bea8a7 --- /dev/null +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy @@ -0,0 +1,90 @@ +/* + * Copyright 2020 University of Oxford and Health and Social Care Information Centre, also known as NHS Digital + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package uk.ac.ox.softeng.maurodatamapper.core.container.test.provider + +import uk.ac.ox.softeng.maurodatamapper.core.container.Folder +import uk.ac.ox.softeng.maurodatamapper.core.container.FolderService +import uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.FolderImporterProviderService +import uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.parameter.FolderFileImporterProviderServiceParameters +import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.FileParameter +import uk.ac.ox.softeng.maurodatamapper.test.integration.BaseIntegrationSpec + +import grails.testing.spock.RunOnce +import grails.util.BuildSettings +import groovy.util.logging.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import spock.lang.Shared + +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths + +@Slf4j +abstract class BaseFolderImporterServiceSpec extends BaseIntegrationSpec { + + @Autowired + FolderService folderService + + @Shared + Path resourcesPath + + @Shared + FolderFileImporterProviderServiceParameters basicParameters + + abstract FolderImporterProviderService getImporterService() + + abstract String getImportType() + + @Override + Object setupSpec() { + basicParameters = new FolderFileImporterProviderServiceParameters() + } + + @RunOnce + @Override + Object setup() { + resourcesPath = Paths.get(BuildSettings.BASE_DIR.absolutePath, 'src', 'integration-test', 'resources', importType) + basicParameters.importFile = null + } + + @Override + void setupDomainData() { + } + + byte[] loadTestFile(String filename) { + Path testFilePath = resourcesPath.resolve("${filename}.${importType}").toAbsolutePath() + assert Files.exists(testFilePath) + Files.readAllBytes(testFilePath) + } + + Folder importFolder(byte[] bytes) { + log.trace('Importing:\n {}', new String(bytes)) + basicParameters.importFile = new FileParameter(fileContents: bytes) + + Folder imported = importerService.importDomain(admin, basicParameters) as Folder + check(imported) + folderService.save(imported) + sessionFactory.currentSession.flush() + log.debug('Folder saved') + folderService.get(imported.id) + } + + Folder importFolder(String filename) { + importFolder(loadTestFile(filename)) + } +} From 3915ca788fe946acda4df5adeb3b2fd07fec5b23 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Fri, 10 Dec 2021 16:14:48 +0000 Subject: [PATCH 04/17] mc-9714 test: Import JSON of Folder --- .../importer/FolderJsonImporterService.groovy | 9 ++++++--- .../FolderJsonImporterServiceSpec.groovy | 14 ++++++++++++++ .../ContainerImporterProviderService.groovy | 4 ++++ ...ataBindFolderImporterProviderService.groovy | 18 ++++++++++++++++-- .../FolderImporterProviderService.groovy | 8 ++++++-- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterService.groovy b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterService.groovy index d0453c9943..dd9a3fcb76 100644 --- a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterService.groovy +++ b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterService.groovy @@ -23,11 +23,9 @@ import uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.paramet import uk.ac.ox.softeng.maurodatamapper.core.traits.provider.importer.JsonImportMapping import uk.ac.ox.softeng.maurodatamapper.security.User -import groovy.transform.CompileStatic import groovy.util.logging.Slf4j @Slf4j -@CompileStatic class FolderJsonImporterService extends DataBindFolderImporterProviderService implements JsonImportMapping { @Override @@ -47,7 +45,12 @@ class FolderJsonImporterService extends DataBindFolderImporterProviderService importDomains(User currentUser, P params) + List getImportBlacklistedProperties() { + ['id', 'lastUpdated', 'domainType'] + } + Class

getImporterProviderServiceParametersClass() { (Class

) GenericTypeResolver.resolveTypeArguments(getClass(), ContainerImporterProviderService).last() } diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy index 04d4edfef1..9c042dbcba 100644 --- a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy @@ -23,6 +23,7 @@ import uk.ac.ox.softeng.maurodatamapper.core.container.Folder import uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.parameter.FolderFileImporterProviderServiceParameters import uk.ac.ox.softeng.maurodatamapper.security.User +import grails.web.databinding.DataBindingUtils import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import org.springframework.core.GenericTypeResolver @@ -43,13 +44,26 @@ abstract class DataBindFolderImporterProviderService

importDomains(User currentUser, FolderFileImporterProviderServiceParameters params) { checkImportParams(currentUser, params) - importFolders(currentUser, params.importFile.fileContents) + List folders = importFolders(currentUser, params.importFile.fileContents) + folders.collect { checkImport(it) } + } + + Folder bindMapToFolder(User currentUser, Map folderMap) { + if (!folderMap) throw new ApiBadRequestException('FBIP03', 'No FolderMap supplied to import') + + Folder folder = new Folder() + log.debug('Binding map to new Folder instance') + DataBindingUtils.bindObjectToInstance(folder, folderMap, null, importBlacklistedProperties, null) + + log.debug('Binding complete') + folder } private void checkImportParams(User currentUser, FolderFileImporterProviderServiceParameters params) { diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderImporterProviderService.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderImporterProviderService.groovy index f5c044bff4..e0a4b2c1c8 100644 --- a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderImporterProviderService.groovy +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderImporterProviderService.groovy @@ -23,10 +23,8 @@ import uk.ac.ox.softeng.maurodatamapper.core.container.FolderService import uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.parameter.FolderImporterProviderServiceParameters import uk.ac.ox.softeng.maurodatamapper.core.provider.ProviderType -import groovy.transform.CompileStatic import org.springframework.beans.factory.annotation.Autowired -@CompileStatic abstract class FolderImporterProviderService

extends ContainerImporterProviderService { @Autowired @@ -44,4 +42,10 @@ abstract class FolderImporterProviderService

Date: Fri, 10 Dec 2021 23:28:50 +0000 Subject: [PATCH 05/17] mc-9714 test: Import JSON of empty Folder --- .../importer/FolderJsonImporterServiceSpec.groovy | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy index 73f6fa3f7f..7afdfb6cef 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy @@ -53,4 +53,13 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { domainType == 'Folder' } } + + void 'test import empty Folder'() { + when: + Folder folder = importFolder('emptyFolder') + + then: + !folder.childFolders + !folderService.findAllModelsInFolder(folder) + } } From b29514869e29c25374b6f511c070d0f6af4f3af9 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Fri, 10 Dec 2021 16:20:08 +0000 Subject: [PATCH 06/17] mc-9714 test: Import JSON of Folder with description --- .../provider/importer/FolderJsonImporterServiceSpec.groovy | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy index 7afdfb6cef..dcfab968b6 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy @@ -62,4 +62,9 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { !folder.childFolders !folderService.findAllModelsInFolder(folder) } + + void 'test import Folder with description'() { + expect: + importFolder('folderIncDescription').description == 'Test Folder description' + } } From eca58e9467b15d60ec4be39d9e686a1567cb6fa7 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Thu, 3 Mar 2022 18:31:45 +0000 Subject: [PATCH 07/17] mc-9714 test: Import JSON of Folder with metadata --- .../core/container/FolderService.groovy | 9 ++++++++- .../importer/FolderJsonImporterServiceSpec.groovy | 13 +++++++++++++ .../provider/BaseFolderImporterServiceSpec.groovy | 2 +- .../DataBindFolderImporterProviderService.groovy | 9 +++++---- .../importer/FolderImporterProviderService.groovy | 12 ++---------- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy index dd39d408de..2b344bb2eb 100644 --- a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy +++ b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy @@ -184,7 +184,7 @@ class FolderService extends ContainerService { (folder as GormEntity).save(saveArgs) sessionFactory.currentSession.flush() } else { - (folder as GormEntity).save(args) + (folder as GormEntity).save(saveArgs) } folder } @@ -645,4 +645,11 @@ class FolderService extends ContainerService { if (parentCache) parentCache.addDiffCache(folder.path, fDiffCache) fDiffCache } + + Folder checkImportedFolderAssociations(User importingUser, Folder folder) { + folder.createdBy = importingUser.emailAddress + if (!folder.id) save(folder, insert: true, validate: false) // Skip validation to avoid error on null folderId/multiFacetAwareItemId + checkFacetsAfterImportingMultiFacetAware(folder) + log.debug('Folder associations checked') + } } diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy index dcfab968b6..17cf4e1eec 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy @@ -67,4 +67,17 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { expect: importFolder('folderIncDescription').description == 'Test Folder description' } + + void 'test import Folder with metadata'() { + when: + Folder folder = importFolder('folderIncMetadata') + + then: + folder.metadata.size() == 3 + folder.metadata.tap { + find { it.namespace == 'test.com' && it.key == 'mdk1' && it.value == 'mdv1' } + find { it.namespace == 'test.com/simple' && it.key == 'mdk1' && it.value == 'mdv1' } + find { it.namespace == 'test.com/simple' && it.key == 'mdk2' && it.value == 'mdv2' } + } + } } diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy index 52e3bea8a7..ce8ceffda1 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy @@ -79,7 +79,7 @@ abstract class BaseFolderImporterServiceSpec extends BaseIntegrationSpec { Folder imported = importerService.importDomain(admin, basicParameters) as Folder check(imported) folderService.save(imported) - sessionFactory.currentSession.flush() + // sessionFactory.currentSession.flush() log.debug('Folder saved') folderService.get(imported.id) } diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy index 9c042dbcba..edceba22dd 100644 --- a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/DataBindFolderImporterProviderService.groovy @@ -44,15 +44,13 @@ abstract class DataBindFolderImporterProviderService

importDomains(User currentUser, FolderFileImporterProviderServiceParameters params) { checkImportParams(currentUser, params) - List folders = importFolders(currentUser, params.importFile.fileContents) - folders.collect { checkImport(it) } + importFolders(currentUser, params.importFile.fileContents) } Folder bindMapToFolder(User currentUser, Map folderMap) { @@ -62,6 +60,9 @@ abstract class DataBindFolderImporterProviderService

extends ContainerImporterProviderService { - @Autowired - AuthorityService authorityService - @Autowired FolderService folderService @@ -42,10 +40,4 @@ abstract class FolderImporterProviderService

Date: Thu, 3 Mar 2022 19:01:05 +0000 Subject: [PATCH 08/17] mc-9714 test: Import JSON of Folder with annotations --- .../importer/FolderJsonImporterServiceSpec.groovy | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy index 17cf4e1eec..0567900ee0 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy @@ -17,8 +17,10 @@ */ package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer +import uk.ac.ox.softeng.maurodatamapper.core.bootstrap.StandardEmailAddress import uk.ac.ox.softeng.maurodatamapper.core.container.Folder import uk.ac.ox.softeng.maurodatamapper.core.container.test.provider.BaseFolderImporterServiceSpec +import uk.ac.ox.softeng.maurodatamapper.core.facet.Annotation import grails.gorm.transactions.Rollback import grails.testing.mixin.integration.Integration @@ -80,4 +82,17 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { find { it.namespace == 'test.com/simple' && it.key == 'mdk2' && it.value == 'mdv2' } } } + + void 'test import Folder with annotations'() { + when: + Folder folder = importFolder('folderIncAnnotations') + + then: + folder.annotations.size() == 2 + folder.annotations.eachWithIndex { Annotation it, int i -> + it.createdBy == StandardEmailAddress.INTEGRATION_TEST + it.label == "Test Annotation ${i}" + it.description == "Test Annotation ${i} description" + } + } } From 1c6cc28badb9ec9cb8e16a1959f99a6297300927 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Thu, 3 Mar 2022 18:33:02 +0000 Subject: [PATCH 09/17] mc-9714 test: Import JSON of Folder with rules --- .../importer/FolderJsonImporterServiceSpec.groovy | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy index 0567900ee0..f1ed5d01bb 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy @@ -21,6 +21,7 @@ import uk.ac.ox.softeng.maurodatamapper.core.bootstrap.StandardEmailAddress import uk.ac.ox.softeng.maurodatamapper.core.container.Folder import uk.ac.ox.softeng.maurodatamapper.core.container.test.provider.BaseFolderImporterServiceSpec import uk.ac.ox.softeng.maurodatamapper.core.facet.Annotation +import uk.ac.ox.softeng.maurodatamapper.core.facet.Rule import grails.gorm.transactions.Rollback import grails.testing.mixin.integration.Integration @@ -95,4 +96,17 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { it.description == "Test Annotation ${i} description" } } + + void 'test import Folder with rules'() { + when: + Folder folder = importFolder('folderIncRules') + + then: + folder.rules.size() == 2 + folder.rules.eachWithIndex { Rule it, int i -> + it.createdBy == StandardEmailAddress.INTEGRATION_TEST + it.name == "Test Rule ${i}" + it.description == "Test Rule ${i} description" + } + } } From e94494384921f6fa93bb2ede6187a01424585b6b Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Sat, 11 Dec 2021 01:00:43 +0000 Subject: [PATCH 10/17] mc-9714 test: Import JSON of Folder with child Folders --- .../core/container/FolderService.groovy | 10 +++++ .../FolderJsonImporterServiceSpec.groovy | 38 +++++++++++++++++++ .../service/MultiFacetAwareService.groovy | 3 ++ 3 files changed, 51 insertions(+) diff --git a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy index 2b344bb2eb..6f52d963f5 100644 --- a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy +++ b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy @@ -650,6 +650,16 @@ class FolderService extends ContainerService { folder.createdBy = importingUser.emailAddress if (!folder.id) save(folder, insert: true, validate: false) // Skip validation to avoid error on null folderId/multiFacetAwareItemId checkFacetsAfterImportingMultiFacetAware(folder) + + // Check imported child Folder assocations breadth-first to avoid recursion + List childFolders = folder.childFolders?.toList() ?: [] + for (int i = 0; i < childFolders.size(); i++) { + childFolders[i].childFolders?.each { + if (!childFolders.contains(it)) childFolders << it + } + } + childFolders.each { checkImportedFolderAssociations(importingUser, it) } + log.debug('Folder associations checked') } } diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy index f1ed5d01bb..f5318b1a5e 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy @@ -109,4 +109,42 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { it.description == "Test Rule ${i} description" } } + + void 'test import Folder with child Folders'() { + when: + Folder folder = importFolder('folderIncEmptyChildFolder') + + then: + folder.childFolders.size() == 1 + folder.childFolders.find { it.label == 'Empty Child Folder' && !it.childFolders } + + when: + folder = importFolder('folderIncChildFolders') + + then: + folder.childFolders.size() == 2 + folder.childFolders.find { it.label == 'Empty Child Folder' && !it.childFolders } + folder.childFolders.find { it.label == 'Child Folder with Facets and Own Child Folder' }.tap { + metadata.size() == 3 + metadata.tap { + find { it.namespace == 'test.com' && it.key == 'mdk1' && it.value == 'mdv1' } + find { it.namespace == 'test.com/simple' && it.key == 'mdk1' && it.value == 'mdv1' } + find { it.namespace == 'test.com/simple' && it.key == 'mdk2' && it.value == 'mdv2' } + } + annotations.size() == 2 + annotations.eachWithIndex { Annotation annotation, int i -> + annotation.createdBy == StandardEmailAddress.INTEGRATION_TEST + annotation.label == "Test Annotation ${i}" + annotation.description == "Test Annotation ${i} description" + } + rules.size() == 2 + rules.eachWithIndex { Rule rule, int i -> + rule.createdBy == StandardEmailAddress.INTEGRATION_TEST + rule.name == "Test Rule ${i}" + rule.description == "Test Rule ${i} description" + } + childFolders.size() == 1 + childFolders.find { it.label == 'Inner Child Folder' && !it.childFolders } + } + } } diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/traits/service/MultiFacetAwareService.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/traits/service/MultiFacetAwareService.groovy index 916dea05f8..f449c5c547 100644 --- a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/traits/service/MultiFacetAwareService.groovy +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/traits/service/MultiFacetAwareService.groovy @@ -128,18 +128,21 @@ trait MultiFacetAwareService { if (multiFacetAware.metadata) { multiFacetAware.metadata.each { it.multiFacetAwareItemId = multiFacetAware.id + it.multiFacetAwareItemDomainType = it.domainType it.createdBy = it.createdBy ?: multiFacetAware.createdBy } } if (multiFacetAware.rules) { multiFacetAware.rules.each { it.multiFacetAwareItemId = multiFacetAware.id + it.multiFacetAwareItemDomainType = it.domainType it.createdBy = it.createdBy ?: multiFacetAware.createdBy } } if (multiFacetAware.annotations) { multiFacetAware.annotations.each { it.multiFacetAwareItemId = multiFacetAware.id + it.multiFacetAwareItemDomainType = it.domainType it.createdBy = it.createdBy ?: multiFacetAware.createdBy } } From 751d01c2b35c4ccc67ce2f8722356291cd2ea292 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Mon, 17 Jan 2022 03:55:06 +0000 Subject: [PATCH 11/17] mc-9714 test: Import invalid Folder --- .../FolderJsonImporterServiceSpec.groovy | 19 ++++++++++++++++++- .../BaseFolderImporterServiceSpec.groovy | 3 +++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy index f5318b1a5e..97fd2a325f 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy @@ -17,6 +17,7 @@ */ package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer +import uk.ac.ox.softeng.maurodatamapper.api.exception.ApiBadRequestException import uk.ac.ox.softeng.maurodatamapper.core.bootstrap.StandardEmailAddress import uk.ac.ox.softeng.maurodatamapper.core.container.Folder import uk.ac.ox.softeng.maurodatamapper.core.container.test.provider.BaseFolderImporterServiceSpec @@ -42,7 +43,23 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { 'json' } - // TODO: Test import null and invalid Folders + // TODO: Test import null Folder + + void 'test import invalid Folder'() { + when: 'given empty content' + importFolder(''.bytes) + + then: + ApiBadRequestException exception = thrown(ApiBadRequestException) + exception.errorCode == CANNOT_IMPORT_EMPTY_FILE_CODE + + when: 'given an empty JSON map' + importFolder('{}'.bytes) + + then: + exception = thrown(ApiBadRequestException) + exception.errorCode == CANNOT_IMPORT_JSON_CODE + } void 'test import Folder'() { when: diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy index ce8ceffda1..4dbf27e6a6 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy @@ -37,6 +37,9 @@ import java.nio.file.Paths @Slf4j abstract class BaseFolderImporterServiceSpec extends BaseIntegrationSpec { + static final String CANNOT_IMPORT_EMPTY_FILE_CODE = 'FBIP02' + static final String CANNOT_IMPORT_JSON_CODE = 'JIS03' + @Autowired FolderService folderService From 9439590e132426a53c35596e90cfcd00e247ea1c Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Tue, 18 Jan 2022 02:30:27 +0000 Subject: [PATCH 12/17] mc-9714 style: Add identifiers to feature test method names --- .../FolderJsonImporterServiceSpec.groovy | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy index 97fd2a325f..c10b4e47b2 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/FolderJsonImporterServiceSpec.groovy @@ -43,9 +43,9 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { 'json' } - // TODO: Test import null Folder + // TODO: FI01 : Test import null Folder - void 'test import invalid Folder'() { + void 'FI02 : test import invalid Folder'() { when: 'given empty content' importFolder(''.bytes) @@ -61,7 +61,7 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { exception.errorCode == CANNOT_IMPORT_JSON_CODE } - void 'test import Folder'() { + void 'FI03 : test import Folder'() { when: Folder folder = importFolder('emptyFolder') @@ -74,7 +74,7 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { } } - void 'test import empty Folder'() { + void 'FI04 : test import empty Folder'() { when: Folder folder = importFolder('emptyFolder') @@ -83,12 +83,12 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { !folderService.findAllModelsInFolder(folder) } - void 'test import Folder with description'() { + void 'FI05 : test import Folder with description'() { expect: importFolder('folderIncDescription').description == 'Test Folder description' } - void 'test import Folder with metadata'() { + void 'FI06 : test import Folder with metadata'() { when: Folder folder = importFolder('folderIncMetadata') @@ -101,7 +101,7 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { } } - void 'test import Folder with annotations'() { + void 'FI07 : test import Folder with annotations'() { when: Folder folder = importFolder('folderIncAnnotations') @@ -114,7 +114,7 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { } } - void 'test import Folder with rules'() { + void 'FI08 : test import Folder with rules'() { when: Folder folder = importFolder('folderIncRules') @@ -127,7 +127,7 @@ class FolderJsonImporterServiceSpec extends BaseFolderImporterServiceSpec { } } - void 'test import Folder with child Folders'() { + void 'FI09 : test import Folder with child Folders'() { when: Folder folder = importFolder('folderIncEmptyChildFolder') From 5714cf22566c25450ea58cbc8c7e7066255c2ea6 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Mon, 7 Mar 2022 13:51:41 +0000 Subject: [PATCH 13/17] mc-9714 Prefer def variable type over Object --- .../test/provider/BaseFolderImporterServiceSpec.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy index 4dbf27e6a6..9c99a940c2 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy @@ -54,13 +54,13 @@ abstract class BaseFolderImporterServiceSpec extends BaseIntegrationSpec { abstract String getImportType() @Override - Object setupSpec() { + def setupSpec() { basicParameters = new FolderFileImporterProviderServiceParameters() } @RunOnce @Override - Object setup() { + def setup() { resourcesPath = Paths.get(BuildSettings.BASE_DIR.absolutePath, 'src', 'integration-test', 'resources', importType) basicParameters.importFile = null } From 80abc14a23e60c009265326fdcfb9e6cc8b52e20 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Wed, 13 Apr 2022 15:00:16 +0100 Subject: [PATCH 14/17] WIP --- mdm-core/README.md | 1 + .../maurodatamapper/core/UrlMappings.groovy | 4 ++ .../BaseFolderImporterServiceSpec.groovy | 5 +++ .../url-mappings/tracked_endpoints.txt | 1 + .../ContainerImporterProviderService.groovy | 40 +------------------ ...erImporterProviderServiceParameters.groovy | 11 +++++ .../importer/ImporterProviderService.groovy | 5 +-- 7 files changed, 26 insertions(+), 41 deletions(-) diff --git a/mdm-core/README.md b/mdm-core/README.md index 95c76d23ab..3f629f4dee 100644 --- a/mdm-core/README.md +++ b/mdm-core/README.md @@ -72,6 +72,7 @@ Controller: folder | PUT | /api/folders/${id} | Action: update | GET | /api/folders/${id} | Action: show | GET | /api/folders/${id}/export | Action: export + | POST | /api/folders/${id}/import | Action: import Controller: importer | GET | /api/importer/parameters/${ns}?/${name}?/${version}? | Action: parameters diff --git a/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/UrlMappings.groovy b/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/UrlMappings.groovy index 0212cb4229..2d97473bd8 100644 --- a/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/UrlMappings.groovy +++ b/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/UrlMappings.groovy @@ -87,6 +87,10 @@ class UrlMappings { get "/export/$exporterNamespace/$exporterName/$exporterVersion"(controller: 'folder', action: 'exportFolder') } + group '/folders', { + post "/import/$importerNamespace/$importerName/$importerVersion"(controller: 'folder', action: 'importFolder') + } + '/versionedFolders'(resources: 'versionedFolder', excludes: DEFAULT_EXCLUDES) { '/folders'(resources: 'folder', excludes: DEFAULT_EXCLUDES) diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy index 9c99a940c2..f3794c94da 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/test/provider/BaseFolderImporterServiceSpec.groovy @@ -53,6 +53,10 @@ abstract class BaseFolderImporterServiceSpec extends BaseIntegrationSpec { abstract String getImportType() + Folder getTestFolder() { + folder + } + @Override def setupSpec() { basicParameters = new FolderFileImporterProviderServiceParameters() @@ -80,6 +84,7 @@ abstract class BaseFolderImporterServiceSpec extends BaseIntegrationSpec { basicParameters.importFile = new FileParameter(fileContents: bytes) Folder imported = importerService.importDomain(admin, basicParameters) as Folder + imported.parentFolder = testFolder check(imported) folderService.save(imported) // sessionFactory.currentSession.flush() diff --git a/mdm-core/src/integration-test/resources/url-mappings/tracked_endpoints.txt b/mdm-core/src/integration-test/resources/url-mappings/tracked_endpoints.txt index 102242adea..2fd03128b0 100644 --- a/mdm-core/src/integration-test/resources/url-mappings/tracked_endpoints.txt +++ b/mdm-core/src/integration-test/resources/url-mappings/tracked_endpoints.txt @@ -68,6 +68,7 @@ | PUT | /api/folders/${folderId}/folders/${id} | update | | GET | /api/folders/${folderId}/folders/${id} | show | | GET | /api/folders/${folderId}/export/${exporterNamespace}/${exporterName}/${exporterVersion} | exportFolder | +| GET | /api/folders/${folderId}/import/${exporterNamespace}/${exporterName}/${exporterVersion} | importFolder | | PUT | /api/folders/${folderId}/folder/${destinationFolderId} | changeFolder | | POST | /api/folders | save | | GET | /api/folders | index | diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/ContainerImporterProviderService.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/ContainerImporterProviderService.groovy index 46a06a6535..c2137fc7b3 100644 --- a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/ContainerImporterProviderService.groovy +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/ContainerImporterProviderService.groovy @@ -17,51 +17,15 @@ */ package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer -import uk.ac.ox.softeng.maurodatamapper.api.exception.ApiInternalException import uk.ac.ox.softeng.maurodatamapper.core.model.Container import uk.ac.ox.softeng.maurodatamapper.core.model.ContainerService +import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.ImporterProviderService import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.ImporterProviderServiceParameters -import uk.ac.ox.softeng.maurodatamapper.provider.MauroDataMapperService -import uk.ac.ox.softeng.maurodatamapper.security.User import groovy.transform.CompileStatic -import org.springframework.core.GenericTypeResolver @CompileStatic -abstract class ContainerImporterProviderService extends MauroDataMapperService { +abstract class ContainerImporterProviderService extends ImporterProviderService { abstract ContainerService getContainerService() - - abstract Boolean canImportMultipleDomains() - - abstract C importDomain(User currentUser, P params) - - abstract List importDomains(User currentUser, P params) - - List getImportBlacklistedProperties() { - ['id', 'lastUpdated', 'domainType'] - } - - Class

getImporterProviderServiceParametersClass() { - (Class

) GenericTypeResolver.resolveTypeArguments(getClass(), ContainerImporterProviderService).last() - } - - /** - * Returns a new instance object of the defined ImporterProviderServiceParameters parameter. - * - * The method makes use of Class.newInstance(), this was deprecated in Java9 however is still part of the Groovy extension. - * Due to the fact Groovy auto adds empty constructors to all its classes the Java9 method of getDeclaredConstructor.getNewInstance() fails - * as there is no declared constructor. - * - * @return - * @throws ApiInternalException - */ - @SuppressWarnings('GrDeprecatedAPIUsage') - P createNewImporterProviderServiceParameters() throws ApiInternalException { - try { - return importerProviderServiceParametersClass.newInstance() - } catch (InstantiationException | IllegalAccessException ex) { - throw new ApiInternalException('IP01', 'Cannot create new import params instance', ex) - } - } } diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy index 245be17eec..5e6e6663a5 100644 --- a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy @@ -18,9 +18,20 @@ package uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.parameter import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.ImporterProviderServiceParameters +import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.config.ImportGroupConfig +import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.config.ImportParameterConfig import groovy.transform.CompileStatic @CompileStatic class FolderImporterProviderServiceParameters implements ImporterProviderServiceParameters { + @ImportParameterConfig( + displayName = 'Parent Folder', + description = 'The parent folder into which the Model/s should be imported.', + order = 0, + group = @ImportGroupConfig( + name = 'Model', + order = 0 + )) + UUID parentFolderId } diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/provider/importer/ImporterProviderService.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/provider/importer/ImporterProviderService.groovy index f1bf687881..23e7a496db 100644 --- a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/provider/importer/ImporterProviderService.groovy +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/provider/importer/ImporterProviderService.groovy @@ -18,18 +18,17 @@ package uk.ac.ox.softeng.maurodatamapper.core.provider.importer import uk.ac.ox.softeng.maurodatamapper.api.exception.ApiInternalException -import uk.ac.ox.softeng.maurodatamapper.core.model.CatalogueItem import uk.ac.ox.softeng.maurodatamapper.core.provider.ProviderType import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.parameter.ImporterProviderServiceParameters import uk.ac.ox.softeng.maurodatamapper.provider.MauroDataMapperService import uk.ac.ox.softeng.maurodatamapper.security.User +import uk.ac.ox.softeng.maurodatamapper.traits.domain.MdmDomain import groovy.transform.CompileStatic import org.springframework.core.GenericTypeResolver @CompileStatic -abstract class ImporterProviderService - extends MauroDataMapperService { +abstract class ImporterProviderService extends MauroDataMapperService { abstract D importDomain(User currentUser, T params) From c87169c49ce9ad175fb4145cf7328f95025c931c Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Mon, 21 Mar 2022 15:10:07 +0000 Subject: [PATCH 15/17] WIP: "NULL not allowed for column CREATED_BY" even after checking imported Folder associations Error encountered in FolderFunctionalSpec.FE07 --- .../core/container/FolderController.groovy | 55 ++++++- .../core/container/FolderInterceptor.groovy | 7 + .../core/container/FolderService.groovy | 25 ++-- .../container/FolderFunctionalSpec.groovy | 138 +++++++++++++++++- 4 files changed, 208 insertions(+), 17 deletions(-) diff --git a/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/container/FolderController.groovy b/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/container/FolderController.groovy index c5b185f55c..65d4dc45ff 100644 --- a/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/container/FolderController.groovy +++ b/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/container/FolderController.groovy @@ -17,10 +17,15 @@ */ package uk.ac.ox.softeng.maurodatamapper.core.container +import uk.ac.ox.softeng.maurodatamapper.api.exception.ApiException +import uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.FolderImporterProviderService +import uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer.parameter.FolderImporterProviderServiceParameters import uk.ac.ox.softeng.maurodatamapper.core.controller.EditLoggingController +import uk.ac.ox.softeng.maurodatamapper.core.importer.ImporterService import uk.ac.ox.softeng.maurodatamapper.core.model.CatalogueItem import uk.ac.ox.softeng.maurodatamapper.core.provider.MauroDataMapperServiceProviderService import uk.ac.ox.softeng.maurodatamapper.core.provider.exporter.ExporterProviderService +import uk.ac.ox.softeng.maurodatamapper.core.provider.importer.ImporterProviderService import uk.ac.ox.softeng.maurodatamapper.core.rest.transport.search.SearchParams import uk.ac.ox.softeng.maurodatamapper.core.search.SearchService import uk.ac.ox.softeng.maurodatamapper.hibernate.search.PaginatedHibernateSearchResult @@ -28,8 +33,11 @@ import uk.ac.ox.softeng.maurodatamapper.security.SecurityPolicyManagerService import grails.gorm.transactions.Transactional import grails.web.http.HttpHeaders +import grails.web.mime.MimeType import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired +import org.springframework.validation.Errors +import org.springframework.web.multipart.support.AbstractMultipartHttpServletRequest import static org.springframework.http.HttpStatus.CREATED import static org.springframework.http.HttpStatus.NO_CONTENT @@ -41,6 +49,7 @@ class FolderController extends EditLoggingController { static responseFormats = ['json', 'xml'] MauroDataMapperServiceProviderService mauroDataMapperServiceProviderService + ImporterService importerService FolderService folderService SearchService mdmCoreSearchService VersionedFolderService versionedFolderService @@ -185,11 +194,54 @@ class FolderController extends EditLoggingController { log.info("Exporting Folder using ${exporter.displayName}") ByteArrayOutputStream outputStream = exporter.exportDomain(currentUser, params.folderId) if (!outputStream) return errorResponse(UNPROCESSABLE_ENTITY, 'Folder could not be exported') - log.info('Export complete') + log.info('Single Folder Export complete') render(file: outputStream.toByteArray(), fileName: "${instance.label}.${exporter.fileExtension}", contentType: exporter.fileType) } + @Transactional + def importFolder() throws ApiException { + FolderImporterProviderService importer = + mauroDataMapperServiceProviderService.findImporterProvider(params.importerNamespace, params.importerName, params.importerVersion) as FolderImporterProviderService + if (!importer) return notFound(ImporterProviderService, "${params.importerNamespace}:${params.importerName}:${params.importerVersion}") + + FolderImporterProviderServiceParameters importerProviderServiceParameters = request.contentType.startsWith(MimeType.MULTIPART_FORM.name) + ? importerService.extractImporterProviderServiceParameters(importer, request as AbstractMultipartHttpServletRequest) + : importerService.extractImporterProviderServiceParameters(importer, request) + + Errors errors = importerService.validateParameters(importerProviderServiceParameters, importer.importerProviderServiceParametersClass) + if (errors.hasErrors()) { + transactionStatus.setRollbackOnly() + return respond(errors) + } + + if (!currentUserSecurityPolicyManager.userCanCreateResourceId(resource, null, Folder, importerProviderServiceParameters.parentFolderId)) { + if (!currentUserSecurityPolicyManager.userCanReadSecuredResourceId(Folder, importerProviderServiceParameters.parentFolderId)) { + return notFound(Folder, importerProviderServiceParameters.parentFolderId) + } + return forbiddenDueToPermissions() + } + + Folder folder = importer.importDomain(currentUser, importerProviderServiceParameters) as Folder + if (!folder) { + transactionStatus.setRollbackOnly() + return errorResponse(UNPROCESSABLE_ENTITY, 'No folder imported') + } + + folder.parentFolder = folderService.get(importerProviderServiceParameters.parentFolderId) + folderService.validate(folder) + if (folder.hasErrors()) { + transactionStatus.setRollbackOnly() + return respond(folder.errors) + } + + log.debug('No errors in imported folder') + log.info('Single Folder Import complete') + + saveResource(folder) + saveResponse(folder) + } + @Override protected Folder queryForResource(Serializable id) { folderService.get(id) @@ -239,7 +291,6 @@ class FolderController extends EditLoggingController { namespace: hasProperty('namespace') ? this.namespace : null)) respond instance, [status: OK, view: 'update', model: [userSecurityPolicyManager: currentUserSecurityPolicyManager, folder: instance]] } - } } diff --git a/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/container/FolderInterceptor.groovy b/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/container/FolderInterceptor.groovy index 1613bcb0c4..53191dfda2 100644 --- a/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/container/FolderInterceptor.groovy +++ b/mdm-core/grails-app/controllers/uk/ac/ox/softeng/maurodatamapper/core/container/FolderInterceptor.groovy @@ -72,6 +72,13 @@ class FolderInterceptor extends SecurableResourceInterceptor { return true } + if (actionName == 'importFolder') { + if (!currentUserSecurityPolicyManager.userCanEditSecuredResourceId(Folder, id)) { + return forbiddenOrNotFound(false, Folder, id) + } + return true + } + checkActionAuthorisationOnSecuredResource(Folder, getId(), true) } } diff --git a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy index 6f52d963f5..19f1050883 100644 --- a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy +++ b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy @@ -362,7 +362,6 @@ class FolderService extends ContainerService { throw new ApiNotYetImplementedException('MSXX', 'Folder permission copying') } log.warn('Permission copying is not yet implemented') - } log.debug('Validating and saving copy') setFolderRefinesFolder(copiedFolder, original, copier) @@ -645,21 +644,19 @@ class FolderService extends ContainerService { if (parentCache) parentCache.addDiffCache(folder.path, fDiffCache) fDiffCache } - - Folder checkImportedFolderAssociations(User importingUser, Folder folder) { - folder.createdBy = importingUser.emailAddress - if (!folder.id) save(folder, insert: true, validate: false) // Skip validation to avoid error on null folderId/multiFacetAwareItemId - checkFacetsAfterImportingMultiFacetAware(folder) - - // Check imported child Folder assocations breadth-first to avoid recursion - List childFolders = folder.childFolders?.toList() ?: [] - for (int i = 0; i < childFolders.size(); i++) { - childFolders[i].childFolders?.each { - if (!childFolders.contains(it)) childFolders << it + + void checkImportedFolderAssociations(User importingUser, Folder folder) { + // Traverse breadth-first to avoid recursion + List folders = [folder] + for (int i = 0; i < folders.size(); i++) { + folders[i].checkPath() + folders[i].createdBy = importingUser.emailAddress + if (!folders[i].id) save(folders[i], validate: false) // Skip validation to avoid error on null folderId/multiFacetAwareItemId + checkFacetsAfterImportingMultiFacetAware(folders[i]) + folders[i].childFolders?.each { + if (!folders.contains(it)) folders << it } } - childFolders.each { checkImportedFolderAssociations(importingUser, it) } - log.debug('Folder associations checked') } } diff --git a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/FolderFunctionalSpec.groovy b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/FolderFunctionalSpec.groovy index d54d472149..01d2bef427 100644 --- a/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/FolderFunctionalSpec.groovy +++ b/mdm-core/src/integration-test/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/FolderFunctionalSpec.groovy @@ -23,9 +23,13 @@ import uk.ac.ox.softeng.maurodatamapper.test.functional.ResourceFunctionalSpec import grails.gorm.transactions.Rollback import grails.testing.mixin.integration.Integration import grails.testing.spock.RunOnce +import grails.web.mime.MimeType import groovy.util.logging.Slf4j import io.micronaut.core.type.Argument import io.micronaut.http.HttpStatus +import spock.lang.Shared + +import static uk.ac.ox.softeng.maurodatamapper.core.bootstrap.StandardEmailAddress.getFUNCTIONAL_TEST /** *

@@ -42,6 +46,7 @@ import io.micronaut.http.HttpStatus
  *  |   POST   | /api/folders/${folderId}/search   | Action: search
  *
  *  |   GET    | /api/folders/${folderId}/export    | Action: export
+ *  |   POST   | /api/folders/${folderId}/import    | Action: import
  * 
* @see uk.ac.ox.softeng.maurodatamapper.core.container.FolderController */ @@ -49,10 +54,17 @@ import io.micronaut.http.HttpStatus @Slf4j class FolderFunctionalSpec extends ResourceFunctionalSpec { + @Shared + UUID parentFolderId + @RunOnce @Rollback def setup() { - assert Folder.count() == 0 + log.debug('Check and setup test data') + sessionFactory.currentSession.flush() + parentFolderId = new Folder(label: 'Parent Functional Test Folder', createdBy: FUNCTIONAL_TEST).save(flush: true).id + assert parentFolderId + assert Folder.count() == 1 } @Override @@ -94,6 +106,14 @@ class FolderFunctionalSpec extends ResourceFunctionalSpec { }''' } + @Override + void verifyR1EmptyIndexResponse() { + verifyResponse(HttpStatus.OK, response) + assert response.body().count == 1 + assert response.body().items.size() == 1 + assert response.body().items[0].label == 'Parent Functional Test Folder' + } + void 'Test the save action fails when using the same label persists an instance'() { given: List createdIds = [] @@ -544,4 +564,120 @@ class FolderFunctionalSpec extends ResourceFunctionalSpec { cleanup: ids.reverseEach { cleanUpData(it) } } + + void 'FE06 : test import Folder JSON'() { + when: 'The save action is executed with valid data' + POST('', validJson) + + then: 'The response is correct' + response.status == HttpStatus.CREATED + response.body().id + + when: 'The export action is executed with a valid instance' + String id = response.body().id + GET("${id}/export/uk.ac.ox.softeng.maurodatamapper.core.container.provider.exporter/FolderJsonExporterService/1.0", STRING_ARG) + + then: 'The response is correct' + verifyResponse(HttpStatus.OK, jsonCapableResponse) + + when: 'The delete action is executed with a valid instance' + String exportedJson = jsonCapableResponse.body() + DELETE(getDeleteEndpoint(id)) + + then: 'The response is correct' + response.status == HttpStatus.NO_CONTENT + + when: 'The import action is executed with valid data' + POST('import/uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer/FolderJsonImporterService/1.0', [ + parentFolderId: parentFolderId.toString(), + importFile : [ + fileName : 'FT Import', + fileType : MimeType.JSON_API.name, + fileContents: exportedJson.bytes.toList() + ] + ], STRING_ARG) + + then: 'The response is correct' + verifyJsonResponse(HttpStatus.CREATED, '''{ + "id": "${json-unit.matches:id}", + "label": "Functional Test Folder", + "lastUpdated": "${json-unit.matches:offsetDateTime}", + "domainType": "Folder", + "hasChildFolders": false, + "readableByEveryone": false, + "readableByAuthenticatedUsers": false, + "availableActions": [ + "delete", + "show", + "update" + ] +}''') + } + + void 'FE07 : test import Folder with child Folders JSON'() { + given: + List responseStatuses = [] + List ids = [] + Closure saveResponse = { -> + responseStatuses << response.status + ids << response.body()?.id + } + + when: 'The save actions are executed with valid data' + POST('', validJson) + saveResponse() + POST("${ids[0]}/folders", [label: 'Functional Test Folder 2']) + saveResponse() + POST("${ids[0]}/folders", [label: 'Functional Test Folder 3']) + saveResponse() + POST("${ids[2]}/folders", [label: 'Functional Test Folder 4']) + saveResponse() + + then: 'The responses are correct' + responseStatuses.every { it == HttpStatus.CREATED } + ids.every() + + when: 'The export action is executed with a valid instance' + GET("${ids[0]}/export/uk.ac.ox.softeng.maurodatamapper.core.container.provider.exporter/FolderJsonExporterService/1.0", STRING_ARG) + + then: 'The response is correct' + verifyResponse(HttpStatus.OK, jsonCapableResponse) + + when: 'The delete action is executed with a valid instance' + String exportedJson = jsonCapableResponse.body() + responseStatuses.clear() + ids.reverseEach { + DELETE(getDeleteEndpoint(it)) + responseStatuses << response.status + } + + then: 'The response is correct' + responseStatuses.every { it == HttpStatus.NO_CONTENT } + + when: 'The import action is executed with valid data' + POST('import/uk.ac.ox.softeng.maurodatamapper.core.container.provider.importer/FolderJsonImporterService/1.0', [ + parentFolderId: parentFolderId.toString(), + importFile : [ + fileName : 'FT Import', + fileType : MimeType.JSON_API.name, + fileContents: exportedJson.bytes.toList() + ] + ], STRING_ARG) + + then: 'The response is correct' + verifyJsonResponse(HttpStatus.CREATED, '''{ + "id": "${json-unit.matches:id}", + "label": "Functional Test Folder", + "lastUpdated": "${json-unit.matches:offsetDateTime}", + "domainType": "Folder", + "hasChildFolders": true, + "readableByEveryone": false, + "readableByAuthenticatedUsers": false, + "availableActions": [ + "delete", + "show", + "update" + ] +}''') + } } From cbbe2229e8492a14520d45acf36f0be23c09ef5d Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Fri, 25 Mar 2022 14:20:31 +0000 Subject: [PATCH 16/17] Fix Parent Folder parameter description, revert saveArgs argument change --- .../maurodatamapper/core/container/FolderService.groovy | 2 +- .../parameter/FolderImporterProviderServiceParameters.groovy | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy index 19f1050883..006408f772 100644 --- a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy +++ b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy @@ -184,7 +184,7 @@ class FolderService extends ContainerService { (folder as GormEntity).save(saveArgs) sessionFactory.currentSession.flush() } else { - (folder as GormEntity).save(saveArgs) + (folder as GormEntity).save(args) } folder } diff --git a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy index 5e6e6663a5..92e643b59c 100644 --- a/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy +++ b/mdm-core/src/main/groovy/uk/ac/ox/softeng/maurodatamapper/core/container/provider/importer/parameter/FolderImporterProviderServiceParameters.groovy @@ -27,10 +27,10 @@ import groovy.transform.CompileStatic class FolderImporterProviderServiceParameters implements ImporterProviderServiceParameters { @ImportParameterConfig( displayName = 'Parent Folder', - description = 'The parent folder into which the Model/s should be imported.', + description = 'The parent Folder into which the Folder should be imported.', order = 0, group = @ImportGroupConfig( - name = 'Model', + name = 'Folder', order = 0 )) UUID parentFolderId From 757260ac64de1d32296d0d392889d635446ef220 Mon Sep 17 00:00:00 2001 From: Adei Josol Date: Fri, 25 Mar 2022 14:21:30 +0000 Subject: [PATCH 17/17] Validate and do not save when checking imported Folder associations --- .../core/container/FolderService.groovy | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy index 006408f772..d076c74959 100644 --- a/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy +++ b/mdm-core/grails-app/services/uk/ac/ox/softeng/maurodatamapper/core/container/FolderService.groovy @@ -646,17 +646,11 @@ class FolderService extends ContainerService { } void checkImportedFolderAssociations(User importingUser, Folder folder) { - // Traverse breadth-first to avoid recursion - List folders = [folder] - for (int i = 0; i < folders.size(); i++) { - folders[i].checkPath() - folders[i].createdBy = importingUser.emailAddress - if (!folders[i].id) save(folders[i], validate: false) // Skip validation to avoid error on null folderId/multiFacetAwareItemId - checkFacetsAfterImportingMultiFacetAware(folders[i]) - folders[i].childFolders?.each { - if (!folders.contains(it)) folders << it - } - } + folder.checkPath() + folder.createdBy = importingUser.emailAddress + folder.validate() + checkFacetsAfterImportingMultiFacetAware(folder) + folder.childFolders?.each { checkImportedFolderAssociations(importingUser, it) } log.debug('Folder associations checked') } }