From b244b56e7bfa0583eb6fec4dba46b908186be6f0 Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Wed, 12 Apr 2017 07:36:12 +0100 Subject: [PATCH] Feature/rm 4639 rest api refactoring 2 --- .../rm-automation-community-rest-api/pom.xml | 18 +- .../alfresco/rest/core/RMRestProperties.java | 35 +- .../org/alfresco/rest/core/RMRestWrapper.java | 20 +- .../alfresco/rest/core/RestAPIFactory.java | 139 +- .../IdNamePair.java} | 13 +- .../Owner.java} | 21 +- .../Path.java} | 20 +- .../ReviewPeriod.java} | 11 +- .../rm/community/model/fileplan/FilePlan.java | 99 + .../model/fileplan/FilePlanProperties.java | 78 + .../FilePlanComponentAlias.java | 1 - .../FilePlanComponentAspects.java | 4 +- .../FilePlanComponentFields.java | 92 +- .../FilePlanComponentType.java | 2 - .../rm/community/model/record/Record.java | 103 + .../RecordBodyFile.java | 10 +- .../RecordContent.java} | 17 +- .../model/record/RecordProperties.java | 203 + .../model/recordcategory/RecordCategory.java | 101 + .../recordcategory/RecordCategoryChild.java | 112 + .../RecordCategoryChildCollection.java | 41 + .../RecordCategoryChildEntry.java | 48 + .../RecordCategoryChildProperties.java} | 93 +- .../RecordCategoryCollection.java | 41 + .../recordcategory/RecordCategoryEntry.java | 50 + .../RecordCategoryProperties.java | 95 + .../model/recordfolder/RecordFolder.java | 100 + .../RecordFolderCollection.java} | 7 +- .../RecordFolderEntry.java} | 13 +- .../recordfolder/RecordFolderProperties.java | 119 + .../rest/rm/community/model/site/RMSite.java | 4 +- .../rm/community/model/transfer/Transfer.java | 89 + .../model/transfer/TransferChild.java | 110 + .../transfer/TransferChildCollection.java | 40 + .../model/transfer/TransferChildEntry.java | 55 + .../transfer/TransferChildProperties.java | 223 + .../model/transfer/TransferCollection.java | 40 + .../model/transfer/TransferEntry.java | 56 + .../model/transfer/TransferProperties.java | 88 + .../transfercontainer/TransferContainer.java | 95 + .../TransferContainerProperties.java | 76 + .../unfiledcontainer/UnfiledContainer.java | 97 + .../UnfiledContainerChild.java} | 110 +- .../UnfiledContainerChildCollection.java | 41 + .../UnfiledContainerChildEntry.java | 48 + .../UnfiledContainerChildProperties.java | 196 + .../UnfiledContainerProperties.java | 71 + .../unfiledcontainer/UnfiledRecordFolder.java | 113 + .../rm/community/requests/RMModelRequest.java | 20 +- .../community/requests/gscore/GSCoreAPI.java | 182 + .../requests/gscore/api/FilePlanAPI.java | 175 + .../{igCoreAPI => gscore/api}/FilesAPI.java | 22 +- .../{igCoreAPI => gscore/api}/RMSiteAPI.java | 20 +- .../{igCoreAPI => gscore/api}/RMUserAPI.java | 47 +- .../gscore/api/RecordCategoryAPI.java | 242 ++ .../requests/gscore/api/RecordFolderAPI.java | 291 ++ .../{igCoreAPI => gscore/api}/RecordsAPI.java | 134 +- .../requests/gscore/api/TransferAPI.java | 125 + .../gscore/api/TransferContainerAPI.java | 172 + .../gscore/api/UnfiledContainerAPI.java | 268 ++ .../gscore/api/UnfiledRecordFolderAPI.java | 292 ++ .../igCoreAPI/FilePlanComponentAPI.java | 351 -- .../requests/igCoreAPI/RestIGCoreAPI.java | 111 - .../util/FilePlanComponentMixIn.java | 7 +- .../rest/rm/community/util/PojoUtility.java | 51 +- .../util/ReviewPeriodSerializer.java | 8 +- .../src/main/resources/config.properties | 2 +- .../rm/community/base/BaseRMRestTest.java | 349 +- .../rest/rm/community/base/TestData.java | 49 +- .../fileplancomponents/DeleteRecordTests.java | 181 +- .../ElectronicRecordTests.java | 350 +- .../fileplancomponents/FilePlanTests.java | 279 +- .../fileplancomponents/FileRecordsTests.java | 421 +- .../NonElectronicRecordTests.java | 432 +- .../fileplancomponents/ReadRecordTests.java | 487 ++- .../RecordCategoryTest.java | 411 -- .../RecordCategoryTests.java | 331 ++ .../fileplancomponents/RecordFolderTests.java | 494 +-- .../UnfiledContainerTests.java | 353 ++ .../UnfiledRecordsFolderTests.java | 317 +- .../UpdateRecordsTests.java | 246 +- .../files/DeclareDocumentAsRecordTests.java | 39 +- .../rest/rm/community/site/RMSiteTests.java | 2 +- .../utils/FilePlanComponentsUtil.java | 277 +- .../rm-public-rest-context.xml | 178 +- rm-community/rm-community-repo/pom.xml | 2 +- .../org/alfresco/rm/rest/api/RMNodes.java | 51 +- .../org/alfresco/rm/rest/api/RMSites.java | 13 +- .../org/alfresco/rm/rest/api/Records.java | 63 - .../FileplanComponentChildrenRelation.java | 111 - .../FileplanComponentsEntityResource.java | 93 - .../fileplans/FilePlanChildrenRelation.java | 193 + .../api/fileplans/FilePlanEntityResource.java | 125 + .../rm/rest/api/fileplans/package-info.java | 37 + .../rest/api/files/FilesEntityResource.java | 72 +- .../rm/rest/api/files/package-info.java | 4 +- .../rest/api/impl/ApiNodesModelFactory.java | 839 ++++ .../api/impl/FilePlanComponentsApiUtils.java | 961 +++++ .../rm/rest/api/impl/RMNodesImpl.java | 497 --- .../rm/rest/api/impl/RMSitesImpl.java | 175 +- .../rm/rest/api/impl/RecordsImpl.java | 202 - .../rm/rest/api/impl/SearchTypesFactory.java | 292 ++ .../alfresco/rm/rest/api/model/FilePlan.java | 43 + .../rest/api/model/FileplanComponentNode.java | 106 - .../alfresco/rm/rest/api/model/RMNode.java | 223 + .../alfresco/rm/rest/api/model/RMSite.java | 33 +- .../model/{RecordNode.java => Record.java} | 47 +- .../rm/rest/api/model/RecordCategory.java | 56 + ...goryNode.java => RecordCategoryChild.java} | 70 +- .../rm/rest/api/model/RecordFolder.java | 57 + .../rm/rest/api/model/SiteUpdate.java | 117 - .../rm/rest/api/model/TargetContainer.java | 14 +- .../alfresco/rm/rest/api/model/Transfer.java | 190 + ...cordFolderNode.java => TransferChild.java} | 60 +- .../rm/rest/api/model/TransferContainer.java | 173 + .../rm/rest/api/model/UnfiledChild.java | 69 + .../rm/rest/api/model/UnfiledContainer.java | 43 + .../rest/api/model/UnfiledContainerChild.java | 37 + .../rest/api/model/UnfiledRecordFolder.java | 43 + .../api/model/UnfiledRecordFolderChild.java | 53 + .../rm/rest/api/model/UploadInfo.java | 124 + .../RecordCategoriesEntityResource.java | 131 + .../RecordCategoryChildrenRelation.java | 184 + .../package-info.java | 8 +- .../RecordFolderChildrenRelation.java | 223 + .../RecordFolderEntityResource.java | 127 + .../rest/api/recordfolders/package-info.java | 37 + .../api/records/RecordsEntityResource.java | 161 +- .../rm/rest/api/records/package-info.java | 4 +- .../rest/api/sites/RMSiteEntityResource.java | 84 +- .../rm/rest/api/sites/package-info.java | 2 +- .../TransferContainerChildrenRelation.java | 140 + .../TransferContainerEntityResource.java | 116 + .../api/transfercontainers/package-info.java | 37 + .../transfers/TransferChildrenRelation.java | 137 + .../api/transfers/TransferEntityResource.java | 100 + .../rm/rest/api/transfers/package-info.java | 37 + .../UnfiledContainerChildrenRelation.java | 237 ++ .../UnfiledContainerEntityResource.java | 116 + .../api/unfiledcontainers/package-info.java | 37 + .../UnfiledRecordFolderChildrenRelation.java | 246 ++ .../UnfiledRecordFolderEntityResource.java | 131 + .../unfiledrecordfolders/package-info.java | 37 + ...planComponentChildrenRelationUnitTest.java | 107 - ...eplanComponentsEntityResourceUnitTest.java | 121 - .../impl/RMNodesImplRelativePathUnitTest.java | 292 -- .../rm/rest/api/impl/RMNodesImplUnitTest.java | 671 --- .../rm/rest/api/impl/RMSitesImplUnitTest.java | 5 +- .../rm/rest/api/impl/RecordsImplUnitTest.java | 542 ++- .../sites/RMSiteEntityResourceUnitTest.java | 39 +- .../rm-community-rest-api-explorer/pom.xml | 2 +- .../main/webapp/definitions/gs-core-api.yaml | 3691 +++++++++++++++++ .../main/webapp/definitions/ig-core-api.yaml | 1198 ------ .../src/main/webapp/index.html | 23 +- 154 files changed, 18151 insertions(+), 7134 deletions(-) rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/{fileplancomponents/FilePlanComponentIdNamePair.java => common/IdNamePair.java} (83%) rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/{fileplancomponents/FilePlanComponentUserInfo.java => common/Owner.java} (76%) rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/{fileplancomponents/FilePlanComponentPath.java => common/Path.java} (75%) rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/{fileplancomponents/FilePlanComponentReviewPeriod.java => common/ReviewPeriod.java} (84%) create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplan/FilePlan.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplan/FilePlanProperties.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/Record.java rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/{fileplancomponents => record}/RecordBodyFile.java (90%) rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/{fileplancomponents/FilePlanComponentContent.java => record/RecordContent.java} (85%) create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordProperties.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategory.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChild.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildCollection.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildEntry.java rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/{fileplancomponents/FilePlanComponentProperties.java => recordcategory/RecordCategoryChildProperties.java} (55%) create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryCollection.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryEntry.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryProperties.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolder.java rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/{fileplancomponents/FilePlanComponentsCollection.java => recordfolder/RecordFolderCollection.java} (83%) rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/{fileplancomponents/FilePlanComponentEntry.java => recordfolder/RecordFolderEntry.java} (79%) create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderProperties.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/Transfer.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChild.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildCollection.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildEntry.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildProperties.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferCollection.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferEntry.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferProperties.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfercontainer/TransferContainer.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfercontainer/TransferContainerProperties.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainer.java rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/{fileplancomponents/FilePlanComponent.java => unfiledcontainer/UnfiledContainerChild.java} (59%) create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildCollection.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildEntry.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildProperties.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerProperties.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledRecordFolder.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/GSCoreAPI.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/FilePlanAPI.java rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/{igCoreAPI => gscore/api}/FilesAPI.java (77%) rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/{igCoreAPI => gscore/api}/RMSiteAPI.java (91%) rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/{igCoreAPI => gscore/api}/RMUserAPI.java (86%) create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordCategoryAPI.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordFolderAPI.java rename rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/{igCoreAPI => gscore/api}/RecordsAPI.java (53%) create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/TransferAPI.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/TransferContainerAPI.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/UnfiledContainerAPI.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/UnfiledRecordFolderAPI.java delete mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/FilePlanComponentAPI.java delete mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RestIGCoreAPI.java delete mode 100644 rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordCategoryTest.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordCategoryTests.java create mode 100644 rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UnfiledContainerTests.java delete mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/Records.java delete mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentChildrenRelation.java delete mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentsEntityResource.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanChildrenRelation.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanEntityResource.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/package-info.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/ApiNodesModelFactory.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/FilePlanComponentsApiUtils.java delete mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RMNodesImpl.java delete mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RecordsImpl.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/FilePlan.java delete mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/FileplanComponentNode.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMNode.java rename rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/{RecordNode.java => Record.java} (61%) create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordCategory.java rename rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/{RecordCategoryNode.java => RecordCategoryChild.java} (50%) create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordFolder.java delete mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/SiteUpdate.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/Transfer.java rename rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/{RecordFolderNode.java => TransferChild.java} (56%) create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TransferContainer.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledChild.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledContainer.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledContainerChild.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledRecordFolder.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledRecordFolderChild.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UploadInfo.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoriesEntityResource.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoryChildrenRelation.java rename rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/{fileplancomponents => recordcategories}/package-info.java (85%) create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderChildrenRelation.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderEntityResource.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/package-info.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/TransferContainerChildrenRelation.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/TransferContainerEntityResource.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/package-info.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/TransferChildrenRelation.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/TransferEntityResource.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/package-info.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerChildrenRelation.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerEntityResource.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/package-info.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderChildrenRelation.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderEntityResource.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/package-info.java delete mode 100644 rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentChildrenRelationUnitTest.java delete mode 100644 rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentsEntityResourceUnitTest.java delete mode 100644 rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMNodesImplRelativePathUnitTest.java delete mode 100644 rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMNodesImplUnitTest.java create mode 100644 rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml delete mode 100644 rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/ig-core-api.yaml diff --git a/rm-automation/rm-automation-community-rest-api/pom.xml b/rm-automation/rm-automation-community-rest-api/pom.xml index 7ecbbea085..151b0bcce1 100644 --- a/rm-automation/rm-automation-community-rest-api/pom.xml +++ b/rm-automation/rm-automation-community-rest-api/pom.xml @@ -45,11 +45,6 @@ - - com.jayway.restassured - rest-assured - 2.9.0 - org.alfresco.tas restapi-test @@ -61,16 +56,17 @@ ${tas.restapi.version} test-jar + + org.projectlombok + lombok + 1.16.10 + provided + + org.jglue.fluent-json fluent-json ${fluent.json.version} - - org.projectlombok - lombok - 1.16.10 - provided - diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestProperties.java index b76699e7c0..9f0e8adf35 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestProperties.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestProperties.java @@ -30,12 +30,15 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import lombok.Getter; + /** * Extends {@link RestProperties} to be able to change/add properties * * @author Tuna Aksoy * @since 2.6 */ +@Getter @Configuration @PropertySource(value = {"classpath:default.properties", "classpath:config.properties"}) @PropertySource(value = "classpath:module.properties", ignoreResourceNotFound = true) @@ -53,36 +56,4 @@ public class RMRestProperties extends RestProperties @Value ("${rest.rmPath}") private String restRmPath; - - /** - * @return the scheme - */ - public String getScheme() - { - return this.scheme; - } - - /** - * @return the server - */ - public String getServer() - { - return this.server; - } - - /** - * @return the port - */ - public String getPort() - { - return this.port; - } - - /** - * @return the restRmPath - */ - public String getRestRmPath() - { - return this.restRmPath; - } } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java index e154fc9ea9..807bb66a12 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java @@ -34,15 +34,17 @@ import org.alfresco.rest.model.RestHtmlResponse; import org.alfresco.rest.model.RestSiteModel; import org.alfresco.rest.model.RestSiteModelsCollection; import org.alfresco.rest.requests.coreAPI.RestCoreAPI; -import org.alfresco.rest.rm.community.requests.igCoreAPI.RestIGCoreAPI; +import org.alfresco.rest.rm.community.requests.gscore.GSCoreAPI; import org.alfresco.utility.model.UserModel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; +import lombok.Getter; + /** - * Extends {@link RestWrapper} in order to call IG APIs with our own properties + * Extends {@link RestWrapper} in order to call GS APIs with our own properties * * @author Tuna Aksoy * @since 2.6 @@ -54,12 +56,14 @@ public class RMRestWrapper /** The class that wraps the ReST APIs from core. */ @Autowired private RestWrapper restWrapper; + @Autowired + @Getter private RMRestProperties rmRestProperties; - public RestIGCoreAPI withIGCoreAPI() + public GSCoreAPI withGSCoreAPI() { - return new RestIGCoreAPI(this, rmRestProperties); + return new GSCoreAPI(this, getRmRestProperties()); } /** Get the core class that wraps the ReST APIs. */ @@ -159,12 +163,4 @@ public class RMRestWrapper { return restWrapper.processHtmlResponse(simpleRequest); } - - /** - * @return the rmRestProperties - */ - public RMRestProperties getRmRestProperties() - { - return this.rmRestProperties; - } } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java index 41370c7a55..710413fa33 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java @@ -26,16 +26,24 @@ */ package org.alfresco.rest.core; +import static lombok.AccessLevel.PROTECTED; + import javax.annotation.Resource; import org.alfresco.rest.requests.Node; import org.alfresco.rest.requests.coreAPI.RestCoreAPI; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilesAPI; -import org.alfresco.rest.rm.community.requests.igCoreAPI.RMSiteAPI; -import org.alfresco.rest.rm.community.requests.igCoreAPI.RMUserAPI; -import org.alfresco.rest.rm.community.requests.igCoreAPI.RecordsAPI; -import org.alfresco.rest.rm.community.requests.igCoreAPI.RestIGCoreAPI; +import org.alfresco.rest.rm.community.requests.gscore.GSCoreAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.FilesAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RMUserAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.TransferAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.TransferContainerAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledRecordFolderAPI; import org.alfresco.utility.data.DataUser; import org.alfresco.utility.model.RepoTestModel; import org.alfresco.utility.model.UserModel; @@ -43,8 +51,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; +import lombok.Getter; +import lombok.Setter; + /** - * REST API Factory Implementation + * REST API Factory which provides access to the APIs * * @author Tuna Aksoy * @since 2.6 @@ -54,93 +65,143 @@ import org.springframework.stereotype.Service; public class RestAPIFactory { @Autowired + @Getter (value = PROTECTED) private DataUser dataUser; @Resource(name = "RMRestWrapper") + @Getter + @Setter private RMRestWrapper rmRestWrapper; - /** - * @return the rmRestWrapper - */ - public RMRestWrapper getRmRestWrapper() + private GSCoreAPI getGSCoreAPI(UserModel userModel) { - return this.rmRestWrapper; + getRmRestWrapper().authenticateUser(userModel != null ? userModel : getDataUser().getAdminUser()); + return getRmRestWrapper().withGSCoreAPI(); } - public void setRmRestWrapper(RMRestWrapper rmRestWrapper) + private RestCoreAPI getCoreAPI(UserModel userModel) { - this.rmRestWrapper = rmRestWrapper; - } - - private RestIGCoreAPI getRestIGCoreAPI(UserModel userModel) - { - getRmRestWrapper().authenticateUser(userModel != null ? userModel : dataUser.getAdminUser()); - return getRmRestWrapper().withIGCoreAPI(); - } - - private RestCoreAPI getRestCoreAPI(UserModel userModel) - { - getRmRestWrapper().authenticateUser(userModel != null ? userModel : dataUser.getAdminUser()); + getRmRestWrapper().authenticateUser(userModel != null ? userModel : getDataUser().getAdminUser()); return getRmRestWrapper().withCoreAPI(); } public Node getNodeAPI(RepoTestModel model) throws Exception { - return getRestCoreAPI(null).usingNode(model); + return getCoreAPI(null).usingNode(model); } public Node getNodeAPI(UserModel userModel, RepoTestModel model) throws Exception { - return getRestCoreAPI(userModel).usingNode(model); + return getCoreAPI(userModel).usingNode(model); } public RMSiteAPI getRMSiteAPI() { - return getRestIGCoreAPI(null).usingRMSite(); + return getGSCoreAPI(null).usingRMSite(); } public RMSiteAPI getRMSiteAPI(UserModel userModel) { - return getRestIGCoreAPI(userModel).usingRMSite(); + return getGSCoreAPI(userModel).usingRMSite(); } - public FilePlanComponentAPI getFilePlanComponentsAPI() + public FilePlanAPI getFilePlansAPI() { - return getRestIGCoreAPI(null).usingFilePlanComponents(); + return getGSCoreAPI(null).usingFilePlans(); } - public FilePlanComponentAPI getFilePlanComponentsAPI(UserModel userModel) + public FilePlanAPI getFilePlansAPI(UserModel userModel) { - return getRestIGCoreAPI(userModel).usingFilePlanComponents(); + return getGSCoreAPI(userModel).usingFilePlans(); + } + + public RecordCategoryAPI getRecordCategoryAPI() + { + return getGSCoreAPI(null).usingRecordCategory(); + } + + public RecordCategoryAPI getRecordCategoryAPI(UserModel userModel) + { + return getGSCoreAPI(userModel).usingRecordCategory(); + } + + public RecordFolderAPI getRecordFolderAPI() + { + return getGSCoreAPI(null).usingRecordFolder(); + } + + public RecordFolderAPI getRecordFolderAPI(UserModel userModel) + { + return getGSCoreAPI(userModel).usingRecordFolder(); } public RecordsAPI getRecordsAPI() { - return getRestIGCoreAPI(null).usingRecords(); + return getGSCoreAPI(null).usingRecords(); } public RecordsAPI getRecordsAPI(UserModel userModel) { - return getRestIGCoreAPI(userModel).usingRecords(); + return getGSCoreAPI(userModel).usingRecords(); } public FilesAPI getFilesAPI() { - return getRestIGCoreAPI(null).usingFiles(); + return getGSCoreAPI(null).usingFiles(); } public FilesAPI getFilesAPI(UserModel userModel) { - return getRestIGCoreAPI(userModel).usingFiles(); + return getGSCoreAPI(userModel).usingFiles(); + } + + public TransferContainerAPI getTransferContainerAPI() + { + return getGSCoreAPI(null).usingTransferContainer(); + } + + public TransferContainerAPI getTransferContainerAPI(UserModel userModel) + { + return getGSCoreAPI(userModel).usingTransferContainer(); + } + + public TransferAPI getTransferAPI() + { + return getGSCoreAPI(null).usingTransfer(); + } + + public TransferAPI getTransferAPI(UserModel userModel) + { + return getGSCoreAPI(userModel).usingTransfer(); } public RMUserAPI getRMUserAPI() { - return getRestIGCoreAPI(null).usingRMUser(); + return getGSCoreAPI(null).usingRMUser(); } public RMUserAPI getRMUserAPI(UserModel userModel) { - return getRestIGCoreAPI(userModel).usingRMUser(); + return getGSCoreAPI(userModel).usingRMUser(); + } + + public UnfiledContainerAPI getUnfiledContainersAPI() + { + return getGSCoreAPI(null).usingUnfiledContainers(); + } + + public UnfiledContainerAPI getUnfiledContainersAPI(UserModel userModel) + { + return getGSCoreAPI(userModel).usingUnfiledContainers(); + } + + public UnfiledRecordFolderAPI getUnfiledRecordFoldersAPI() + { + return getGSCoreAPI(null).usingUnfiledRecordFolder(); + } + + public UnfiledRecordFolderAPI getUnfiledRecordFoldersAPI(UserModel userModel) + { + return getGSCoreAPI(userModel).usingUnfiledRecordFolder(); } } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentIdNamePair.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/IdNamePair.java similarity index 83% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentIdNamePair.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/IdNamePair.java index ebe0edc335..925e7c3e87 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentIdNamePair.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/IdNamePair.java @@ -24,7 +24,9 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.model.fileplancomponents; +package org.alfresco.rest.rm.community.model.common; + +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -41,8 +43,11 @@ import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor -public class FilePlanComponentIdNamePair +public class IdNamePair { - public String id; - public String name; + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private String name; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentUserInfo.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/Owner.java similarity index 76% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentUserInfo.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/Owner.java index 411d682470..db8973d980 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentUserInfo.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/Owner.java @@ -24,25 +24,26 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.model.fileplancomponents; +package org.alfresco.rest.rm.community.model.common; + +import org.alfresco.utility.model.TestModel; -import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.EqualsAndHashCode; /** - * POJO for file plan component created by object + * POJO for owner parameter * - * @author Kristijan Conkas + * @author Tuna Aksoy * @since 2.6 */ @Builder @Data -@NoArgsConstructor -@AllArgsConstructor -public class FilePlanComponentUserInfo +@EqualsAndHashCode(callSuper = true) +//@NoArgsConstructor +//@AllArgsConstructor +public class Owner extends TestModel { - private String id; - private String displayName; + } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentPath.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/Path.java similarity index 75% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentPath.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/Path.java index e2a356d163..304e29b92c 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentPath.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/Path.java @@ -24,31 +24,39 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.model.fileplancomponents; +package org.alfresco.rest.rm.community.model.common; import java.util.List; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.utility.model.TestModel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** - * POJO for FilePlanComponent path parameter + * POJO for path parameter * * @author Kristijan Conkas * @since 2.6 */ @Builder @Data +@EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor -@JsonIgnoreProperties(ignoreUnknown = true) -public class FilePlanComponentPath +public class Path extends TestModel { + @JsonProperty (required = true) private String name; + + @JsonProperty (required = true) private Boolean isComplete; - private List elements; + + @JsonProperty (required = true) + private List elements; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentReviewPeriod.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/ReviewPeriod.java similarity index 84% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentReviewPeriod.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/ReviewPeriod.java index 1b466cf071..775fa99bd2 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentReviewPeriod.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/common/ReviewPeriod.java @@ -24,7 +24,9 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.model.fileplancomponents; +package org.alfresco.rest.rm.community.model.common; + +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -32,7 +34,7 @@ import lombok.Data; import lombok.NoArgsConstructor; /** - * POJO for the file plan component review period + * POJO for the review period parameter * * @author Rodica Sutu * @since 2.6 @@ -41,8 +43,11 @@ import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor -public class FilePlanComponentReviewPeriod +public class ReviewPeriod { + @JsonProperty (required = true) private String periodType; + + @JsonProperty (required = true) private String expression; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplan/FilePlan.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplan/FilePlan.java new file mode 100644 index 0000000000..a3accf2d0f --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplan/FilePlan.java @@ -0,0 +1,99 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.fileplan; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.utility.model.TestModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for file plan + * + * @author Ramona Popa + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class FilePlan extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + + @JsonProperty (required = true) + private RestByUserModel createdByUser; + + @JsonProperty (required = true) + private String modifiedAt; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + @JsonProperty (required = true) + private List aspectNames; + + @JsonProperty (required = true) + private String createdAt; + + @JsonProperty (required = true) + private RestByUserModel modifiedByUser; + + @JsonProperty (required = true) + private String name; + + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private FilePlanProperties properties; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private List allowableOperations; + + @JsonProperty + private Path path; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplan/FilePlanProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplan/FilePlanProperties.java new file mode 100644 index 0000000000..174923eabc --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplan/FilePlanProperties.java @@ -0,0 +1,78 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.fileplan; + +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_COMPONENT_ID; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_COUNT; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IDENTIFIER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ID_IS_TEMPORARILY_EDITABLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ROOT_NODE_REF; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.utility.model.TestModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for file plan properties + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class FilePlanProperties extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true, value = PROPERTIES_ID_IS_TEMPORARILY_EDITABLE) + private Boolean idIsTemporarilyEditable; + + @JsonProperty (required = true, value = PROPERTIES_IDENTIFIER) + private String identifier; + + @JsonProperty (required = true, value = PROPERTIES_COMPONENT_ID) + private String componentd; + + @JsonProperty (required = true, value = PROPERTIES_ROOT_NODE_REF) + private String rootNodeRef; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty (PROPERTIES_COUNT) + private Integer count; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentAlias.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentAlias.java index e638648943..708ec447dc 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentAlias.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentAlias.java @@ -37,5 +37,4 @@ public class FilePlanComponentAlias public static final String FILE_PLAN_ALIAS = "-filePlan-"; public static final String TRANSFERS_ALIAS = "-transfers-"; public static final String UNFILED_RECORDS_CONTAINER_ALIAS = "-unfiled-"; - public static final String HOLDS_ALIAS = "-holds-"; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentAspects.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentAspects.java index 7d564ff187..a1b93e5c67 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentAspects.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentAspects.java @@ -34,6 +34,6 @@ package org.alfresco.rest.rm.community.model.fileplancomponents; */ public class FilePlanComponentAspects { - // aspect present on closed records - public static final String ASPECTS_CLOSED_RECORD = "rma:declaredRecord"; + // aspect present on completed records + public static final String ASPECTS_COMPLETED_RECORD = "rma:declaredRecord"; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentFields.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentFields.java index 442e3e15df..ac79160c48 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentFields.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentFields.java @@ -34,33 +34,85 @@ package org.alfresco.rest.rm.community.model.fileplancomponents; */ public class FilePlanComponentFields { - public static final String NAME = "name"; - public static final String NODE_TYPE = "nodeType"; - public static final String NODE_PARENT_ID = "parentId"; - public static final String ENTRY = "entry"; - public static final String PROPERTIES = "properties"; + /** Common properties for file plans, record categories, record folders and records */ + public static final String PROPERTIES_ROOT_NODE_REF = "rma:rootNodeRef"; + public static final String PROPERTIES_IDENTIFIER = "rma:identifier"; + public static final String PROPERTIES_ID_IS_TEMPORARILY_EDITABLE = "rma:idIsTemporarilyEditable"; + + /** Common properties for record categories, record folders and records */ + // Non-electronic record properties public static final String PROPERTIES_TITLE = "cm:title"; - public static final String PROPERTIES_RECORD_ID = "rma:identifier"; - public static final String PROPERTIES_VITAL_RECORD_INDICATOR = "rma:vitalRecordIndicator"; - public static final String PROPERTIES_HOLD_REASON = "rma:holdReason"; public static final String PROPERTIES_DESCRIPTION = "cm:description"; - public static final String PROPERTIES_SUPPLEMENTAL_MARKING_LIST = "rmc:supplementalMarkingList"; - public static final String ALLOWABLE_OPERATIONS = "allowableOperations"; - public static final String IS_CLOSED = "isClosed"; + + /** Common properties for record categories and record folders **/ + public static final String PROPERTIES_VITAL_RECORD_INDICATOR = "rma:vitalRecordIndicator"; public static final String PROPERTIES_REVIEW_PERIOD = "rma:reviewPeriod"; - public static final String PROPERTIES_LOCATION = "rma:location"; + public static final String PROPERTIES_OWNER = "cm:owner"; + + /** Common properties for record folders and records */ + public static final String PROPERTIES_RECORD_SEARCH_HAS_DISPOSITION_SCHEDULE = "rma:recordSearchHasDispositionSchedule"; + + /** File plan properties */ + public static final String PROPERTIES_COMPONENT_ID = "st:componentId"; + public static final String PROPERTIES_COUNT = "rma:count"; + + /** Record category properties */ + // All fields are shared with record folders + + /** Record folder properties */ public static final String PROPERTIES_IS_CLOSED = "rma:isClosed"; // not to be confused with IS_CLOSED! - public static final String IS_COMPLETED = "isCompleted"; - - // for non-electronic records - public static final String PROPERTIES_BOX = "rma:box"; - public static final String PROPERTIES_FILE = "rma:file"; - public static final String PROPERTIES_NUMBER_OF_COPIES = "rma:numberOfCopies"; - public static final String PROPERTIES_PHYSICAL_SIZE = "rma:physicalSize"; + public static final String PROPERTIES_HELD_CHILDREN_COUNT = "rma:heldChildrenCount"; + public static final String PROPERTIES_LOCATION = "rma:location"; + public static final String PROPERTIES_RECORD_SEARCH_VITAL_RECORD_REVIEW_PERIOD = "rma:recordSearchVitalRecordReviewPeriod"; + public static final String PROPERTIES_RECORD_SEARCH_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION = "rma:recordSearchVitalRecordReviewPeriodExpression"; + + /** Record properties */ + public static final String PROPERTIES_DATE_FILED = "rma:dateFiled"; + public static final String PROPERTIES_ORIGINAL_NAME = "rma:origionalName"; + + /** Electronic record properties */ + public static final String PROPERTIES_VERSION_TYPE = "cm:versionType"; + public static final String PROPERTIES_VERSION_LABEL = "cm:versionLabel"; + public static final String PROPERTIES_DATE_TIME_ORIGINAL = "exif:dateTimeOriginal"; + public static final String PROPERTIES_EXPOSURE_TIME = "exif:exposureTime"; + public static final String PROPERTIES_FLASH = "exif:flash"; + public static final String PROPERTIES_F_NUMBER = "exif:fNumber"; + public static final String PROPERTIES_FOCAL_LENGTH = "exif:focalLength"; + public static final String PROPERTIES_ISO_SPEED_RATINGS = "exif:isoSpeedRatings"; + public static final String PROPERTIES_MANUFACTURER = "exif:manufacturer"; + public static final String PROPERTIES_MODEL = "exif:model"; + public static final String PROPERTIES_ORIENTATION = "exif:orientation"; + public static final String PROPERTIES_PIXEL_X_DIMENSION = "exif:pixelXDimension"; + public static final String PROPERTIES_PIXEL_Y_DIMENSION = "exif:pixelYDimension"; + public static final String PROPERTIES_RESOLUTION_UNIT = "exif:resolutionUnit"; + public static final String PROPERTIES_SOFTWARE = "exif:software"; + public static final String PROPERTIES_X_RESOLUTION = "exif:xResolution"; + public static final String PROPERTIES_Y_RESOLUTION = "exif:yResolution"; + public static final String PROPERTIES_RECORD_ORIGINATING_LOCATION = "rma:recordOriginatingLocation"; + public static final String PROPERTIES_RECORD_ORIGINATING_USER_ID = "rma:recordOriginatingUserId"; + public static final String PROPERTIES_RECORD_ORIGINATING_CREATION_DATE = "rma:recordOriginatingCreationDate"; + + /** Non-electronic record properties */ public static final String PROPERTIES_SHELF = "rma:shelf"; public static final String PROPERTIES_STORAGE_LOCATION = "rma:storageLocation"; + public static final String PROPERTIES_FILE = "rma:file"; + public static final String PROPERTIES_BOX = "rma:box"; + public static final String PROPERTIES_NUMBER_OF_COPIES = "rma:numberOfCopies"; + public static final String PROPERTIES_PHYSICAL_SIZE = "rma:physicalSize"; - //RelativePath specifies the container structure to create relative to the nodeId. + /** Transfer properties */ + public static final String PROPERTIES_PDF_INDICATOR = "rma:transferPDFIndicator"; + public static final String PROPERTIES_TRANSFER_LOCATION = "rma:transferLocation"; + public static final String PROPERTIES_ACCESSION_INDICATOR = "rma:transferAccessionIndicator"; + + /** Parameters */ public static final String RELATIVE_PATH = "relativePath"; + public static final String INCLUDE = "include"; + + /** Include options */ + public static final String ALLOWABLE_OPERATIONS = "allowableOperations"; + public static final String IS_CLOSED = "isClosed"; + public static final String IS_COMPLETED = "isCompleted"; + public static final String CONTENT = "content"; public static final String PATH = "path"; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentType.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentType.java index 82e89de568..619411e8bd 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentType.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentType.java @@ -38,9 +38,7 @@ public class FilePlanComponentType public static final String RECORD_CATEGORY_TYPE = "rma:recordCategory"; public static final String RECORD_FOLDER_TYPE = "rma:recordFolder"; public static final String RECORD_TYPE = "rma:record"; // generic record type - public static final String HOLD_TYPE = "rma:hold"; public static final String UNFILED_RECORD_FOLDER_TYPE = "rma:unfiledRecordFolder"; - public static final String HOLD_CONTAINER_TYPE = "rma:holdContainer"; public static final String TRANSFER_TYPE = "rma:transfer"; public static final String TRANSFER_CONTAINER_TYPE = "rma:transferContainer"; public static final String UNFILED_CONTAINER_TYPE = "rma:unfiledRecordContainer"; diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/Record.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/Record.java new file mode 100644 index 0000000000..6ebd2bf33a --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/Record.java @@ -0,0 +1,103 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.record; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.utility.model.TestModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for record + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class Record extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true) + private String createdAt; + + @JsonProperty (required = true) + private RestByUserModel createdByUser; + + @JsonProperty (required = true) + private String modifiedAt; + + @JsonProperty (required = true) + private RestByUserModel modifiedByUser; + + @JsonProperty (required = true) + private String name; + + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private RecordContent content; + + @JsonProperty + private Boolean isCompleted; + + @JsonProperty + private RecordProperties properties; + + @JsonProperty + private List aspectNames; + + @JsonProperty + private List allowableOperations; + + @JsonProperty + private Path path; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/RecordBodyFile.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordBodyFile.java similarity index 90% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/RecordBodyFile.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordBodyFile.java index 4576c386f1..dd0bc282d7 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/RecordBodyFile.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordBodyFile.java @@ -24,16 +24,17 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.model.fileplancomponents; +package org.alfresco.rest.rm.community.model.record; import com.fasterxml.jackson.annotation.JsonProperty; +import org.alfresco.utility.model.TestModel; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import org.alfresco.utility.model.TestModel; /** * POJO for File records @@ -43,14 +44,11 @@ import org.alfresco.utility.model.TestModel; */ @Builder @Data -@EqualsAndHashCode (callSuper = true) +@EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor public class RecordBodyFile extends TestModel { @JsonProperty private String targetParentId; - - @JsonProperty - private String relativePath; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentContent.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordContent.java similarity index 85% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentContent.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordContent.java index 23aa496bb1..eb21a90851 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentContent.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordContent.java @@ -24,30 +24,32 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.model.fileplancomponents; +package org.alfresco.rest.rm.community.model.record; import com.fasterxml.jackson.annotation.JsonProperty; +import org.alfresco.utility.model.TestModel; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** - * POJO for FilePlanComponent content field + * POJO for record content field * * @author Kristijan Conkas + * @author Tuna Aksoy * @since 2.6 */ @Builder @Data +@EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor -public class FilePlanComponentContent +public class RecordContent extends TestModel { - @JsonProperty (required = true) - private String encoding; - @JsonProperty (required = true) private String mimeType; @@ -56,4 +58,7 @@ public class FilePlanComponentContent @JsonProperty (required = true) private Integer sizeInBytes; + + @JsonProperty (required = true) + private String encoding; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordProperties.java new file mode 100644 index 0000000000..e2d0fabdb8 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordProperties.java @@ -0,0 +1,203 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.record; + +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_BOX; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DATE_FILED; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DATE_TIME_ORIGINAL; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DESCRIPTION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_EXPOSURE_TIME; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FILE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FLASH; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FOCAL_LENGTH; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_F_NUMBER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IDENTIFIER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ID_IS_TEMPORARILY_EDITABLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ISO_SPEED_RATINGS; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_MANUFACTURER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_MODEL; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_NUMBER_OF_COPIES; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ORIENTATION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ORIGINAL_NAME; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_OWNER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PHYSICAL_SIZE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PIXEL_X_DIMENSION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PIXEL_Y_DIMENSION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_HAS_DISPOSITION_SCHEDULE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RESOLUTION_UNIT; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ROOT_NODE_REF; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_SHELF; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_SOFTWARE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_STORAGE_LOCATION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TITLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VERSION_LABEL; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VERSION_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_X_RESOLUTION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_Y_RESOLUTION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_ORIGINATING_LOCATION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_ORIGINATING_USER_ID; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_ORIGINATING_CREATION_DATE; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.rm.community.model.common.Owner; +import org.alfresco.utility.model.TestModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for record properties + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class RecordProperties extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true, value = PROPERTIES_ROOT_NODE_REF) + private String rootNodeRef; + + @JsonProperty (required = true, value = PROPERTIES_DATE_FILED) + private String dateField; + + @JsonProperty (required = true, value = PROPERTIES_IDENTIFIER) + private String identifier; + + @JsonProperty (required = true, value = PROPERTIES_RECORD_SEARCH_HAS_DISPOSITION_SCHEDULE) + private Boolean recordSearchHasDispositionSchedule; + + @JsonProperty (required = true, value = PROPERTIES_ORIGINAL_NAME) + private String originalName; + + @JsonProperty (required = true, value = PROPERTIES_ID_IS_TEMPORARILY_EDITABLE) + private Boolean idIsTemporarilyEditable; + + /*********************************/ + /** Electronic record parameters */ + /*********************************/ + @JsonProperty (PROPERTIES_VERSION_TYPE) + private String versionType; + + @JsonProperty (PROPERTIES_VERSION_LABEL) + private String versionLabel; + + @JsonProperty (PROPERTIES_DATE_TIME_ORIGINAL) + private String dateTimeOriginal; + + @JsonProperty (PROPERTIES_EXPOSURE_TIME) + private Double exposureTime; + + @JsonProperty (PROPERTIES_FLASH) + private Boolean flash; + + @JsonProperty (PROPERTIES_F_NUMBER) + private Double fNumber; + + @JsonProperty (PROPERTIES_FOCAL_LENGTH) + private Double focalLength; + + @JsonProperty (PROPERTIES_ISO_SPEED_RATINGS) + private Integer isoSpeedRatings; + + @JsonProperty (PROPERTIES_MANUFACTURER) + private String manufacturer; + + @JsonProperty (PROPERTIES_MODEL) + private String model; + + @JsonProperty (PROPERTIES_ORIENTATION) + private Integer orientation; + + @JsonProperty (PROPERTIES_PIXEL_X_DIMENSION) + private Integer pixelXDimension; + + @JsonProperty (PROPERTIES_PIXEL_Y_DIMENSION) + private Integer pixelYDimension; + + @JsonProperty (PROPERTIES_RESOLUTION_UNIT) + private String resolutionUnit; + + @JsonProperty (PROPERTIES_SOFTWARE) + private String software; + + @JsonProperty (PROPERTIES_X_RESOLUTION) + private Double xResolution; + + @JsonProperty (PROPERTIES_Y_RESOLUTION) + private Double yResolution; + + @JsonProperty (PROPERTIES_RECORD_ORIGINATING_LOCATION) + private String originatingLocation; + + @JsonProperty (PROPERTIES_RECORD_ORIGINATING_USER_ID) + private String originatingUserId; + + @JsonProperty (PROPERTIES_RECORD_ORIGINATING_CREATION_DATE) + private String originatingCreationDate; + + /*************************************/ + /** Non-electronic record parameters */ + /*************************************/ + @JsonProperty (PROPERTIES_TITLE) + private String title; + + @JsonProperty (PROPERTIES_SHELF) + private String shelf; + + @JsonProperty (PROPERTIES_STORAGE_LOCATION) + private String storageLocation; + + @JsonProperty (PROPERTIES_FILE) + private String file; + + @JsonProperty (PROPERTIES_BOX) + private String box; + + @JsonProperty (PROPERTIES_DESCRIPTION) + private String description; + + @JsonProperty (PROPERTIES_NUMBER_OF_COPIES) + private Integer numberOfCopies; + + @JsonProperty (PROPERTIES_PHYSICAL_SIZE) + private Integer physicalSize; + + @JsonProperty (PROPERTIES_OWNER) + private Owner owner; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategory.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategory.java new file mode 100644 index 0000000000..ab668e2f0f --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategory.java @@ -0,0 +1,101 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.recordcategory; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.utility.model.TestModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for record category + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class RecordCategory extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + + @JsonProperty (required = true) + private RestByUserModel createdByUser; + + @JsonProperty (required = true) + private String modifiedAt; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + @JsonProperty (required = true) + private List aspectNames; + + @JsonProperty (required = true) + private String createdAt; + + @JsonProperty (required = true) + private RestByUserModel modifiedByUser; + + @JsonProperty (required = true) + private String name; + + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private RecordCategoryProperties properties; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private Boolean hasRetentionSchedule; + + @JsonProperty + private List allowableOperations; + + @JsonProperty + private Path path; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChild.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChild.java new file mode 100644 index 0000000000..08fab3b764 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChild.java @@ -0,0 +1,112 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.recordcategory; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.utility.model.TestModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for record category child + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class RecordCategoryChild extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true) + private String createdAt; + + @JsonProperty (required = true) + private RestByUserModel createdByUser; + + @JsonProperty (required = true) + private String modifiedAt; + + @JsonProperty (required = true) + private RestByUserModel modifiedByUser; + + @JsonProperty (required = true) + private String name; + + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private Boolean isRecordCategory; + + @JsonProperty + private Boolean isRecordFolder; + + @JsonProperty + private RecordCategoryChildProperties properties; + + @JsonProperty + private List aspectNames; + + @JsonProperty + private Boolean hasRetentionSchedule; + + @JsonProperty + private Boolean isClosed; + + @JsonProperty + private List allowableOperations; + + @JsonProperty + private Path path; + + @JsonProperty + private String relativePath; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildCollection.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildCollection.java new file mode 100644 index 0000000000..21e600b73b --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildCollection.java @@ -0,0 +1,41 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.recordcategory; + +import org.alfresco.rest.core.RestModels; + +/** + * Handle collection of {@link RecordCategoryChildEntry} + * + * @author Kristijan Conkas + * @author Tuna Aksoy + * @since 2.6 + */ +public class RecordCategoryChildCollection extends RestModels +{ + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildEntry.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildEntry.java new file mode 100644 index 0000000000..23fc80197f --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildEntry.java @@ -0,0 +1,48 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.recordcategory; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.core.RestModels; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * POJO for record category child entry + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class RecordCategoryChildEntry extends RestModels +{ + @JsonProperty + private RecordCategoryChild entry; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java similarity index 55% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentProperties.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java index 950c4f18ee..950890b060 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentProperties.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java @@ -24,90 +24,83 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.model.fileplancomponents; +package org.alfresco.rest.rm.community.model.recordcategory; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_BOX; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DESCRIPTION; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FILE; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_HOLD_REASON; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_HELD_CHILDREN_COUNT; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IDENTIFIER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ID_IS_TEMPORARILY_EDITABLE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IS_CLOSED; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_LOCATION; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_NUMBER_OF_COPIES; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PHYSICAL_SIZE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_HAS_DISPOSITION_SCHEDULE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_REVIEW_PERIOD; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_SHELF; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_SUPPLEMENTAL_MARKING_LIST; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ROOT_NODE_REF; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TITLE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VITAL_RECORD_INDICATOR; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_ID; -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.alfresco.rest.rm.community.model.common.ReviewPeriod; import org.alfresco.rest.rm.community.util.ReviewPeriodSerializer; +import org.alfresco.utility.model.TestModel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** - * POJO for file plan component properties + * POJO for record category child properties * - * @author Kristijan Conkas + * @author Tuna Aksoy * @since 2.6 */ -//FIXME: Once the fields have been added the JsonIgnoreProperties annotation should be removed -@JsonIgnoreProperties (ignoreUnknown = true) @Builder @Data +@EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor -public class FilePlanComponentProperties +public class RecordCategoryChildProperties extends TestModel { - @JsonProperty(PROPERTIES_VITAL_RECORD_INDICATOR) - private Boolean vitalRecord; - - @JsonProperty(PROPERTIES_TITLE) + /**************************************************************************/ + /** Mandatory parameters - Shared by record categories and record folders */ + /**************************************************************************/ + @JsonProperty (required = true, value = PROPERTIES_TITLE) private String title; - @JsonProperty(PROPERTIES_HOLD_REASON) - private String holdReason; + @JsonProperty (required = true, value = PROPERTIES_VITAL_RECORD_INDICATOR) + private Boolean vitalRecordIndicator; - @JsonProperty(PROPERTIES_DESCRIPTION) + @JsonProperty (required = true, value = PROPERTIES_ROOT_NODE_REF) + private String rootNodeRef; + + @JsonProperty (required = true, value = PROPERTIES_ID_IS_TEMPORARILY_EDITABLE) + private Boolean idIsTemporarilyEditable; + + @JsonProperty (required = true, value = PROPERTIES_IDENTIFIER) + private String identifier; + + @JsonProperty (required = true, value = PROPERTIES_REVIEW_PERIOD) + @JsonSerialize (using = ReviewPeriodSerializer.class) + private ReviewPeriod reviewPeriod; + + @JsonProperty (required = true, value = PROPERTIES_DESCRIPTION) private String description; - @JsonProperty(PROPERTIES_SUPPLEMENTAL_MARKING_LIST) - private List supplementalMarkingList; + /*********************************************************/ + /** Optional parameters - Applies only to record folders */ + /*********************************************************/ + @JsonProperty (PROPERTIES_HELD_CHILDREN_COUNT) + private Integer heldChildrenCount; - @JsonProperty(PROPERTIES_REVIEW_PERIOD) - @JsonSerialize (using = ReviewPeriodSerializer.class) - private FilePlanComponentReviewPeriod reviewPeriod; - - @JsonProperty(PROPERTIES_LOCATION) + @JsonProperty (PROPERTIES_LOCATION) private String location; - @JsonProperty(value = PROPERTIES_IS_CLOSED, required = false) + @JsonProperty (PROPERTIES_IS_CLOSED) private Boolean isClosed; - @JsonProperty(value = PROPERTIES_BOX, required = false) - private String box; - - @JsonProperty(value = PROPERTIES_FILE, required = false) - private String file; - - @JsonProperty(value = PROPERTIES_SHELF, required = false) - private String shelf; - - @JsonProperty(value = PROPERTIES_NUMBER_OF_COPIES, required = false) - private Integer numberOfCopies; - - @JsonProperty(value = PROPERTIES_PHYSICAL_SIZE, required = false) - private Integer physicalSize; - - @JsonProperty(value = PROPERTIES_RECORD_ID, required = false) - private String rmIdentifier; -} + @JsonProperty (PROPERTIES_RECORD_SEARCH_HAS_DISPOSITION_SCHEDULE) + private Boolean recordSearchHasDispositionSchedule; +} \ No newline at end of file diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryCollection.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryCollection.java new file mode 100644 index 0000000000..839856c2ac --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryCollection.java @@ -0,0 +1,41 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.recordcategory; + +import org.alfresco.rest.core.RestModels; + +/** + * Handle collection of {@link RecordCategoryEntry} + * + * @author Ramona Popa + * @author Tuna Aksoy + * @since 2.6 + */ +public class RecordCategoryCollection extends RestModels +{ + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryEntry.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryEntry.java new file mode 100644 index 0000000000..fae0772f8b --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryEntry.java @@ -0,0 +1,50 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.recordcategory; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.core.RestModels; +import org.alfresco.rest.rm.community.model.fileplan.FilePlan; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * POJO for file plan entry + * + * @author Ramona Popa + * @author Tuna Aksoy + * @since 2.6 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class RecordCategoryEntry extends RestModels +{ + @JsonProperty + private RecordCategory entry; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryProperties.java new file mode 100644 index 0000000000..fb5b86110c --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryProperties.java @@ -0,0 +1,95 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.recordcategory; + +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DESCRIPTION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IDENTIFIER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ID_IS_TEMPORARILY_EDITABLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_OWNER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_REVIEW_PERIOD; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ROOT_NODE_REF; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TITLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VITAL_RECORD_INDICATOR; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.alfresco.rest.rm.community.model.common.Owner; +import org.alfresco.rest.rm.community.model.common.ReviewPeriod; +import org.alfresco.rest.rm.community.util.ReviewPeriodSerializer; +import org.alfresco.utility.model.TestModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for record category properties + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class RecordCategoryProperties extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true, value = PROPERTIES_ID_IS_TEMPORARILY_EDITABLE) + private Boolean idIsTemporarilyEditable; + + @JsonProperty (required = true, value = PROPERTIES_IDENTIFIER) + private String identifier; + + @JsonProperty (required = true, value = PROPERTIES_REVIEW_PERIOD) + @JsonSerialize (using = ReviewPeriodSerializer.class) + private ReviewPeriod reviewPeriod; + + @JsonProperty (required = true, value = PROPERTIES_VITAL_RECORD_INDICATOR) + private Boolean vitalRecordIndicator; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty (PROPERTIES_TITLE) + private String title; + + @JsonProperty (PROPERTIES_ROOT_NODE_REF) + private String rootNodeRef; + + @JsonProperty (PROPERTIES_DESCRIPTION) + private String description; + + @JsonProperty (PROPERTIES_OWNER) + private Owner owner; +} \ No newline at end of file diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolder.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolder.java new file mode 100644 index 0000000000..110bba4ed8 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolder.java @@ -0,0 +1,100 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.recordfolder; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.utility.model.TestModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for record folder + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class RecordFolder extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true) + private RestByUserModel createdByUser; + + @JsonProperty (required = true) + private String modifiedAt; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + @JsonProperty (required = true) + private List aspectNames; + + @JsonProperty (required = true) + private String createdAt; + + @JsonProperty (required = true) + private RestByUserModel modifiedByUser; + + @JsonProperty (required = true) + private String name; + + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private RecordFolderProperties properties; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private Boolean isClosed; + + @JsonProperty + private List allowableOperations; + + @JsonProperty + private Path path; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentsCollection.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderCollection.java similarity index 83% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentsCollection.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderCollection.java index 45cb840416..c52948305a 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentsCollection.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderCollection.java @@ -24,17 +24,18 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.model.fileplancomponents; +package org.alfresco.rest.rm.community.model.recordfolder; import org.alfresco.rest.core.RestModels; /** - * Handle collection of FilePlanComponents + * Handle collection of {@link RecordFolderEntry} * * @author Kristijan Conkas + * @author Tuna Aksoy * @since 2.6 */ -public class FilePlanComponentsCollection extends RestModels +public class RecordFolderCollection extends RestModels { } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentEntry.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderEntry.java similarity index 79% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentEntry.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderEntry.java index 1d72826604..b12e62c464 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentEntry.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderEntry.java @@ -24,13 +24,12 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.model.fileplancomponents; - -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.ENTRY; +package org.alfresco.rest.rm.community.model.recordfolder; import com.fasterxml.jackson.annotation.JsonProperty; import org.alfresco.rest.core.RestModels; +import org.alfresco.rest.rm.community.model.record.Record; import lombok.AllArgsConstructor; import lombok.Builder; @@ -39,7 +38,7 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** - * POJO for file plan component entry + * POJO for record folder entry * * @author Tuna Aksoy * @since 2.6 @@ -49,8 +48,8 @@ import lombok.NoArgsConstructor; @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor -public class FilePlanComponentEntry extends RestModels +public class RecordFolderEntry extends RestModels { - @JsonProperty(ENTRY) - FilePlanComponent filePlanComponentModel; + @JsonProperty + private Record entry; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderProperties.java new file mode 100644 index 0000000000..0c14e4c768 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderProperties.java @@ -0,0 +1,119 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.recordfolder; + +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DESCRIPTION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_HELD_CHILDREN_COUNT; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IDENTIFIER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ID_IS_TEMPORARILY_EDITABLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IS_CLOSED; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_LOCATION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_OWNER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_HAS_DISPOSITION_SCHEDULE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_VITAL_RECORD_REVIEW_PERIOD; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_REVIEW_PERIOD; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ROOT_NODE_REF; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TITLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VITAL_RECORD_INDICATOR; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.alfresco.rest.rm.community.model.common.Owner; +import org.alfresco.rest.rm.community.model.common.ReviewPeriod; +import org.alfresco.rest.rm.community.util.ReviewPeriodSerializer; +import org.alfresco.utility.model.TestModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for record folder properties + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class RecordFolderProperties extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true, value = PROPERTIES_ID_IS_TEMPORARILY_EDITABLE) + private Boolean idIsTemporarilyEditable; + + @JsonProperty (required = true, value = PROPERTIES_IS_CLOSED) + private Boolean isClosed; + + @JsonProperty (required = true, value = PROPERTIES_IDENTIFIER) + private String identifier; + + @JsonProperty (required = true, value = PROPERTIES_HELD_CHILDREN_COUNT) + private Integer heldChildrenCount; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty (PROPERTIES_TITLE) + private String title; + + @JsonProperty (PROPERTIES_VITAL_RECORD_INDICATOR) + private Boolean vitalRecordIndicator; + + @JsonProperty (PROPERTIES_ROOT_NODE_REF) + private String rootNodeRef; + + @JsonProperty (PROPERTIES_LOCATION) + private String location; + + @JsonProperty (PROPERTIES_RECORD_SEARCH_HAS_DISPOSITION_SCHEDULE) + private Boolean recordSearchHasDispositionSchedule; + + @JsonProperty (PROPERTIES_REVIEW_PERIOD) + @JsonSerialize (using = ReviewPeriodSerializer.class) + private ReviewPeriod reviewPeriod; + + @JsonProperty (PROPERTIES_DESCRIPTION) + private String description; + + @JsonProperty (PROPERTIES_OWNER) + private Owner owner; + + @JsonProperty (PROPERTIES_RECORD_SEARCH_VITAL_RECORD_REVIEW_PERIOD) + private String recordSearchVitalRecordReviewPeriod; + + @JsonProperty (PROPERTIES_RECORD_SEARCH_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION) + private String recordSearchVitalRecordReviewPeriodExpression; +} \ No newline at end of file diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/site/RMSite.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/site/RMSite.java index 1316811039..7010a338f0 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/site/RMSite.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/site/RMSite.java @@ -26,8 +26,6 @@ */ package org.alfresco.rest.rm.community.model.site; -import static org.alfresco.rest.rm.community.model.site.RMSiteFields.COMPLIANCE; - import com.fasterxml.jackson.annotation.JsonProperty; import org.alfresco.rest.model.RestSiteModel; @@ -51,6 +49,6 @@ import lombok.NoArgsConstructor; @AllArgsConstructor public class RMSite extends RestSiteModel { - @JsonProperty (value = COMPLIANCE, required = true) + @JsonProperty (required = true) private RMSiteCompliance compliance; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/Transfer.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/Transfer.java new file mode 100644 index 0000000000..098f04a6a9 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/Transfer.java @@ -0,0 +1,89 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.transfer; + +import java.util.List; + +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.utility.model.TestModel; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for Transfer + * + * @author Silviu Dinuta + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class Transfer extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true) + private RestByUserModel createdByUser; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + @JsonProperty (required = true) + private List aspectNames; + + @JsonProperty (required = true) + private String createdAt; + + @JsonProperty (required = true) + private String name; + + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private TransferProperties properties; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private List allowableOperations; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChild.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChild.java new file mode 100644 index 0000000000..23669df0b7 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChild.java @@ -0,0 +1,110 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.transfer; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.utility.model.TestModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for transfer child + * + * @author Silviu Dinuta + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class TransferChild extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true) + private String createdAt; + + @JsonProperty (required = true) + private RestByUserModel createdByUser; + + @JsonProperty (required = true) + private String modifiedAt; + + @JsonProperty (required = true) + private RestByUserModel modifiedByUser; + + @JsonProperty (required = true) + private String name; + + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private TransferChildProperties properties; + + @JsonProperty + private Boolean isRecord; + + @JsonProperty + private Boolean isRecordFolder; + + @JsonProperty + private List aspectNames; + + @JsonProperty + private Boolean isCompleted; + + @JsonProperty + private Boolean isClosed; + + @JsonProperty + private List allowableOperations; + + @JsonProperty + private Path path; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildCollection.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildCollection.java new file mode 100644 index 0000000000..ac135a03a1 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildCollection.java @@ -0,0 +1,40 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.transfer; + +import org.alfresco.rest.core.RestModels; + +/** + * Handle collection of {@link TransferChildEntry} + * @author Silviu Dinuta + * @since 2.6 + */ +public class TransferChildCollection extends RestModels +{ + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildEntry.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildEntry.java new file mode 100644 index 0000000000..5f33f00ed6 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildEntry.java @@ -0,0 +1,55 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.transfer; + +import org.alfresco.rest.core.RestModels; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for transfer child entry + * + * @author Silviu Dinuta + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class TransferChildEntry extends RestModels +{ + @JsonProperty + private TransferChild entry; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildProperties.java new file mode 100644 index 0000000000..ba4fd7ce72 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferChildProperties.java @@ -0,0 +1,223 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.transfer; + +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_BOX; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DATE_FILED; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DATE_TIME_ORIGINAL; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DESCRIPTION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_EXPOSURE_TIME; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FILE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FLASH; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FOCAL_LENGTH; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_F_NUMBER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_HELD_CHILDREN_COUNT; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IDENTIFIER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ID_IS_TEMPORARILY_EDITABLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ISO_SPEED_RATINGS; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IS_CLOSED; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_LOCATION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_MANUFACTURER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_MODEL; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_NUMBER_OF_COPIES; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ORIENTATION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ORIGINAL_NAME; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_OWNER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PHYSICAL_SIZE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PIXEL_X_DIMENSION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PIXEL_Y_DIMENSION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_HAS_DISPOSITION_SCHEDULE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RESOLUTION_UNIT; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_REVIEW_PERIOD; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ROOT_NODE_REF; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_SHELF; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_SOFTWARE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_STORAGE_LOCATION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TITLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VERSION_LABEL; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VERSION_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VITAL_RECORD_INDICATOR; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_X_RESOLUTION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_Y_RESOLUTION; + +import org.alfresco.rest.rm.community.model.common.Owner; +import org.alfresco.rest.rm.community.model.common.ReviewPeriod; +import org.alfresco.rest.rm.community.util.ReviewPeriodSerializer; +import org.alfresco.utility.model.TestModel; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for transfer child properties + * + * @author Silviu Dinuta + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class TransferChildProperties extends TestModel +{ + /**************************************************************************/ + /** Mandatory parameters - Shared by record folders and records*/ + /**************************************************************************/ + @JsonProperty (PROPERTIES_TITLE) + private String title; + + @JsonProperty (required = true, value = PROPERTIES_ROOT_NODE_REF) + private String rootNodeRef; + + @JsonProperty (required = true, value = PROPERTIES_ID_IS_TEMPORARILY_EDITABLE) + private Boolean idIsTemporarilyEditable; + + @JsonProperty (required = true, value = PROPERTIES_IDENTIFIER) + private String identifier; + + @JsonProperty (PROPERTIES_DESCRIPTION) + private String description; + + /*********************************************************/ + /** Optional parameters - Applies only to record folders */ + /*********************************************************/ + @JsonProperty (PROPERTIES_VITAL_RECORD_INDICATOR) + private Boolean vitalRecordIndicator; + + @JsonProperty (PROPERTIES_REVIEW_PERIOD) + @JsonSerialize (using = ReviewPeriodSerializer.class) + private ReviewPeriod reviewPeriod; + + @JsonProperty (PROPERTIES_HELD_CHILDREN_COUNT) + private Integer heldChildrenCount; + + @JsonProperty (PROPERTIES_LOCATION) + private String location; + + @JsonProperty (PROPERTIES_IS_CLOSED) + private Boolean isClosed; + + /*********************************************************/ + /** Optional parameters - Applies only to records */ + /*********************************************************/ + @JsonProperty (PROPERTIES_DATE_FILED) + private String dateField; + + @JsonProperty (PROPERTIES_RECORD_SEARCH_HAS_DISPOSITION_SCHEDULE) + private Boolean recordSearchHasDispositionSchedule; + + @JsonProperty (PROPERTIES_ORIGINAL_NAME) + private String originalName; + + + /*********************************/ + /** Electronic record parameters */ + /*********************************/ + @JsonProperty (PROPERTIES_VERSION_TYPE) + private String versionType; + + @JsonProperty (PROPERTIES_VERSION_LABEL) + private String versionLabel; + + @JsonProperty (PROPERTIES_DATE_TIME_ORIGINAL) + private String dateTimeOriginal; + + @JsonProperty (PROPERTIES_EXPOSURE_TIME) + private Double exposureTime; + + @JsonProperty (PROPERTIES_FLASH) + private Boolean flash; + + @JsonProperty (PROPERTIES_F_NUMBER) + private Double fNumber; + + @JsonProperty (PROPERTIES_FOCAL_LENGTH) + private Double focalLength; + + @JsonProperty (PROPERTIES_ISO_SPEED_RATINGS) + private Integer isoSpeedRatings; + + @JsonProperty (PROPERTIES_MANUFACTURER) + private String manufacturer; + + @JsonProperty (PROPERTIES_MODEL) + private String model; + + @JsonProperty (PROPERTIES_ORIENTATION) + private Integer orientation; + + @JsonProperty (PROPERTIES_PIXEL_X_DIMENSION) + private Integer pixelXDimension; + + @JsonProperty (PROPERTIES_PIXEL_Y_DIMENSION) + private Integer pixelYDimension; + + @JsonProperty (PROPERTIES_RESOLUTION_UNIT) + private String resolutionUnit; + + @JsonProperty (PROPERTIES_SOFTWARE) + private String software; + + @JsonProperty (PROPERTIES_X_RESOLUTION) + private Double xResolution; + + @JsonProperty (PROPERTIES_Y_RESOLUTION) + private Double yResolution; + + /*************************************/ + /** Non-electronic record parameters */ + /*************************************/ + + @JsonProperty (PROPERTIES_SHELF) + private String shelf; + + @JsonProperty (PROPERTIES_STORAGE_LOCATION) + private String storageLocation; + + @JsonProperty (PROPERTIES_FILE) + private String file; + + @JsonProperty (PROPERTIES_BOX) + private String box; + + @JsonProperty (PROPERTIES_NUMBER_OF_COPIES) + private Integer numberOfCopies; + + @JsonProperty (PROPERTIES_PHYSICAL_SIZE) + private Integer physicalSize; + + @JsonProperty (PROPERTIES_OWNER) + private Owner owner; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferCollection.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferCollection.java new file mode 100644 index 0000000000..c536facaf5 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferCollection.java @@ -0,0 +1,40 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.transfer; + +import org.alfresco.rest.core.RestModels; + +/** + * Handle collection of {@link TransferEntry} + * @author Silviu Dinuta + * @since 2.6 + */ +public class TransferCollection extends RestModels +{ + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferEntry.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferEntry.java new file mode 100644 index 0000000000..8185d2558a --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferEntry.java @@ -0,0 +1,56 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.transfer; + +import org.alfresco.rest.core.RestModels; +import org.alfresco.rest.rm.community.model.transfercontainer.TransferContainer; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for transfer entry + * + * @author Silviu Dinuta + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class TransferEntry extends RestModels +{ + @JsonProperty + private Transfer entry; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferProperties.java new file mode 100644 index 0000000000..75f8d19c8d --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfer/TransferProperties.java @@ -0,0 +1,88 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.transfer; + +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IDENTIFIER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ID_IS_TEMPORARILY_EDITABLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_OWNER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ROOT_NODE_REF; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PDF_INDICATOR; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TRANSFER_LOCATION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ACCESSION_INDICATOR; + +import org.alfresco.rest.rm.community.model.common.Owner; +import org.alfresco.utility.model.TestModel; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for Transfer properties + * + * @author Dinuta Silviu + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class TransferProperties extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true, value = PROPERTIES_ID_IS_TEMPORARILY_EDITABLE) + private Boolean idIsTemporarilyEditable; + + @JsonProperty (required = true, value = PROPERTIES_IDENTIFIER) + private String identifier; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty (PROPERTIES_ROOT_NODE_REF) + private String rootNodeRef; + + @JsonProperty (PROPERTIES_OWNER) + private Owner owner; + + @JsonProperty (PROPERTIES_PDF_INDICATOR) + private Boolean pdfIndicator; + + @JsonProperty (PROPERTIES_TRANSFER_LOCATION) + private String transferLocation; + + @JsonProperty (PROPERTIES_ACCESSION_INDICATOR) + private Boolean accessionIndicator; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfercontainer/TransferContainer.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfercontainer/TransferContainer.java new file mode 100644 index 0000000000..6ae938db2b --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfercontainer/TransferContainer.java @@ -0,0 +1,95 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.transfercontainer; + +import java.util.List; + +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.utility.model.TestModel; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for Transfer Container + * + * @author Silviu Dinuta + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class TransferContainer extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true) + private RestByUserModel createdByUser; + + @JsonProperty (required = true) + private String modifiedAt; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + @JsonProperty (required = true) + private List aspectNames; + + @JsonProperty (required = true) + private String createdAt; + + @JsonProperty (required = true) + private RestByUserModel modifiedByUser; + + @JsonProperty (required = true) + private String name; + + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private TransferContainerProperties properties; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private List allowableOperations; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfercontainer/TransferContainerProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfercontainer/TransferContainerProperties.java new file mode 100644 index 0000000000..d360b776a5 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/transfercontainer/TransferContainerProperties.java @@ -0,0 +1,76 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.model.transfercontainer; + +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_COUNT; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IDENTIFIER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ID_IS_TEMPORARILY_EDITABLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ROOT_NODE_REF; + +import org.alfresco.utility.model.TestModel; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for Transfer Container properties + * + * @author Dinuta Silviu + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class TransferContainerProperties extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true, value = PROPERTIES_ID_IS_TEMPORARILY_EDITABLE) + private Boolean idIsTemporarilyEditable; + + @JsonProperty (required = true, value = PROPERTIES_IDENTIFIER) + private String identifier; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty (PROPERTIES_ROOT_NODE_REF) + private String rootNodeRef; + + @JsonProperty (PROPERTIES_COUNT) + private Integer count; + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainer.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainer.java new file mode 100644 index 0000000000..26b5cbcc88 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainer.java @@ -0,0 +1,97 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.unfiledcontainer; + +import java.util.List; + +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.utility.model.TestModel; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for unfiled container + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class UnfiledContainer extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true) + private RestByUserModel createdByUser; + + @JsonProperty (required = true) + private String modifiedAt; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + @JsonProperty (required = true) + private List aspectNames; + + @JsonProperty (required = true) + private String createdAt; + + @JsonProperty (required = true) + private RestByUserModel modifiedByUser; + + @JsonProperty (required = true) + private String name; + + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private UnfiledContainerProperties properties; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private List allowableOperations; + + @JsonProperty + private Path path; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponent.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChild.java similarity index 59% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponent.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChild.java index ab43e79613..3affd91574 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponent.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChild.java @@ -24,21 +24,17 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.model.fileplancomponents; - -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.ALLOWABLE_OPERATIONS; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.IS_CLOSED; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.IS_COMPLETED; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PATH; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.RELATIVE_PATH; +package org.alfresco.rest.rm.community.model.unfiledcontainer; import java.util.List; -import com.fasterxml.jackson.annotation.JsonProperty; - +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.rest.rm.community.model.record.RecordContent; import org.alfresco.utility.model.TestModel; +import com.fasterxml.jackson.annotation.JsonProperty; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -46,10 +42,9 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** - * POJO for file plan component + * POJO for record category child * * @author Tuna Aksoy - * @author Rodica Sutu * @since 2.6 */ @Builder @@ -57,65 +52,68 @@ import lombok.NoArgsConstructor; @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor -public class FilePlanComponent extends TestModel +public class UnfiledContainerChild extends TestModel { + /*************************/ + /** Mandatory parameters */ + /*************************/ @JsonProperty (required = true) - private String id; + private String createdAt; @JsonProperty (required = true) - private String parentId; + private Boolean isUnfiledRecordFolder; @JsonProperty (required = true) - private String name; + private Boolean isRecord; @JsonProperty (required = true) - private String nodeType; - - @JsonProperty (required = true) - private Boolean isCategory; - - @JsonProperty (required = true) - private Boolean isRecordFolder; - - @JsonProperty (required = true) - private Boolean isFile; - - @JsonProperty - private Boolean hasRetentionSchedule; - - @JsonProperty(value = IS_CLOSED) - private Boolean isClosed; - - @JsonProperty(value = IS_COMPLETED) - private Boolean isCompleted; - - @JsonProperty (required = true) - private List aspectNames; - - @JsonProperty (required = true) - private FilePlanComponentUserInfo createdByUser; - - @JsonProperty(value = PROPERTIES) - private FilePlanComponentProperties properties; - - @JsonProperty (value = ALLOWABLE_OPERATIONS) - private List allowableOperations; - - @JsonProperty (required = false) - private FilePlanComponentContent content; - - @JsonProperty (value = PATH) - private FilePlanComponentPath path; + private RestByUserModel createdByUser; @JsonProperty (required = true) private String modifiedAt; @JsonProperty (required = true) - private String createdAt; + private RestByUserModel modifiedByUser; @JsonProperty (required = true) - private FilePlanComponentUserInfo modifiedByUser; + private String name; - @JsonProperty (value = RELATIVE_PATH) + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private UnfiledContainerChildProperties properties; + + @JsonProperty + private List aspectNames; + + @JsonProperty + private Boolean hasRetentionSchedule; + + @JsonProperty + private Boolean isClosed; + + @JsonProperty + private List allowableOperations; + + @JsonProperty + private Path path; + + @JsonProperty private String relativePath; + + @JsonProperty + private RecordContent content; + + @JsonProperty + private Boolean isCompleted; } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildCollection.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildCollection.java new file mode 100644 index 0000000000..ae4e9add28 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildCollection.java @@ -0,0 +1,41 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.unfiledcontainer; + +import org.alfresco.rest.core.RestModels; + +/** + * Handle collection of {@link UnfiledContainerChildEntry} + * + * @author Kristijan Conkas + * @author Tuna Aksoy + * @since 2.6 + */ +public class UnfiledContainerChildCollection extends RestModels +{ + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildEntry.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildEntry.java new file mode 100644 index 0000000000..3988d95b6e --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildEntry.java @@ -0,0 +1,48 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.unfiledcontainer; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.core.RestModels; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * POJO for record category child entry + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class UnfiledContainerChildEntry extends RestModels +{ + @JsonProperty + private UnfiledContainerChild entry; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildProperties.java new file mode 100644 index 0000000000..672ce850e4 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerChildProperties.java @@ -0,0 +1,196 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.unfiledcontainer; + +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_BOX; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DATE_TIME_ORIGINAL; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DESCRIPTION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_EXPOSURE_TIME; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FILE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FLASH; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FOCAL_LENGTH; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_F_NUMBER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IDENTIFIER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ID_IS_TEMPORARILY_EDITABLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ISO_SPEED_RATINGS; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_MANUFACTURER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_MODEL; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_NUMBER_OF_COPIES; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ORIENTATION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ORIGINAL_NAME; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_OWNER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PHYSICAL_SIZE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PIXEL_X_DIMENSION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PIXEL_Y_DIMENSION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RESOLUTION_UNIT; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_REVIEW_PERIOD; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ROOT_NODE_REF; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_SHELF; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_SOFTWARE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_STORAGE_LOCATION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TITLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VERSION_LABEL; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VERSION_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VITAL_RECORD_INDICATOR; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_X_RESOLUTION; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_Y_RESOLUTION; + +import org.alfresco.rest.rm.community.model.common.Owner; +import org.alfresco.rest.rm.community.model.common.ReviewPeriod; +import org.alfresco.rest.rm.community.util.ReviewPeriodSerializer; +import org.alfresco.utility.model.TestModel; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for record category child properties + * + * @author Tuna Aksoy + * @author Ana Bozianu + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class UnfiledContainerChildProperties extends TestModel +{ + /**************************************************************************/ + /** Mandatory parameters - Shared by unfiled record folder and records */ + /**************************************************************************/ + @JsonProperty (required = true, value = PROPERTIES_TITLE) + private String title; + + @JsonProperty (required = true, value = PROPERTIES_VITAL_RECORD_INDICATOR) + private Boolean vitalRecordIndicator; + + @JsonProperty (required = true, value = PROPERTIES_ROOT_NODE_REF) + private String rootNodeRef; + + @JsonProperty (required = true, value = PROPERTIES_ID_IS_TEMPORARILY_EDITABLE) + private Boolean idIsTemporarilyEditable; + + @JsonProperty (required = true, value = PROPERTIES_IDENTIFIER) + private String identifier; + + @JsonProperty (required = true, value = PROPERTIES_REVIEW_PERIOD) + @JsonSerialize (using = ReviewPeriodSerializer.class) + private ReviewPeriod reviewPeriod; + + @JsonProperty (required = true, value = PROPERTIES_DESCRIPTION) + private String description; + + /*********************************/ + /** Electronic record parameters */ + /*********************************/ + @JsonProperty (PROPERTIES_VERSION_TYPE) + private String versionType; + + @JsonProperty (PROPERTIES_VERSION_LABEL) + private String versionLabel; + + @JsonProperty (PROPERTIES_DATE_TIME_ORIGINAL) + private String dateTimeOriginal; + + @JsonProperty (PROPERTIES_EXPOSURE_TIME) + private Double exposureTime; + + @JsonProperty (PROPERTIES_FLASH) + private Boolean flash; + + @JsonProperty (PROPERTIES_F_NUMBER) + private Double fNumber; + + @JsonProperty (PROPERTIES_FOCAL_LENGTH) + private Double focalLength; + + @JsonProperty (PROPERTIES_ISO_SPEED_RATINGS) + private Integer isoSpeedRatings; + + @JsonProperty (PROPERTIES_MANUFACTURER) + private String manufacturer; + + @JsonProperty (PROPERTIES_MODEL) + private String model; + + @JsonProperty (PROPERTIES_ORIENTATION) + private Integer orientation; + + @JsonProperty (PROPERTIES_PIXEL_X_DIMENSION) + private Integer pixelXDimension; + + @JsonProperty (PROPERTIES_PIXEL_Y_DIMENSION) + private Integer pixelYDimension; + + @JsonProperty (PROPERTIES_RESOLUTION_UNIT) + private String resolutionUnit; + + @JsonProperty (PROPERTIES_SOFTWARE) + private String software; + + @JsonProperty (PROPERTIES_X_RESOLUTION) + private Double xResolution; + + @JsonProperty (PROPERTIES_Y_RESOLUTION) + private Double yResolution; + + @JsonProperty (PROPERTIES_ORIGINAL_NAME) + private String originalName; + + /*************************************/ + /** Non-electronic record parameters */ + /*************************************/ + + @JsonProperty (PROPERTIES_SHELF) + private String shelf; + + @JsonProperty (PROPERTIES_STORAGE_LOCATION) + private String storageLocation; + + @JsonProperty (PROPERTIES_FILE) + private String file; + + @JsonProperty (PROPERTIES_BOX) + private String box; + + @JsonProperty (PROPERTIES_NUMBER_OF_COPIES) + private Integer numberOfCopies; + + @JsonProperty (PROPERTIES_PHYSICAL_SIZE) + private Integer physicalSize; + + @JsonProperty (PROPERTIES_OWNER) + private Owner owner; +} \ No newline at end of file diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerProperties.java new file mode 100644 index 0000000000..c82efad674 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledContainerProperties.java @@ -0,0 +1,71 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.unfiledcontainer; + +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IDENTIFIER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ID_IS_TEMPORARILY_EDITABLE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_OWNER; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_ROOT_NODE_REF; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TITLE; + +import org.alfresco.rest.rm.community.model.common.Owner; +import org.alfresco.utility.model.TestModel; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for file plan properties + * + * @author Tuna Aksoy + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class UnfiledContainerProperties extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true, value = PROPERTIES_ID_IS_TEMPORARILY_EDITABLE) + private Boolean idIsTemporarilyEditable; + + @JsonProperty (required = true, value = PROPERTIES_IDENTIFIER) + private String identifier; + + @JsonProperty (required = true, value = PROPERTIES_ROOT_NODE_REF) + private String rootNodeRef; + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledRecordFolder.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledRecordFolder.java new file mode 100644 index 0000000000..da1ea046a8 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/unfiledcontainer/UnfiledRecordFolder.java @@ -0,0 +1,113 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.model.unfiledcontainer; + +import java.util.List; + +import org.alfresco.rest.model.RestByUserModel; +import org.alfresco.rest.rm.community.model.common.Path; +import org.alfresco.rest.rm.community.model.record.RecordContent; +import org.alfresco.utility.model.TestModel; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * POJO for unfiled container + * + * @author Ramona Popa + * @since 2.6 + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class UnfiledRecordFolder extends TestModel +{ + /*************************/ + /** Mandatory parameters */ + /*************************/ + @JsonProperty (required = true) + private String createdAt; + + @JsonProperty (required = true) + private RestByUserModel createdByUser; + + @JsonProperty (required = true) + private String modifiedAt; + + @JsonProperty (required = true) + private RestByUserModel modifiedByUser; + + @JsonProperty (required = true) + private String name; + + @JsonProperty (required = true) + private String id; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String parentId; + + /************************/ + /** Optional parameters */ + /************************/ + @JsonProperty + private UnfiledContainerChildProperties properties; + + @JsonProperty + private List aspectNames; + + @JsonProperty + private Boolean hasRetentionSchedule; + + @JsonProperty + private Boolean isClosed; + + @JsonProperty + private List allowableOperations; + + @JsonProperty + private Path path; + + @JsonProperty + private String relativePath; + + @JsonProperty + private RecordContent content; + + @JsonProperty + private Boolean isCompleted; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/RMModelRequest.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/RMModelRequest.java index bf0648a60c..4b543c3b3e 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/RMModelRequest.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/RMModelRequest.java @@ -26,9 +26,15 @@ */ package org.alfresco.rest.rm.community.requests; +import static lombok.AccessLevel.PRIVATE; +import static lombok.AccessLevel.PROTECTED; + import org.alfresco.rest.core.RMRestWrapper; import org.alfresco.rest.requests.ModelRequest; +import lombok.Getter; +import lombok.Setter; + /** * Extends {@link ModelRequest} to set {@link RMRestWrapper} * @@ -37,22 +43,16 @@ import org.alfresco.rest.requests.ModelRequest; */ public abstract class RMModelRequest extends ModelRequest { + @Getter (value = PROTECTED) + @Setter (value = PRIVATE) private RMRestWrapper rmRestWrapper; /** - * @return the rmRestWrapper - */ - public RMRestWrapper getRMRestWrapper() - { - return this.rmRestWrapper; - } - - /** - * @param rmRestWrapper + * @param restWrapper */ public RMModelRequest(RMRestWrapper rmRestWrapper) { super(rmRestWrapper.getRestWrapper()); - this.rmRestWrapper = rmRestWrapper; + setRmRestWrapper(rmRestWrapper); } } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/GSCoreAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/GSCoreAPI.java new file mode 100644 index 0000000000..e7e7405b56 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/GSCoreAPI.java @@ -0,0 +1,182 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.requests.gscore; + +import static java.lang.Integer.parseInt; +import static java.lang.String.format; + +import com.jayway.restassured.RestAssured; + +import org.alfresco.rest.core.RMRestProperties; +import org.alfresco.rest.core.RMRestWrapper; +import org.alfresco.rest.rm.community.requests.RMModelRequest; +import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.FilesAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RMUserAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.TransferAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.TransferContainerAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledRecordFolderAPI; + +/** + * Defines the entire GS Core API + * {@link http://host:port/gs-api-explorer} select "GS Core API" + * + * @author Tuna Aksoy + * @since 2.6 + */ +public class GSCoreAPI extends RMModelRequest +{ + /** + * Constructor + * + * @param rmRestWrapper RM REST Wrapper + * @param rmRestProperties RM REST Properties + */ + public GSCoreAPI(RMRestWrapper rmRestWrapper, RMRestProperties rmRestProperties) + { + super(rmRestWrapper); + RestAssured.baseURI = format("%s://%s", rmRestProperties.getScheme(), rmRestProperties.getServer()); + RestAssured.port = parseInt(rmRestProperties.getPort()); + RestAssured.basePath = rmRestProperties.getRestRmPath(); + restWrapper.configureRequestSpec().setBasePath(RestAssured.basePath); + } + + /** + * Provides DSL on all REST calls under ig-sites/rm/... API path + * + * @return {@link RMSiteAPI} + */ + public RMSiteAPI usingRMSite() + { + return new RMSiteAPI(getRmRestWrapper()); + } + + /** + * Provides DSL on all REST calls under file-plans/... API path + * + * @return {@link FilePlanAPI} + */ + public FilePlanAPI usingFilePlans() + { + return new FilePlanAPI(getRmRestWrapper()); + } + + /** + * Provides DSL on all REST calls under record-categories/... API path + * + * @return {@link RecordCategoryAPI} + */ + public RecordCategoryAPI usingRecordCategory() + { + return new RecordCategoryAPI(getRmRestWrapper()); + } + + /** + * Provides DSL on all REST calls under record-folders/... API path + * + * @return {@link RecordFolderAPI} + */ + public RecordFolderAPI usingRecordFolder() + { + return new RecordFolderAPI(getRmRestWrapper()); + } + + /** + * Provides DSL on all REST calls under records/... API path + * + * @return {@link FilePlanComponentAPI} + */ + public RecordsAPI usingRecords() + { + return new RecordsAPI(getRmRestWrapper()); + } + + /** + * Provides DSL on all REST calls under files/... API path + * + * @return {@link FilesAPI} + */ + public FilesAPI usingFiles() + { + return new FilesAPI(getRmRestWrapper()); + } + + /** + * Provides DSL on all REST calls under transfer-containers/... API path + * + * @return {@link TransferContainerAPI} + */ + public TransferContainerAPI usingTransferContainer() + { + return new TransferContainerAPI(getRmRestWrapper()); + } + + /** + * Provides DSL on all REST calls under transfers/... API path + * + * @return {@link TransferAPI} + */ + public TransferAPI usingTransfer() + { + return new TransferAPI(getRmRestWrapper()); + } + + /** + * Provides DSL for RM unfiled container API + * + * @return {@link UnfiledContainerAPI} + */ + public UnfiledContainerAPI usingUnfiledContainers() + { + return new UnfiledContainerAPI(getRmRestWrapper()); + } + + /** + * Provides DSL for RM unfiled record folders API + * + * @return {@link UnfiledRecordFolderAPI} + */ + public UnfiledRecordFolderAPI usingUnfiledRecordFolder() + { + return new UnfiledRecordFolderAPI(getRmRestWrapper()); + } + + /** + * Provides DSL for RM user management API + * + * @return {@link RMUserAPI} + */ + public RMUserAPI usingRMUser() + { + return new RMUserAPI(getRmRestWrapper()); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/FilePlanAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/FilePlanAPI.java new file mode 100644 index 0000000000..fcaea4b6a1 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/FilePlanAPI.java @@ -0,0 +1,175 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.requests.gscore.api; + +import static org.alfresco.rest.core.RestRequest.requestWithBody; +import static org.alfresco.rest.core.RestRequest.simpleRequest; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; +import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.POST; + +import org.alfresco.rest.core.RMRestWrapper; +import org.alfresco.rest.rm.community.model.fileplan.FilePlan; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryCollection; +import org.alfresco.rest.rm.community.requests.RMModelRequest; + +/** + * File plan REST API Wrapper + * + * @author Ramona Popa + * @author Tuna Aksoy + * @since 2.6 + */ +public class FilePlanAPI extends RMModelRequest +{ + /** + * Constructor. + * + * @param restWrapper + */ + public FilePlanAPI(RMRestWrapper rmRestWrapper) + { + super(rmRestWrapper); + } + + /** + * see {@link #getFilePlan(String, String)} + */ + public FilePlan getFilePlan(String filePlanId) + { + mandatoryString("filePlanId", filePlanId); + + return getFilePlan(filePlanId, EMPTY); + } + + /** + * Gets a file plan. + * + * @param filePlanId The identifier of a file plan + * @param parameters The URL parameters to add + * @return The {@link FilePlan} for the given {@code filePlanId} + * @throws Exception for the following cases: + *
    + *
  • {@code filePlanId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code filePlanId}
  • + *
  • {@code filePlanId} does not exist
  • + *
+ */ + public FilePlan getFilePlan(String filePlanId, String parameters) + { + mandatoryString("filePlanId", filePlanId); + + return getRmRestWrapper().processModel(FilePlan.class, simpleRequest( + GET, + "/file-plans/{filePlanId}?{parameters}", + filePlanId, + parameters + )); + } + + /** + * see {@link #getRootRecordCategories(String, String)} + */ + public RecordCategoryCollection getRootRecordCategories(String filePlanId) + { + mandatoryString("filePlanId", filePlanId); + + return getRootRecordCategories(filePlanId, EMPTY); + } + + /** + * Gets the children (root categories) of a file plan. + * + * @param filePlanId The identifier of a file plan + * @param parameters The URL parameters to add + * @return The {@link RecordCategoryCollection} for the given {@code filePlanId} + * @throws Exception for the following cases: + *
    + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code filePlanId}
  • + *
  • {@code filePlanId} does not exist
  • + *
+ */ + public RecordCategoryCollection getRootRecordCategories(String filePlanId, String parameters) + { + mandatoryString("filePlanId", filePlanId); + + return getRmRestWrapper().processModels(RecordCategoryCollection.class, simpleRequest( + GET, + "file-plans/{filePlanId}/categories?{parameters}", + filePlanId, + parameters + )); + } + + /** + * see {@link #createRootRecordCategory(RecordCategory, String, String)} + */ + public RecordCategory createRootRecordCategory(RecordCategory recordCategoryModel, String filePlanId) throws Exception + { + mandatoryObject("recordCategoryModel", recordCategoryModel); + mandatoryString("filePlanId", filePlanId); + + return createRootRecordCategory(recordCategoryModel, filePlanId, EMPTY); + } + + /** + * Creates a root record category. + * + * @param recordCategoryModel The record category model which holds the information + * @param filePlanId The identifier of a file plan + * @param parameters The URL parameters to add + * @return The created {@link RecordCategory} + * @throws Exception for the following cases: + *
    + *
  • {@code filePlanId} is not a valid format or {@code filePlanId} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to add children to {@code filePlanId}
  • + *
  • {@code filePlanIds} does not exist
  • + *
  • new name clashes with an existing node in the current parent container
  • + *
  • model integrity exception, including node name with invalid characters
  • + *
+ */ + public RecordCategory createRootRecordCategory(RecordCategory recordCategoryModel, String filePlanId, String parameters) throws Exception + { + mandatoryObject("recordCategoryModel", recordCategoryModel); + mandatoryString("filePlanId", filePlanId); + + return getRmRestWrapper().processModel(RecordCategory.class, requestWithBody( + POST, + toJson(recordCategoryModel), + "file-plans/{filePlanId}/categories?{parameters}", + filePlanId, + parameters + )); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/FilesAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/FilesAPI.java similarity index 77% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/FilesAPI.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/FilesAPI.java index afa93ac2f8..a297837136 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/FilesAPI.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/FilesAPI.java @@ -24,7 +24,7 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.requests.igCoreAPI; +package org.alfresco.rest.rm.community.requests.gscore.api; import static org.alfresco.rest.core.RestRequest.simpleRequest; import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; @@ -32,10 +32,8 @@ import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.springframework.http.HttpMethod.POST; import org.alfresco.rest.core.RMRestWrapper; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; +import org.alfresco.rest.rm.community.model.record.Record; import org.alfresco.rest.rm.community.requests.RMModelRequest; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; /** * Files REST API Wrapper @@ -43,8 +41,6 @@ import org.springframework.stereotype.Component; * @author Kristijan Conkas * @since 2.6 */ -@Component -@Scope (value = "prototype") public class FilesAPI extends RMModelRequest { /** @@ -57,16 +53,17 @@ public class FilesAPI extends RMModelRequest /** * Declare file as record + * * @param fileId The Id of a file to declare as record * @param parameters Request parameters, refer to API documentation for more details - * @return The {@link FilePlanComponent} for created record + * @return The {@link Record} for created record * @throws Exception for malformed JSON responses */ - public FilePlanComponent declareAsRecord(String fileId, String parameters) throws Exception + public Record declareAsRecord(String fileId, String parameters) throws Exception { mandatoryString("fileId", fileId); - return getRMRestWrapper().processModel(FilePlanComponent.class, simpleRequest( + return getRmRestWrapper().processModel(Record.class, simpleRequest( POST, "/files/{fileId}/declare?{parameters}", fileId, @@ -76,12 +73,15 @@ public class FilesAPI extends RMModelRequest /** * A no-parameter version of {@link FilesAPI#declareAsRecord} + * * @param fileId The Id of a file to declare as record - * @return The {@link FilePlanComponent} for created record + * @return The {@link Record} for created record * @throws Exception for malformed JSON responses */ - public FilePlanComponent declareAsRecord(String fileId) throws Exception + public Record declareAsRecord(String fileId) throws Exception { + mandatoryString("fileId", fileId); + return declareAsRecord(fileId, EMPTY); } } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RMSiteAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RMSiteAPI.java similarity index 91% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RMSiteAPI.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RMSiteAPI.java index 0b7ef5e80d..75e4062f47 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RMSiteAPI.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RMSiteAPI.java @@ -24,7 +24,7 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.requests.igCoreAPI; +package org.alfresco.rest.rm.community.requests.gscore.api; import static org.alfresco.rest.core.RestRequest.requestWithBody; import static org.alfresco.rest.core.RestRequest.simpleRequest; @@ -72,9 +72,9 @@ public class RMSiteAPI extends RMModelRequest */ public RMSite getSite() throws Exception { - return getRMRestWrapper().processModel(RMSite.class, simpleRequest( + return getRmRestWrapper().processModel(RMSite.class, simpleRequest( GET, - "ig-sites/rm" + "gs-sites/rm" )); } @@ -95,10 +95,10 @@ public class RMSiteAPI extends RMModelRequest { mandatoryObject("rmSiteModel", rmSiteModel); - return getRMRestWrapper().processModel(RMSite.class, requestWithBody( + return getRmRestWrapper().processModel(RMSite.class, requestWithBody( POST, toJson(rmSiteModel), - "ig-sites" + "gs-sites" )); } @@ -115,9 +115,9 @@ public class RMSiteAPI extends RMModelRequest */ public void deleteRMSite() throws Exception { - getRMRestWrapper().processEmptyModel(simpleRequest( + getRmRestWrapper().processEmptyModel(simpleRequest( DELETE, - "ig-sites/rm" + "gs-sites/rm" )); } @@ -139,10 +139,10 @@ public class RMSiteAPI extends RMModelRequest { mandatoryObject("rmSiteProperties", rmSiteModel); - return getRMRestWrapper().processModel(RMSite.class, requestWithBody( + return getRmRestWrapper().processModel(RMSite.class, requestWithBody( PUT, toJson(rmSiteModel), - "ig-sites/rm" + "gs-sites/rm" )); } @@ -161,6 +161,6 @@ public class RMSiteAPI extends RMModelRequest public boolean existsRMSite() throws Exception { getSite(); - return getRMRestWrapper().getStatusCode().equals(OK.toString()); + return getRmRestWrapper().getStatusCode().equals(OK.toString()); } } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RMUserAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RMUserAPI.java similarity index 86% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RMUserAPI.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RMUserAPI.java index d1c4ff0535..8783a3384e 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RMUserAPI.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RMUserAPI.java @@ -24,7 +24,7 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.requests.igCoreAPI; +package org.alfresco.rest.rm.community.requests.gscore.api; import static com.jayway.restassured.RestAssured.basic; import static com.jayway.restassured.RestAssured.given; @@ -42,13 +42,10 @@ import org.alfresco.dataprep.AlfrescoHttpClient; import org.alfresco.dataprep.AlfrescoHttpClientFactory; import org.alfresco.rest.core.RMRestProperties; import org.alfresco.rest.core.RMRestWrapper; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; import org.alfresco.rest.rm.community.model.user.UserPermissions; import org.alfresco.rest.rm.community.model.user.UserRoles; import org.alfresco.rest.rm.community.requests.RMModelRequest; import org.alfresco.utility.model.UserModel; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; /** * RM user management API @@ -59,8 +56,6 @@ import org.springframework.stereotype.Component; // FIXME: As of December 2016 there is no v1-style API for managing RM users and users' // roles/permissions. Until such APIs have become available, methods in this class are just proxies to // "old-style" API calls. -@Component -@Scope (value = "prototype") public class RMUserAPI extends RMModelRequest { /** @@ -77,16 +72,16 @@ public class RMUserAPI extends RMModelRequest */ private AlfrescoHttpClient getAlfrescoHttpClient() { - RMRestProperties properties = getRMRestWrapper().getRmRestProperties(); - + RMRestProperties properties = getRmRestWrapper().getRmRestProperties(); + AlfrescoHttpClientFactory factory = new AlfrescoHttpClientFactory(); factory.setHost(properties.getServer()); factory.setPort(Integer.parseInt(properties.getPort())); factory.setScheme(properties.getScheme()); - + return factory.getObject(); } - + /** * Assign RM role to user * @param userName User's username @@ -95,8 +90,8 @@ public class RMUserAPI extends RMModelRequest */ public void assignRoleToUser(String userName, String userRole) throws Exception { - UserModel adminUser = getRMRestWrapper().getTestUser(); - + UserModel adminUser = getRmRestWrapper().getTestUser(); + // get an "old-style" REST API client AlfrescoHttpClient client = getAlfrescoHttpClient(); @@ -111,25 +106,25 @@ public class RMUserAPI extends RMModelRequest .log().all() .pathParam("role", userRole) .pathParam("authority", userName) - .param("alf_ticket", client.getAlfTicket(adminUser.getUsername(), + .param("alf_ticket", client.getAlfTicket(adminUser.getUsername(), adminUser.getPassword())) .when() .post("/rm/roles/{role}/authorities/{authority}") .prettyPeek() .andReturn(); - getRMRestWrapper().setStatusCode(Integer.toString(response.getStatusCode())); + getRmRestWrapper().setStatusCode(Integer.toString(response.getStatusCode())); } /** * Helper method to add permission on a component to user - * @param component {@link FilePlanComponent} on which permission should be given + * @param component The id of the file plan component on which permission should be given * @param user {@link UserModel} for a user to be granted permission * @param permission {@link UserPermissions} to be granted */ - public void addUserPermission(FilePlanComponent component, UserModel user, String permission) + public void addUserPermission(String filePlanComponentId, UserModel user, String permission) { - UserModel adminUser = getRMRestWrapper().getTestUser(); - + UserModel adminUser = getRmRestWrapper().getTestUser(); + // get an "old-style" REST API client AlfrescoHttpClient client = getAlfrescoHttpClient(); @@ -146,20 +141,20 @@ public class RMUserAPI extends RMModelRequest .setBaseUri(client.getApiUrl()) .setBasePath("/") .build(); - + // execute an "old-style" API call Response response = given() .spec(spec) .auth().basic(adminUser.getUsername(), adminUser.getPassword()) .contentType(ContentType.JSON) .body(bodyJson.toString()) - .pathParam("nodeId", component.getId()) + .pathParam("nodeId", filePlanComponentId) .log().all() .when() .post("/node/workspace/SpacesStore/{nodeId}/rmpermissions") .prettyPeek() .andReturn(); - getRMRestWrapper().setStatusCode(Integer.toString(response.getStatusCode())); + getRmRestWrapper().setStatusCode(Integer.toString(response.getStatusCode())); } /** @@ -172,9 +167,9 @@ public class RMUserAPI extends RMModelRequest */ public boolean createUser(String userName, String userPassword, String userEmail) { - UserModel adminUser = getRMRestWrapper().getTestUser(); + UserModel adminUser = getRmRestWrapper().getTestUser(); AlfrescoHttpClient client = getAlfrescoHttpClient(); - + JsonObject body = buildObject() .add("userName", userName) .add("firstName", userName) @@ -182,7 +177,7 @@ public class RMUserAPI extends RMModelRequest .add("password", userPassword) .add("email", userEmail) .getJson(); - + RequestSpecification spec = new RequestSpecBuilder() .setBaseUri(client.getApiUrl()) .setBasePath("/") @@ -190,7 +185,7 @@ public class RMUserAPI extends RMModelRequest .setContentType(ContentType.JSON) .setBody(body.toString()) .build(); - + // create POST request to "people" endpoint Response response = given() .spec(spec) @@ -199,7 +194,7 @@ public class RMUserAPI extends RMModelRequest .post("people") .prettyPeek() .andReturn(); - + return (response.getStatusCode() == OK.value()); } } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordCategoryAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordCategoryAPI.java new file mode 100644 index 0000000000..7bfbe6eba0 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordCategoryAPI.java @@ -0,0 +1,242 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.requests.gscore.api; + +import static org.alfresco.rest.core.RestRequest.requestWithBody; +import static org.alfresco.rest.core.RestRequest.simpleRequest; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; +import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.springframework.http.HttpMethod.DELETE; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.HttpMethod.PUT; + +import org.alfresco.rest.core.RMRestWrapper; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChildCollection; +import org.alfresco.rest.rm.community.requests.RMModelRequest; + +/** + * Record category REST API Wrapper + * + * @author Tuna Aksoy + * @since 2.6 + */ +public class RecordCategoryAPI extends RMModelRequest +{ + /** + * Constructor. + * + * @param restWrapper + */ + public RecordCategoryAPI(RMRestWrapper rmRestWrapper) + { + super(rmRestWrapper); + } + + /** + * Deletes a record category. + * + * @param recordCategoryId The identifier of a record category + * @throws Exception for the following cases: + *
    + *
  • {@code recordCategoryId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to delete {@code recordCategoryId}
  • + *
  • {@code recordCategoryId} does not exist
  • + *
  • {@code recordCategoryId} is locked and cannot be deleted
  • + *
+ */ + public void deleteRecordCategory(String recordCategoryId) + { + mandatoryString("recordCategoryId", recordCategoryId); + + getRmRestWrapper().processEmptyModel(simpleRequest( + DELETE, + "record-categories/{recordCategoryId}", + recordCategoryId + )); + } + + /** + * see {@link #getRecordCategory(String, String)} + */ + public RecordCategory getRecordCategory(String recordCategoryId) + { + mandatoryString("recordCategoryId", recordCategoryId); + + return getRecordCategory(recordCategoryId, EMPTY); + } + + /** + * Gets a record category. + * + * @param recordCategoryId The identifier of a record category + * @param parameters The URL parameters to add + * @return The {@link RecordCategory} for the given {@code recordCategoryId} + * @throws Exception for the following cases: + *
    + *
  • {@code recordCategoryId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code recordCategoryId}
  • + *
  • {@code recordCategoryId} does not exist
  • + *
+ */ + public RecordCategory getRecordCategory(String recordCategoryId, String parameters) + { + mandatoryString("recordCategoryId", recordCategoryId); + + return getRmRestWrapper().processModel(RecordCategory.class, simpleRequest( + GET, + "record-categories/{recordCategoryId}?{parameters}", + recordCategoryId, + parameters + )); + } + + /** + * see {@link #updateRecordCategory(RecordCategory, String, String) + */ + public RecordCategory updateRecordCategory(RecordCategory recordCategoryModel, String recordCategoryId) throws Exception + { + mandatoryObject("recordCategoryModel", recordCategoryModel); + mandatoryString("recordCategoryId", recordCategoryId); + + return updateRecordCategory(recordCategoryModel, recordCategoryId, EMPTY); + } + + /** + * Updates a record category. + * + * @param recordCategoryModel The record category model which holds the information + * @param recordCategoryId The identifier of a record category + * @param parameters The URL parameters to add + * @param returns The updated {@link RecordCategory} + * @throws Exception for the following cases: + *
    + *
  • the update request is invalid or {@code recordCategoryId} is not a valid format or {@code recordCategoryModel} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to update {@code recordCategoryId}
  • + *
  • {@code recordCategoryId} does not exist
  • + *
  • the updated name clashes with an existing record category in the current parent category
  • + *
  • model integrity exception, including file name with invalid characters
  • + *
+ */ + public RecordCategory updateRecordCategory(RecordCategory recordCategoryModel, String recordCategoryId, String parameters) throws Exception + { + mandatoryObject("recordCategoryModel", recordCategoryModel); + mandatoryString("recordCategoryId", recordCategoryId); + + return getRmRestWrapper().processModel(RecordCategory.class, requestWithBody( + PUT, + toJson(recordCategoryModel), + "record-categories/{recordCategoryId}?{parameters}", + recordCategoryId, + parameters + )); + } + + /** + * see {@link #getRecordCategoryChildren(String, String)} + */ + public RecordCategoryChildCollection getRecordCategoryChildren(String recordCategoryId) + { + mandatoryString("recordCategoryId", recordCategoryId); + + return getRecordCategoryChildren(recordCategoryId, EMPTY); + } + + /** + * Gets the children of a record category. + * + * @param recordCategoryId The identifier of a record category + * @param parameters The URL parameters to add + * @return The {@link RecordCategoryChildCollection} for the given {@code recordCategoryId} + * @throws Exception for the following cases: + *
    + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code recordCategoryId}
  • + *
  • {@code recordCategoryId} does not exist
  • + *
+ */ + public RecordCategoryChildCollection getRecordCategoryChildren(String recordCategoryId, String parameters) + { + mandatoryString("recordCategoryId", recordCategoryId); + + return getRmRestWrapper().processModels(RecordCategoryChildCollection.class, simpleRequest( + GET, + "record-categories/{recordCategoryId}/children?{parameters}", + recordCategoryId, + parameters + )); + } + + /** + * see {@link #createRecordCategoryChild(RecordCategoryChild, String, String)} + */ + public RecordCategoryChild createRecordCategoryChild(RecordCategoryChild recordCategoryChildModel, String recordCategoryId) throws Exception + { + mandatoryObject("recordCategoryChildModel", recordCategoryChildModel); + mandatoryString("recordCategoryId", recordCategoryId); + + return createRecordCategoryChild(recordCategoryChildModel, recordCategoryId, EMPTY); + } + + /** + * Creates a record category child. Can be a record category or a record folder. + * + * @param recordCategoryChildModel The record category child model which holds the information + * @param recordCategoryId The identifier of a record category + * @param parameters The URL parameters to add + * @return The created {@link RecordCategoryChild} + * @throws Exception for the following cases: + *
    + *
  • {@code recordCategoryId} is not a valid format or {@code recordCategoryChildModel} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to add children to {@code recordCategoryId}
  • + *
  • {@code recordCategoryId} does not exist
  • + *
  • new name clashes with an existing node in the current parent container
  • + *
  • model integrity exception, including node name with invalid characters
  • + *
+ */ + public RecordCategoryChild createRecordCategoryChild(RecordCategoryChild recordCategoryChildModel, String recordCategoryId, String parameters) throws Exception + { + mandatoryObject("filePlanComponentProperties", recordCategoryChildModel); + mandatoryString("recordCategoryId", recordCategoryId); + + return getRmRestWrapper().processModel(RecordCategoryChild.class, requestWithBody( + POST, + toJson(recordCategoryChildModel), + "record-categories/{recordCategoryId}/children?{parameters}", + recordCategoryId, + parameters + )); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordFolderAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordFolderAPI.java new file mode 100644 index 0000000000..46e0824eeb --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordFolderAPI.java @@ -0,0 +1,291 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.requests.gscore.api; + +import static org.alfresco.rest.core.RestRequest.requestWithBody; +import static org.alfresco.rest.core.RestRequest.simpleRequest; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; +import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.springframework.http.HttpMethod.DELETE; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.HttpMethod.PUT; +import static org.testng.Assert.fail; + +import java.io.File; +import java.util.Iterator; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.http.ContentType; + +import org.alfresco.rest.core.RMRestWrapper; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.recordfolder.RecordFolder; +import org.alfresco.rest.rm.community.model.recordfolder.RecordFolderCollection; +import org.alfresco.rest.rm.community.requests.RMModelRequest; +import org.alfresco.rest.rm.community.util.FilePlanComponentMixIn; + +/** + * Record folder REST API Wrapper + * + * @author Tuna Aksoy + * @since 2.6 + */ +public class RecordFolderAPI extends RMModelRequest +{ + /** + * Constructor. + * + * @param restWrapper + */ + public RecordFolderAPI(RMRestWrapper rmRestWrapper) + { + super(rmRestWrapper); + } + + /** + * Deletes a record folder. + * + * @param recordFolderId The identifier of a record folder + * @throws Exception for the following cases: + *
    + *
  • {@code recordFolderId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to delete {@code recordFolderId}
  • + *
  • {@code recordFolderId} does not exist
  • + *
  • {@code recordFolderId} is locked and cannot be deleted
  • + *
+ */ + public void deleteRecordFolder(String recordFolderId) + { + mandatoryString("recordFolderId", recordFolderId); + + getRmRestWrapper().processEmptyModel(simpleRequest( + DELETE, + "record-folders/{recordFolderId}", + recordFolderId + )); + } + + /** + * see {@link #getRecordFolder(String, String)} + */ + public RecordFolder getRecordFolder(String recordFolderId) + { + mandatoryString("recordFolderId", recordFolderId); + + return getRecordFolder(recordFolderId, EMPTY); + } + + /** + * Gets a record folder. + * + * @param recordFolderId The identifier of a record folder + * @param parameters The URL parameters to add + * @return The {@link RecordFolder} for the given {@code recordFolderId} + * @throws Exception for the following cases: + *
    + *
  • {@code recordFolderId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code recordFolderId}
  • + *
  • {@code recordFolderId} does not exist
  • + *
+ */ + public RecordFolder getRecordFolder(String recordFolderId, String parameters) + { + mandatoryString("recordFolderId", recordFolderId); + + return getRmRestWrapper().processModel(RecordFolder.class, simpleRequest( + GET, + "record-folders/{recordFolderId}?{parameters}", + recordFolderId, + parameters + )); + } + + /** + * see {@link #updateRecordFolder(RecordFolder, String, String) + */ + public RecordFolder updateRecordFolder(RecordFolder recordFolderModel, String recordFolderId) throws Exception + { + mandatoryObject("recordFolderModel", recordFolderModel); + mandatoryString("recordFolderId", recordFolderId); + + return updateRecordFolder(recordFolderModel, recordFolderId, EMPTY); + } + + /** + * Updates a record folder. + * + * @param recordFolderModel The record folder model which holds the information + * @param recordFolderId The identifier of a record folder + * @param parameters The URL parameters to add + * @param returns The updated {@link RecordFolder} + * @throws Exception for the following cases: + *
    + *
  • the update request is invalid or {@code recordFolderId} is not a valid format or {@code recordFolderModel} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to update {@code recordFolderId}
  • + *
  • {@code recordFolderId} does not exist
  • + *
  • the updated name clashes with an existing record folder in the current parent category
  • + *
  • model integrity exception, including file name with invalid characters
  • + *
+ */ + public RecordFolder updateRecordFolder(RecordFolder recordFolderModel, String recordFolderId, String parameters) throws Exception + { + mandatoryObject("recordFolderModel", recordFolderModel); + mandatoryString("recordFolderId", recordFolderId); + + return getRmRestWrapper().processModel(RecordFolder.class, requestWithBody( + PUT, + toJson(recordFolderModel), + "record-folders/{recordFolderId}?{parameters}", + recordFolderId, + parameters + )); + } + + /** + * see {@link #getRecordFolderChildren(String, String)} + */ + public RecordFolderCollection getRecordFolderChildren(String recordFolderId) + { + mandatoryString("recordFolderId", recordFolderId); + + return getRecordFolderChildren(recordFolderId, EMPTY); + } + + /** + * Gets the children of a record folder. + * + * @param recordFolderId The identifier of a record folder + * @param parameters The URL parameters to add + * @return The {@link RecordFolderCollection} for the given {@code recordFolderId} + * @throws Exception for the following cases: + *
    + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code recordFolderId}
  • + *
  • {@code recordFolderId} does not exist
  • + *
+ */ + public RecordFolderCollection getRecordFolderChildren(String recordFolderId, String parameters) + { + mandatoryString("recordFolderId", recordFolderId); + + return getRmRestWrapper().processModels(RecordFolderCollection.class, simpleRequest( + GET, + "record-folders/{recordFolderId}/records?{parameters}", + recordFolderId, + parameters + )); + } + + /** + * see {@link #createRecord(Record, String, String)} + */ + public Record createRecord(Record recordModel, String recordFolderId) throws Exception + { + mandatoryObject("recordModel", recordModel); + mandatoryString("recordFolderId", recordFolderId); + + return createRecord(recordModel, recordFolderId, EMPTY); + } + + /** + * Create a record from file resource + * + * @param electronicRecordModel {@link Record} for electronic record to be created + * @param recordContent {@link File} pointing to the content of the electronic record to be created + * @param recordFolderId The identifier of a record folder + * @return newly created {@link Record} + * @throws Exception for invalid recordModel JSON strings + */ + public Record createRecord(Record recordModel, String recordFolderId, File recordContent) throws Exception + { + mandatoryString("recordFolderId", recordFolderId); + mandatoryObject("recordContent", recordContent); + mandatoryObject("recordModel", recordModel); + + if (!recordModel.getNodeType().equals(CONTENT_TYPE)) + { + fail("Only electronic records are supported"); + } + + /* + * For file uploads nodeBodyCreate is ignored hence can't be used. Append all Record fields + * to the request. + */ + RequestSpecBuilder builder = getRmRestWrapper().configureRequestSpec(); + JsonNode root = new ObjectMapper().readTree(toJson(recordModel, Record.class, FilePlanComponentMixIn.class)); + // add request fields + Iterator fieldNames = root.fieldNames(); + while (fieldNames.hasNext()) + { + String fieldName = fieldNames.next(); + builder.addMultiPart(fieldName, root.get(fieldName).asText(), ContentType.JSON.name()); + } + builder.addMultiPart("filedata", recordContent, ContentType.BINARY.name()); + + // create node with given content + return createRecord(recordModel, recordFolderId); + } + + /** + * Creates a record in a record folder child, i.e. a record. + * + * @param recordModel The record model which holds the information + * @param recordfolderId The identifier of a record folder + * @param parameters The URL parameters to add + * @return The created {@link Record} + * @throws Exception for the following cases: + *
    + *
  • {@code recordFolderId is not a valid format or {@code recordModel} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to add children to {@code recordFolderId}
  • + *
  • {@code recordFolderId} does not exist
  • + *
  • model integrity exception, including node name with invalid characters
  • + *
+ */ + public Record createRecord(Record recordModel, String recordFolderId, String parameters) throws Exception + { + mandatoryObject("recordModel", recordModel); + mandatoryString("recordFolderId", recordFolderId); + + return getRmRestWrapper().processModel(Record.class, requestWithBody( + POST, + toJson(recordModel), + "record-folders/{recordFolderId}/records?{parameters}", + recordFolderId, + parameters + )); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RecordsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordsAPI.java similarity index 53% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RecordsAPI.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordsAPI.java index be85682079..ab403834f6 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RecordsAPI.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordsAPI.java @@ -24,35 +24,32 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.rm.community.requests.igCoreAPI; +package org.alfresco.rest.rm.community.requests.gscore.api; -import static com.jayway.restassured.RestAssured.given; import static org.alfresco.rest.core.RestRequest.requestWithBody; import static org.alfresco.rest.core.RestRequest.simpleRequest; import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.springframework.http.HttpMethod.DELETE; import static org.springframework.http.HttpMethod.GET; import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.HttpMethod.PUT; import com.jayway.restassured.response.ResponseBody; import org.alfresco.rest.core.RMRestWrapper; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.fileplancomponents.RecordBodyFile; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.record.RecordBodyFile; import org.alfresco.rest.rm.community.requests.RMModelRequest; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; /** - * Records REST API Wrapper + * Records REST API Wrapper * * @author Rodica Sutu * @since 2.6 */ -@Component -@Scope (value = "prototype") public class RecordsAPI extends RMModelRequest { /** @@ -79,11 +76,11 @@ public class RecordsAPI extends RMModelRequest public ResponseBody getRecordContent(String recordId) throws Exception { mandatoryString("recordId", recordId); - - return getRMRestWrapper() + + return getRmRestWrapper() .processHtmlResponse(simpleRequest(GET,"records/{recordId}/content", recordId)) .getBody(); - + } /** @@ -91,7 +88,7 @@ public class RecordsAPI extends RMModelRequest * * @param recordBodyFile The properties where to file the record * @param recordId The id of the record to file - * @return The {@link FilePlanComponent} with the given properties + * @return The {@link Record} with the given properties * @throws Exception for the following cases: *
    *
  • Invalid parameter: {@code recordBodyFile} is not a valid format,{@code recordId} is not a record
  • @@ -103,7 +100,7 @@ public class RecordsAPI extends RMModelRequest *
* */ - public FilePlanComponent fileRecord(RecordBodyFile recordBodyFile, String recordId) throws Exception + public Record fileRecord(RecordBodyFile recordBodyFile, String recordId) throws Exception { mandatoryObject("recordBodyFile", recordBodyFile); mandatoryString("recordId", recordId); @@ -116,7 +113,7 @@ public class RecordsAPI extends RMModelRequest * * @param recordBodyFile The properties where to file the record * @param recordId The id of the record to file - * @return The {@link FilePlanComponent} with the given properties + * @return The {@link Record} with the given properties * @throws Exception for the following cases: *
    *
  • Invalid parameter: {@code recordBodyFile} is not a valid format,{@code recordId} is not a record
  • @@ -128,12 +125,12 @@ public class RecordsAPI extends RMModelRequest *
* */ - public FilePlanComponent fileRecord(RecordBodyFile recordBodyFile, String recordId, String parameters) throws Exception + public Record fileRecord(RecordBodyFile recordBodyFile, String recordId, String parameters) throws Exception { mandatoryObject("requestBodyFile", recordBodyFile); mandatoryString("recordId", recordId); - return getRMRestWrapper().processModel(FilePlanComponent.class, requestWithBody( + return getRmRestWrapper().processModel(Record.class, requestWithBody( POST, toJson(recordBodyFile), "/records/{recordId}/file?{parameters}", @@ -141,5 +138,106 @@ public class RecordsAPI extends RMModelRequest parameters )); } -} + /** + * Deletes a record. + * + * @param recordId The identifier of a record + * @throws Exception for the following cases: + *
    + *
  • {@code recordId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to delete {@code recordId}
  • + *
  • {@code recordId} does not exist
  • + *
  • {@code recordId} is locked and cannot be deleted
  • + *
+ */ + public void deleteRecord(String recordId) + { + mandatoryString("recordId", recordId); + + getRmRestWrapper().processEmptyModel(simpleRequest( + DELETE, + "records/{recordId}", + recordId + )); + } + + /** + * see {@link #getRecord(String, String)} + */ + public Record getRecord(String recordId) + { + mandatoryString("recordId", recordId); + + return getRecord(recordId, EMPTY); + } + + /** + * Gets a record. + * + * @param recordId The identifier of a record + * @param parameters The URL parameters to add + * @return The {@link Record} for the given {@code recordId} + * @throws Exception for the following cases: + *
    + *
  • {@code recordId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code recordId}
  • + *
  • {@code recordId} does not exist
  • + *
+ */ + public Record getRecord(String recordId, String parameters) + { + mandatoryString("recordId", recordId); + + return getRmRestWrapper().processModel(Record.class, simpleRequest( + GET, + "records/{recordId}?{parameters}", + recordId, + parameters + )); + } + + /** + * see {@link #updateRecord(Record, String, String) + */ + public Record updateRecord(Record recordModel, String recordId) throws Exception + { + mandatoryObject("recordModel", recordModel); + mandatoryString("recordId", recordId); + + return updateRecord(recordModel, recordId, EMPTY); + } + + /** + * Updates a record. + * + * @param recordModel The record model which holds the information + * @param recordId The identifier of a record + * @param parameters The URL parameters to add + * @param returns The updated {@link Record} + * @throws Exception for the following cases: + *
    + *
  • the update request is invalid or {@code recordId} is not a valid format or {@code recordModel} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to update {@code recordId}
  • + *
  • {@code recordId} does not exist
  • + *
  • the updated name clashes with an existing record in the current parent folder
  • + *
  • model integrity exception, including file name with invalid characters
  • + *
+ */ + public Record updateRecord(Record recordModel, String recordId, String parameters) throws Exception + { + mandatoryObject("recordModel", recordModel); + mandatoryString("recordId", recordId); + + return getRmRestWrapper().processModel(Record.class, requestWithBody( + PUT, + toJson(recordModel), + "records/{recordId}?{parameters}", + recordId, + parameters + )); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/TransferAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/TransferAPI.java new file mode 100644 index 0000000000..9dd1c12012 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/TransferAPI.java @@ -0,0 +1,125 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.requests.gscore.api; + +import static org.alfresco.rest.core.RestRequest.simpleRequest; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.springframework.http.HttpMethod.GET; + +import org.alfresco.rest.core.RMRestWrapper; +import org.alfresco.rest.rm.community.model.transfer.Transfer; +import org.alfresco.rest.rm.community.model.transfer.TransferChildCollection; +import org.alfresco.rest.rm.community.requests.RMModelRequest; + +/** + * Transfer REST API Wrapper + * + * @author Silviu Dinuta + * @since 2.6 + */ +public class TransferAPI extends RMModelRequest +{ + /** + * @param rmRestWrapper + */ + public TransferAPI(RMRestWrapper rmRestWrapper) + { + super(rmRestWrapper); + } + + /** + * see {@link #getTransfer(String, String)} + */ + public Transfer getTransfer(String transferId) + { + mandatoryString("transferId", transferId); + + return getTransfer(transferId, EMPTY); + } + + /** + * Gets a transfer. + * + * @param transferId The identifier of a transfer + * @param parameters The URL parameters to add + * @return The {@link Transfer} for the given {@code transferId} + * @throws Exception for the following cases: + *
    + *
  • {@code transferId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code transferId}
  • + *
  • {@code transferId} does not exist
  • + *
+ */ + public Transfer getTransfer(String transferId, String parameters) + { + mandatoryString("transferId", transferId); + + return getRmRestWrapper().processModel(Transfer.class, simpleRequest( + GET, + "/transfers/{transferId}?{parameters}", + transferId, + parameters + )); + } + /** + * see {@link #getTransfersChildren(String, String)} + */ + public TransferChildCollection getTransfersChildren(String transferId) + { + mandatoryString("transferId", transferId); + + return getTransfersChildren(transferId, EMPTY); + } + + /** + * Gets the children (record folder or record) of a transfer. + * + * @param transferId The identifier of a transfer + * @param parameters The URL parameters to add + * @return The {@link TransferChildCollection} for the given {@code transferId} + * @throws Exception for the following cases: + *
    + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code transferId}
  • + *
  • {@code filePlanId} does not exist
  • + *
+ */ + public TransferChildCollection getTransfersChildren(String transferId, String parameters) + { + mandatoryString("transferId", transferId); + + return getRmRestWrapper().processModels(TransferChildCollection.class, simpleRequest( + GET, + "transfers/{filePlanId}/children?{parameters}", + transferId, + parameters + )); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/TransferContainerAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/TransferContainerAPI.java new file mode 100644 index 0000000000..fb4988dfdc --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/TransferContainerAPI.java @@ -0,0 +1,172 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.rm.community.requests.gscore.api; + +import static org.alfresco.rest.core.RestRequest.requestWithBody; +import static org.alfresco.rest.core.RestRequest.simpleRequest; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; +import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.PUT; + +import org.alfresco.rest.core.RMRestWrapper; +import org.alfresco.rest.rm.community.model.transfer.TransferCollection; +import org.alfresco.rest.rm.community.model.transfercontainer.TransferContainer; +import org.alfresco.rest.rm.community.requests.RMModelRequest; + +/** + * Transfer Container REST API Wrapper + * + * @author Silviu Dinuta + * @since 2.6 + */ +public class TransferContainerAPI extends RMModelRequest +{ + /** + * @param rmRestWrapper + */ + public TransferContainerAPI(RMRestWrapper rmRestWrapper) + { + super(rmRestWrapper); + } + + /** + * see {@link #getTransferContainer(String, String)} + */ + public TransferContainer getTransferContainer(String transferContainerId) + { + mandatoryString("transferContainerId", transferContainerId); + + return getTransferContainer(transferContainerId, EMPTY); + } + + /** + * Gets a transfer container. + * + * @param transferContainerId The identifier of a transfer container + * @param parameters The URL parameters to add + * @return The {@link TransferContainer} for the given {@code transferContainerId} + * @throws Exception for the following cases: + *
    + *
  • {@code transferContainerId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code transferContainerId}
  • + *
  • {@code transferContainerId} does not exist
  • + *
+ */ + public TransferContainer getTransferContainer(String transferContainerId, String parameters) + { + mandatoryString("transferContainerId", transferContainerId); + + return getRmRestWrapper().processModel(TransferContainer.class, simpleRequest( + GET, + "/transfer-containers/{transferContainerId}?{parameters}", + transferContainerId, + parameters + )); + } + + /** + * see {@link #updateTransferContainer(TransferContainer, String, String) + */ + public TransferContainer updateTransferContainer(TransferContainer transferContainerModel, String transferContainerId) throws Exception + { + mandatoryObject("transferContainerModel", transferContainerModel); + mandatoryString("transferContainerId", transferContainerId); + + return updateTransferContainer(transferContainerModel, transferContainerId, EMPTY); + } + + /** + * Updates a transfer container. + * + * @param transferContainerModel The transfer container model which holds the information + * @param transferContainerId The identifier of a transfer container + * @param parameters The URL parameters to add + * @param returns The updated {@link TransferContainer} + * @throws Exception for the following cases: + *
    + *
  • the update request is invalid or {@code transferContainerId} is not a valid format or {@code transferContainerModel} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to update {@code transferContainerId}
  • + *
  • {@code transferContainerId} does not exist
  • + *
  • the updated name clashes with an existing transfer container in the current file plan
  • + *
  • model integrity exception, including transfer container name with invalid characters
  • + *
+ */ + public TransferContainer updateTransferContainer(TransferContainer transferContainerModel, String transferContainerId, String parameters) throws Exception + { + mandatoryObject("transferContainerModel", transferContainerModel); + mandatoryString("transferContainerId", transferContainerId); + + return getRmRestWrapper().processModel(TransferContainer.class, requestWithBody( + PUT, + toJson(transferContainerModel), + "transfer-containers/{transferContainerId}?{parameters}", + transferContainerId, + parameters + )); + } + + /** + * see {@link #getTransfers(String, String)} + */ + public TransferCollection getTransfers(String transferContainerId) + { + mandatoryString("transferContainerId", transferContainerId); + + return getTransfers(transferContainerId, EMPTY); + } + + /** + * Gets the children (transfers) of a transfer container. + * + * @param transferContainerId The identifier of a transfer container + * @param parameters The URL parameters to add + * @return The {@link TransferCollection} for the given {@code transferContainerId} + * @throws Exception for the following cases: + *
    + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code transferContainerId}
  • + *
  • {@code filePlanId} does not exist
  • + *
+ */ + public TransferCollection getTransfers(String transferContainerId, String parameters) + { + mandatoryString("transferContainerId", transferContainerId); + + return getRmRestWrapper().processModels(TransferCollection.class, simpleRequest( + GET, + "transfer-containers/{filePlanId}/transfers?{parameters}", + transferContainerId, + parameters + )); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/UnfiledContainerAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/UnfiledContainerAPI.java new file mode 100644 index 0000000000..b1d91fbdbd --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/UnfiledContainerAPI.java @@ -0,0 +1,268 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.requests.gscore.api; + +import static org.alfresco.rest.core.RestRequest.requestWithBody; +import static org.alfresco.rest.core.RestRequest.simpleRequest; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; +import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.HttpMethod.PUT; +import static org.testng.Assert.fail; + +import java.io.File; +import java.util.Iterator; + +import org.alfresco.rest.core.RMRestWrapper; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainer; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildCollection; +import org.alfresco.rest.rm.community.requests.RMModelRequest; +import org.alfresco.rest.rm.community.util.FilePlanComponentMixIn; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.http.ContentType; + +/** + * Unfiled Container REST API Wrapper + * + * @author Tuna Aksoy + * @author Ana Bozianu + * @since 2.6 + */ +public class UnfiledContainerAPI extends RMModelRequest +{ + /** + * @param rmRestWrapper + */ + public UnfiledContainerAPI(RMRestWrapper rmRestWrapper) + { + super(rmRestWrapper); + } + + /** + * see {@link #getUnfiledContainer(String, String)} + */ + public UnfiledContainer getUnfiledContainer(String unfiledContainerId) + { + mandatoryString("unfiledContainerId", unfiledContainerId); + + return getUnfiledContainer(unfiledContainerId, EMPTY); + } + + /** + * Gets an unfiled record container. + * + * @param unfiledContainerId The identifier of a unfiled record container + * @param parameters The URL parameters to add + * @return The {@link UnfiledContainer} for the given {@code unfiledContainerId} + * @throws Exception for the following cases: + *
    + *
  • {@code unfiledContainerId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code unfiledContainerId}
  • + *
  • {@code unfiledContainerId} does not exist
  • + *
+ */ + public UnfiledContainer getUnfiledContainer(String unfiledContainerId, String parameters) + { + mandatoryString("unfiledContainerId", unfiledContainerId); + + return getRmRestWrapper().processModel(UnfiledContainer.class, simpleRequest( + GET, + "unfiled-containers/{unfiledContainerId}?{parameters}", + unfiledContainerId, + parameters + )); + } + + /** + * see {@link #getRootRecordCategories(String, String)} + */ + public UnfiledContainerChildCollection getUnfiledContainerChildren(String unfiledContainerId) + { + mandatoryString("unfiledContainerId", unfiledContainerId); + + return getUnfiledContainerChildren(unfiledContainerId, EMPTY); + } + + /** + * Gets the children of an unfiled records container + * + * @param unfiledContainerId The identifier of an unfiled records container + * @param parameters The URL parameters to add + * @return The {@link UnfiledContainerChildCollection} for the given {@code unfiledContainerId} + * @throws Exception for the following cases: + *
    + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code unfiledContainerId}
  • + *
  • {@code unfiledContainerId} does not exist
  • + *
+ */ + public UnfiledContainerChildCollection getUnfiledContainerChildren(String unfiledContainerId, String parameters) + { + mandatoryString("unfiledContainerId", unfiledContainerId); + + return getRmRestWrapper().processModels(UnfiledContainerChildCollection.class, simpleRequest( + GET, + "unfiled-containers/{unfiledContainerId}/children?{parameters}", + unfiledContainerId, + parameters + )); + } + + /** + * see {@link #createUnfiledContainerChild(UnfiledContainerChild, String, String)} + */ + public UnfiledContainerChild createUnfiledContainerChild(UnfiledContainerChild unfiledContainerChildModel, String unfiledContainerId) throws Exception + { + mandatoryObject("unfiledContainerChildModel", unfiledContainerChildModel); + mandatoryString("unfiledContainerId", unfiledContainerId); + + return createUnfiledContainerChild(unfiledContainerChildModel, unfiledContainerId, EMPTY); + } + + /** + * Creates an unfiled container child. Can be a record or an unfiled record folder. + * + * @param unfiledContainerChildModel The unfiled container child model which holds the information + * @param unfiledContainerId The identifier of an unfiled container + * @param parameters The URL parameters to add + * @return The created {@link UnfiledContainerChild} + * @throws Exception for the following cases: + *
    + *
  • {@code unfiledContainerId} is not a valid format or {@code unfiledContainerChildModel} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to add children to {@code unfiledContainerId}
  • + *
  • {@code unfiledContainerId} does not exist
  • + *
  • new name clashes with an existing node in the current parent container
  • + *
  • model integrity exception, including node name with invalid characters
  • + *
+ */ + public UnfiledContainerChild createUnfiledContainerChild(UnfiledContainerChild unfiledContainerChildModel, String unfiledContainerId, String parameters) throws Exception + { + mandatoryObject("unfiledContainerChildModel", unfiledContainerChildModel); + mandatoryString("unfiledContainerId", unfiledContainerId); + + return getRmRestWrapper().processModel(UnfiledContainerChild.class, requestWithBody( + POST, + toJson(unfiledContainerChildModel), + "unfiled-containers/{unfiledContainerId}/children?{parameters}", + unfiledContainerId, + parameters + )); + } + + /** + * Create a record from file resource + * + * @param unfiledContainerChildModel {@link UnfiledContainerChild} for electronic record to be created + * @param unfiledContainerChildContent {@link File} pointing to the content of the electronic record to be created + * @param unfiledContainerId The identifier of a unfiled container + * @return newly created {@link UnfiledContainerChild} + * @throws Exception for invalid recordModel JSON strings + */ + public UnfiledContainerChild uploadRecord(UnfiledContainerChild unfiledContainerChildModel, String unfiledContainerId, File unfiledContainerChildContent) throws Exception + { + mandatoryObject("unfiledContainerChildModel", unfiledContainerChildModel); + mandatoryObject("unfiledContainerChildContent", unfiledContainerChildContent); + mandatoryString("unfiledContainerId", unfiledContainerId); + + if (!unfiledContainerChildModel.getNodeType().equals(CONTENT_TYPE)) + { + fail("Only electronic records are supported"); + } + + /* + * For file uploads nodeBodyCreate is ignored hence can't be used. Append all Record fields + * to the request. + */ + RequestSpecBuilder builder = getRmRestWrapper().configureRequestSpec(); + JsonNode root = new ObjectMapper().readTree(toJson(unfiledContainerChildModel, Record.class, FilePlanComponentMixIn.class)); + // add request fields + Iterator fieldNames = root.fieldNames(); + while (fieldNames.hasNext()) + { + String fieldName = fieldNames.next(); + builder.addMultiPart(fieldName, root.get(fieldName).asText(), ContentType.JSON.name()); + } + builder.addMultiPart("filedata", unfiledContainerChildContent, ContentType.BINARY.name()); + + // create node with given content + return createUnfiledContainerChild(unfiledContainerChildModel, unfiledContainerId); + } + + /** + * see {@link #updateUnfiledContainer(UnfiledContainer, String, String) + */ + public UnfiledContainer updateUnfiledContainer(UnfiledContainer unfiledContainerModel, String unfiledContainerId) throws Exception + { + mandatoryObject("unfiledContainerModel", unfiledContainerModel); + mandatoryString("unfiledContainerId", unfiledContainerId); + + return updateUnfiledContainer(unfiledContainerModel, unfiledContainerId, EMPTY); + } + + /** + * Updates an unfiled record container + * + * @param unfiledContainerModel The unfiled record container model which holds the information + * @param unfiledContainerId The identifier of an unfiled record container + * @param parameters The URL parameters to add + * @param returns The updated {@link UnfiledContainer} + * @throws Exception for the following cases: + *
    + *
  • the update request is invalid or {@code unfiledContainerId} is not a valid format or {@code unfiledContainerModel} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to update {@code unfiledContainerId}
  • + *
  • {@code unfiledContainerId} does not exist
  • + *
  • the updated name clashes with an existing root category of special container in the current fileplan
  • + *
  • model integrity exception, including file name with invalid characters
  • + *
+ */ + public UnfiledContainer updateUnfiledContainer(UnfiledContainer unfiledContainerModel, String unfiledContainerId, String parameters) throws Exception + { + mandatoryObject("unfiledContainerModel", unfiledContainerModel); + mandatoryString("unfiledContainerId", unfiledContainerId); + + return getRmRestWrapper().processModel(UnfiledContainer.class, requestWithBody( + PUT, + toJson(unfiledContainerModel), + "unfiled-containers/{unfiledContainerId}?{parameters}", + unfiledContainerId, + parameters + )); + } + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/UnfiledRecordFolderAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/UnfiledRecordFolderAPI.java new file mode 100644 index 0000000000..b13a564329 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/UnfiledRecordFolderAPI.java @@ -0,0 +1,292 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.requests.gscore.api; + +import static org.alfresco.rest.core.RestRequest.requestWithBody; +import static org.alfresco.rest.core.RestRequest.simpleRequest; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; +import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.springframework.http.HttpMethod.DELETE; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.HttpMethod.PUT; +import static org.testng.Assert.fail; + +import java.io.File; +import java.util.Iterator; + +import org.alfresco.rest.core.RMRestWrapper; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildCollection; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledRecordFolder; +import org.alfresco.rest.rm.community.requests.RMModelRequest; +import org.alfresco.rest.rm.community.util.FilePlanComponentMixIn; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.http.ContentType; + +/** + * Unfiled Record Folders REST API Wrapper + * + * @author Ramona Popa + * @since 2.6 + */ +public class UnfiledRecordFolderAPI extends RMModelRequest +{ + /** + * @param rmRestWrapper + */ + public UnfiledRecordFolderAPI(RMRestWrapper rmRestWrapper) + { + super(rmRestWrapper); + } + + /** + * see {@link #getUnfiledRecordFolder(String, String)} + */ + public UnfiledRecordFolder getUnfiledRecordFolder(String unfiledRecordFolderId) + { + mandatoryString("unfiledRecordFolderId", unfiledRecordFolderId); + + return getUnfiledRecordFolder(unfiledRecordFolderId, EMPTY); + } + + /** + * Gets an unfiled record folder. + * + * @param unfiledRecordFolderId The identifier of a unfiled record folder + * @param parameters The URL parameters to add + * @return The {@link UnfiledRecordFolder} for the given {@code unfiledRecordFolderId} + * @throws Exception for the following cases: + *
    + *
  • {@code unfiledRecordFolderId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code unfiledRecordFolderId}
  • + *
  • {@code unfiledRecordFolderId} does not exist
  • + *
+ */ + public UnfiledRecordFolder getUnfiledRecordFolder(String unfiledRecordFolderId, String parameters) + { + mandatoryString("unfiledRecordFolderId", unfiledRecordFolderId); + + return getRmRestWrapper().processModel(UnfiledRecordFolder.class, simpleRequest( + GET, + "unfiled-record-folders/{unfiledRecordFolderId}?{parameters}", + unfiledRecordFolderId, + parameters + )); + } + + /** + * see {@link #getUnfiledRecordFolderChildren(String, String)} + */ + public UnfiledContainerChildCollection getUnfiledRecordFolderChildren(String unfiledRecordFolderId) + { + mandatoryString("unfiledRecordFolderId", unfiledRecordFolderId); + + return getUnfiledRecordFolderChildren(unfiledRecordFolderId, EMPTY); + } + + /** + * Gets the children of an unfiled record folder + * + * @param unfiledRecordFolderId The identifier of an unfiled records folder + * @param parameters The URL parameters to add + * @return The {@link UnfiledRecordFolderChildCollection} for the given {@code unfiledRecordFolderId} + * @throws Exception for the following cases: + *
    + *
  • authentication fails
  • + *
  • current user does not have permission to read {@code unfiledRecordFolderId}
  • + *
  • {@code unfiledRecordFolderId} does not exist
  • + *
+ */ + public UnfiledContainerChildCollection getUnfiledRecordFolderChildren(String unfiledRecordFolderId, String parameters) + { + mandatoryString("unfiledRecordFolderId", unfiledRecordFolderId); + + return getRmRestWrapper().processModels(UnfiledContainerChildCollection.class, simpleRequest( + GET, + "unfiled-record-folders/{unfiledRecordFolderId}/children?{parameters}", + unfiledRecordFolderId, + parameters + )); + } + + /** + * see {@link #createUnfiledRecordFolderChild(UnfiledContainerChild, String, String)} + */ + public UnfiledContainerChild createUnfiledRecordFolderChild(UnfiledContainerChild unfiledRecordFolderChildModel, String unfiledRecordFolderId) throws Exception + { + mandatoryObject("unfiledRecordFolderChildModel", unfiledRecordFolderChildModel); + mandatoryString("unfiledRecordFolderId", unfiledRecordFolderId); + + return createUnfiledRecordFolderChild(unfiledRecordFolderChildModel, unfiledRecordFolderId, EMPTY); + } + + /** + * Creates an unfiled record folder child. Can be a record or an unfiled record folder. + * + * @param unfiledRecordFolderChildModel The unfiled folder child model which holds the information + * @param unfiledRecordFolderId The identifier of an unfiled folder + * @param parameters The URL parameters to add + * @return The created {@link UnfiledRecordFolderChild} + * @throws Exception for the following cases: + *
    + *
  • {@code unfiledRecordFolderId} is not a valid format or {@code unfiledRecordFolderChildModel} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to add children to {@code unfiledRecordFolderId}
  • + *
  • {@code unfiledRecordFolderId} does not exist
  • + *
  • new name clashes with an existing node in the current parent container
  • + *
  • model integrity exception, including node name with invalid characters
  • + *
+ */ + public UnfiledContainerChild createUnfiledRecordFolderChild(UnfiledContainerChild unfiledRecordFolderChildModel, String unfiledRecordFolderId, String parameters) throws Exception + { + mandatoryObject("unfiledRecordFolderChildModel", unfiledRecordFolderChildModel); + mandatoryString("unfiledRecordFolderId", unfiledRecordFolderId); + + return getRmRestWrapper().processModel(UnfiledContainerChild.class, requestWithBody( + POST, + toJson(unfiledRecordFolderChildModel), + "unfiled-record-folders/{unfiledRecordFolderId}/children?{parameters}", + unfiledRecordFolderId, + parameters + )); + } + + /** + * Create a record from file resource + * + * @param unfiledRecordFolderChildModel {@link UnfiledContainerChild} for electronic record to be created + * @param unfiledRecordFolderChildContent {@link File} pointing to the content of the electronic record to be created + * @param unfiledRecordFolderId The identifier of a unfiled record folder + * @return newly created {@link UnfiledContainerChild} + * @throws Exception for invalid recordModel JSON strings + */ + public UnfiledContainerChild uploadRecord(UnfiledContainerChild unfiledRecordFolderChildModel, String unfiledRecordFolderId, File unfiledRecordFolderChildContent) throws Exception + { + mandatoryObject("unfiledRecordFolderChildModel", unfiledRecordFolderChildModel); + mandatoryObject("unfiledRecordFolderChildContent", unfiledRecordFolderChildContent); + mandatoryString("unfiledRecordFolderId", unfiledRecordFolderId); + + if (!unfiledRecordFolderChildModel.getNodeType().equals(CONTENT_TYPE)) + { + fail("Only electronic records are supported"); + } + + /* + * For file uploads nodeBodyCreate is ignored hence can't be used. Append all Record fields + * to the request. + */ + RequestSpecBuilder builder = getRmRestWrapper().configureRequestSpec(); + JsonNode root = new ObjectMapper().readTree(toJson(unfiledRecordFolderChildModel, Record.class, FilePlanComponentMixIn.class)); + // add request fields + Iterator fieldNames = root.fieldNames(); + while (fieldNames.hasNext()) + { + String fieldName = fieldNames.next(); + builder.addMultiPart(fieldName, root.get(fieldName).asText(), ContentType.JSON.name()); + } + builder.addMultiPart("filedata", unfiledRecordFolderChildContent, ContentType.BINARY.name()); + + // create node with given content + return createUnfiledRecordFolderChild(unfiledRecordFolderChildModel, unfiledRecordFolderId); + } + + /** + * see {@link #updateUnfiledRecordFolder(UnfiledRecordFolder, String, String) + */ + public UnfiledRecordFolder updateUnfiledRecordFolder(UnfiledRecordFolder unfiledRecordFolderModel, String unfiledRecordFolderId) throws Exception + { + mandatoryObject("unfiledRecordFolderModel", unfiledRecordFolderModel); + mandatoryString("unfiledRecordFolderId", unfiledRecordFolderId); + + return updateUnfiledRecordFolder(unfiledRecordFolderModel, unfiledRecordFolderId, EMPTY); + } + + /** + * Updates an unfiled record folder + * + * @param unfiledRecordFolderModel The unfiled record folder model which holds the information + * @param unfiledRecordFolderId The identifier of an unfiled record folder + * @param parameters The URL parameters to add + * @param returns The updated {@link UnfiledRecordFolder} + * @throws Exception for the following cases: + *
    + *
  • the update request is invalid or {@code unfiledRecordFolderId} is not a valid format or {@code unfiledRecordFolderModel} is invalid
  • + *
  • authentication fails
  • + *
  • current user does not have permission to update {@code unfiledRecordFolderId}
  • + *
  • {@code unfiledRecordFolderId} does not exist
  • + *
  • the updated name clashes with an existing root category of special container in the current fileplan
  • + *
  • model integrity exception, including file name with invalid characters
  • + *
+ */ + public UnfiledRecordFolder updateUnfiledRecordFolder(UnfiledRecordFolder unfiledRecordFolderModel, String unfiledRecordFolderId, String parameters) throws Exception + { + mandatoryObject("unfiledRecordFolderModel", unfiledRecordFolderModel); + mandatoryString("unfiledRecordFolderId", unfiledRecordFolderId); + + return getRmRestWrapper().processModel(UnfiledRecordFolder.class, requestWithBody( + PUT, + toJson(unfiledRecordFolderModel), + "unfiled-record-folders/{unfiledRecordFolderId}?{parameters}", + unfiledRecordFolderId, + parameters + )); + } + + /** + * Deletes an unfiled record folder. + * + * @param unfiledRecordFolderId The identifier of a unfiled record folder + * @throws Exception for the following cases: + *
    + *
  • {@code unfiledRecordFolderId} is not a valid format
  • + *
  • authentication fails
  • + *
  • current user does not have permission to delete {@code unfiledRecordFolderId}
  • + *
  • {@code unfiledRecordFolderId} does not exist
  • + *
  • {@code unfiledRecordFolderId} is locked and cannot be deleted
  • + *
+ */ + public void deleteUnfiledRecordFolder(String unfiledRecordFolderId) + { + mandatoryString("unfiledRecordFolderId", unfiledRecordFolderId); + + getRmRestWrapper().processEmptyModel(simpleRequest( + DELETE, + "unfiled-record-folders/{recordFolderId}", + unfiledRecordFolderId + )); + } + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/FilePlanComponentAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/FilePlanComponentAPI.java deleted file mode 100644 index afd8c15de0..0000000000 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/FilePlanComponentAPI.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * #%L - * Alfresco Records Management Module - * %% - * Copyright (C) 2005 - 2017 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * - - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.rest.rm.community.requests.igCoreAPI; - -import static com.jayway.restassured.RestAssured.basic; -import static com.jayway.restassured.RestAssured.given; -import static org.alfresco.rest.core.RestRequest.requestWithBody; -import static org.alfresco.rest.core.RestRequest.simpleRequest; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; -import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; -import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; -import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; -import static org.alfresco.rest.rm.community.util.PojoUtility.toJsonElectronicRecord; -import static org.apache.commons.lang3.StringUtils.EMPTY; -import static org.springframework.http.HttpMethod.DELETE; -import static org.springframework.http.HttpMethod.GET; -import static org.springframework.http.HttpMethod.POST; -import static org.springframework.http.HttpMethod.PUT; -import static org.testng.Assert.fail; - -import java.io.File; -import java.util.Iterator; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.io.Resources; -import com.jayway.restassured.builder.RequestSpecBuilder; -import com.jayway.restassured.http.ContentType; - -import org.alfresco.rest.core.RMRestWrapper; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentsCollection; -import org.alfresco.rest.rm.community.requests.RMModelRequest; - -/** - * File plan component REST API Wrapper - * - * @author Tuna Aksoy - * @since 2.6 - */ -public class FilePlanComponentAPI extends RMModelRequest -{ - /** - * Constructor - * - * @param restWrapper - */ - public FilePlanComponentAPI(RMRestWrapper rmRestWrapper) - { - super(rmRestWrapper); - } - - /** - * Get a file plan component - * - * @param filePlanComponentId The id of the file plan component to get - * @return The {@link FilePlanComponent} for the given file plan component id - * @throws Exception for the following cases: - *
    - *
  • {@code fileplanComponentId} is not a valid format
  • - *
  • authentication fails
  • - *
  • {@code fileplanComponentId} does not exist
  • - *
- */ - public FilePlanComponent getFilePlanComponent(String filePlanComponentId) throws Exception - { - mandatoryString("filePlanComponentId", filePlanComponentId); - - return getFilePlanComponent(filePlanComponentId, EMPTY); - } - - /** - * Get a file plan component - * - * @param filePlanComponentId The id of the file plan component to get - * @param parameters The URL parameters to add - * @return The {@link FilePlanComponent} for the given file plan component id - * @throws Exception for the following cases: - *
    - *
  • {@code fileplanComponentId} is not a valid format
  • - *
  • authentication fails
  • - *
  • {@code fileplanComponentId} does not exist
  • - *
- */ - public FilePlanComponent getFilePlanComponent(String filePlanComponentId, String parameters) throws Exception - { - mandatoryString("filePlanComponentId", filePlanComponentId); - - return getRMRestWrapper().processModel(FilePlanComponent.class, simpleRequest( - GET, - "fileplan-components/{fileplanComponentId}?{parameters}", - filePlanComponentId, - parameters - )); - } - - /** - * List child components of a file plan component - * - * @param filePlanComponentId The id of the file plan component of which to get child components - * @return The {@link FilePlanComponent} for the given file plan component id - * @throws Exception for the following cases: - *
    - *
  • {@code fileplanComponentId} is not a valid format
  • - *
  • authentication fails
  • - *
  • {@code fileplanComponentId} does not exist
  • - *
- */ - public FilePlanComponentsCollection listChildComponents(String filePlanComponentId) throws Exception - { - mandatoryString("filePlanComponentId", filePlanComponentId); - - return getRMRestWrapper().processModels(FilePlanComponentsCollection.class, simpleRequest( - GET, - "fileplan-components/{fileplanComponentId}/children", - filePlanComponentId - )); - } - - /** - * List child components of a file plan component - * @param parameters The URL parameters to add - * @param filePlanComponentId The id of the file plan component of which to get child components - * @return The {@link FilePlanComponent} for the given file plan component id - * @throws Exception for the following cases: - *
    - *
  • {@code fileplanComponentId} is not a valid format
  • - *
  • authentication fails
  • - *
  • {@code fileplanComponentId} does not exist
  • - *
- */ - public FilePlanComponentsCollection listChildComponents(String filePlanComponentId, String parameters) throws Exception - { - mandatoryString("filePlanComponentId", filePlanComponentId); - - return getRMRestWrapper().processModels(FilePlanComponentsCollection.class, simpleRequest( - GET, - "fileplan-components/{fileplanComponentId}/children?{parameters}", - filePlanComponentId,parameters - )); - } - - /** - * Creates a file plan component with the given properties under the parent node with the given id - * - * @param filePlanComponentModel The properties of the file plan component to be created - * @param parentId The id of the parent where the new file plan component should be created - * @return The {@link FilePlanComponent} with the given properties - * @throws Exception for the following cases: - *
    - *
  • {@code fileplanComponentId} is not a valid format
  • - *
  • authentication fails
  • - *
  • current user does not have permission to add children to {@code fileplanComponentId}
  • - *
  • {@code fileplanComponentId} does not exist
  • - *
  • new name clashes with an existing node in the current parent container
  • - *
  • model integrity exception, including node name with invalid characters
  • - *
- */ - public FilePlanComponent createFilePlanComponent(FilePlanComponent filePlanComponentModel, String parentId) throws Exception - { - mandatoryObject("filePlanComponentProperties", filePlanComponentModel); - mandatoryString("parentId", parentId); - - return createFilePlanComponent(filePlanComponentModel, parentId, EMPTY); - } - - /** - * Creates a file plan component with the given properties under the parent node with the given id - * - * @param filePlanComponentModel The properties of the file plan component to be created - * @param parameters The URL parameters to add - * @param parentId The id of the parent where the new file plan component should be created - * @return The {@link FilePlanComponent} with the given properties - * @throws Exception for the following cases: - *
    - *
  • {@code fileplanComponentId} is not a valid format
  • - *
  • authentication fails
  • - *
  • current user does not have permission to add children to {@code fileplanComponentId}
  • - *
  • {@code fileplanComponentId} does not exist
  • - *
  • new name clashes with an existing node in the current parent container
  • - *
  • model integrity exception, including node name with invalid characters
  • - *
- */ - public FilePlanComponent createFilePlanComponent(FilePlanComponent filePlanComponentModel, String parentId, String parameters) throws Exception - { - mandatoryObject("filePlanComponentProperties", filePlanComponentModel); - mandatoryString("parentId", parentId); - - return getRMRestWrapper().processModel(FilePlanComponent.class, requestWithBody( - POST, - toJson(filePlanComponentModel), - "fileplan-components/{fileplanComponentId}/children?{parameters}", - parentId, - parameters - )); - } - - /** - * Create electronic record from file resource - * @param electronicRecordModel {@link FilePlanComponent} for electronic record to be created - * @param fileName the name of the resource file - * @param parentId parent container id - * @return newly created {@link FilePlanComponent} - * @throws Exception if operation failed - */ - public FilePlanComponent createElectronicRecord(FilePlanComponent electronicRecordModel, String fileName, String parentId) throws Exception - { - return createElectronicRecord(electronicRecordModel, new File(Resources.getResource(fileName).getFile()), parentId); - } - - /** - * Create electronic record from file resource - * - * @param electronicRecordModel {@link FilePlanComponent} for electronic record to be created - * @param recordContent {@link File} pointing to the content of the electronic record to be created - * @param parentId parent container id - * @return newly created {@link FilePlanComponent} - * @throws Exception for invalid electronicRecordModel JSON strings - */ - public FilePlanComponent createElectronicRecord(FilePlanComponent electronicRecordModel, File recordContent, - String parentId) throws Exception - { - mandatoryObject("electronicRecordModel", electronicRecordModel); - mandatoryString("parentId", parentId); - if (!electronicRecordModel.getNodeType().equals(CONTENT_TYPE)) - { - fail("Only electronic records are supported"); - } - - /* - * For file uploads nodeBodyCreate is ignored hence can't be used. Append all FilePlanComponent fields - * to the request. - */ - RequestSpecBuilder builder = getRMRestWrapper().configureRequestSpec(); - JsonNode root = new ObjectMapper().readTree(toJsonElectronicRecord(electronicRecordModel)); - // add request fields - Iterator fieldNames = root.fieldNames(); - while (fieldNames.hasNext()) - { - String fieldName = fieldNames.next(); - builder.addMultiPart(fieldName, root.get(fieldName).asText(), ContentType.JSON.name()); - } - builder.addMultiPart("filedata", recordContent, ContentType.BINARY.name()); - - // create node with given content - return createFilePlanComponent(electronicRecordModel, parentId); - } - - /** - * Updates a file plan component - * - * @param filePlanComponentModel The properties to be updated - * @param filePlanComponentId The id of the file plan component which will be updated - * @param returns The updated {@link FilePlanComponent} - * @throws Exception for the following cases: - *
    - *
  • the update request is invalid or {@code fileplanComponentId} is not a valid format or {@code filePlanComponentProperties} is invalid
  • - *
  • authentication fails
  • - *
  • current user does not have permission to update {@code fileplanComponentId}
  • - *
  • {@code fileplanComponentId} does not exist
  • - *
  • the updated name clashes with an existing node in the current parent folder
  • - *
  • model integrity exception, including node name with invalid characters
  • - *
- */ - public FilePlanComponent updateFilePlanComponent(FilePlanComponent filePlanComponentModel, String filePlanComponentId) throws Exception - { - mandatoryObject("filePlanComponentProperties", filePlanComponentModel); - mandatoryString("filePlanComponentId", filePlanComponentId); - - return updateFilePlanComponent(filePlanComponentModel, filePlanComponentId, EMPTY); - } - - /** - * Updates a file plan component - * - * @param filePlanComponent The properties to be updated - * @param parameters The URL parameters to add - * @param filePlanComponentId The id of the file plan component which will be updated - * @param returns The updated {@link FilePlanComponent} - * @throws Exception for the following cases: - *
    - *
  • the update request is invalid or {@code fileplanComponentId} is not a valid format or {@code filePlanComponentProperties} is invalid
  • - *
  • authentication fails
  • - *
  • current user does not have permission to update {@code fileplanComponentId}
  • - *
  • {@code fileplanComponentId} does not exist
  • - *
  • the updated name clashes with an existing node in the current parent folder
  • - *
  • model integrity exception, including node name with invalid characters
  • - *
- */ - public FilePlanComponent updateFilePlanComponent(FilePlanComponent filePlanComponent, String filePlanComponentId, String parameters) throws Exception - { - mandatoryObject("filePlanComponentProperties", filePlanComponent); - mandatoryString("filePlanComponentId", filePlanComponentId); - - return getRMRestWrapper().processModel(FilePlanComponent.class, requestWithBody( - PUT, - toJson(filePlanComponent), - "fileplan-components/{fileplanComponentId}?{parameters}", - filePlanComponentId, - parameters - )); - } - - /** - * Delete file plan component - * - * @param filePlanComponentId The id of the file plan component to be deleted - * @throws Exception for the following cases: - *
    - *
  • {@code fileplanComponentId} is not a valid format
  • - *
  • authentication fails
  • - *
  • current user does not have permission to delete {@code fileplanComponentId}
  • - *
  • {@code fileplanComponentId} does not exist
  • - *
  • {@code fileplanComponentId} is locked and cannot be deleted
  • - *
- */ - public void deleteFilePlanComponent(String filePlanComponentId) throws Exception - { - mandatoryString("filePlanComponentId", filePlanComponentId); - - getRMRestWrapper().processEmptyModel(simpleRequest( - DELETE, - "fileplan-components/{fileplanComponentId}", - filePlanComponentId - )); - } -} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RestIGCoreAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RestIGCoreAPI.java deleted file mode 100644 index cdc8910fea..0000000000 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/igCoreAPI/RestIGCoreAPI.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * #%L - * Alfresco Records Management Module - * %% - * Copyright (C) 2005 - 2017 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * - - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.rest.rm.community.requests.igCoreAPI; - -import static java.lang.Integer.parseInt; -import static java.lang.String.format; - -import com.jayway.restassured.RestAssured; - -import org.alfresco.rest.core.RMRestProperties; -import org.alfresco.rest.core.RMRestWrapper; -import org.alfresco.rest.rm.community.requests.RMModelRequest; - -/** - * Defines the entire IG Core API - * {@link http://host:port/ig-api-explorer} select "IG Core API" - * - * @author Tuna Aksoy - * @since 2.6 - */ -public class RestIGCoreAPI extends RMModelRequest -{ - /** - * Constructor - * - * @param rmRestWrapper RM REST Wrapper - * @param rmRestProperties RM REST Properties - */ - public RestIGCoreAPI(RMRestWrapper rmRestWrapper, RMRestProperties rmRestProperties) - { - super(rmRestWrapper); - RestAssured.baseURI = format("%s://%s", rmRestProperties.getScheme(), rmRestProperties.getServer()); - RestAssured.port = parseInt(rmRestProperties.getPort()); - RestAssured.basePath = rmRestProperties.getRestRmPath(); - restWrapper.configureRequestSpec().setBasePath(RestAssured.basePath); - } - - /** - * Provides DSL on all REST calls under ig-sites/rm/... API path - * - * @return {@link RMSiteAPI} - */ - public RMSiteAPI usingRMSite() - { - return new RMSiteAPI(getRMRestWrapper()); - } - - /** - * Provides DSL on all REST calls under fileplan-components/... API path - * - * @return {@link FilePlanComponentAPI} - */ - public FilePlanComponentAPI usingFilePlanComponents() - { - return new FilePlanComponentAPI(getRMRestWrapper()); - } - - /** - * Provides DSL on all REST calls under records/... API path - * - * @return {@link FilePlanComponentAPI} - */ - public RecordsAPI usingRecords() - { - return new RecordsAPI(getRMRestWrapper()); - } - - /** - * Provides DSL on all REST calls under files/... API path - * - * @return {@link FilesAPI} - */ - public FilesAPI usingFiles() - { - return new FilesAPI(getRMRestWrapper()); - } - - /** - * Provides DSL for RM user management API - * - * @return {@link RMUserAPI} - */ - public RMUserAPI usingRMUser() - { - return new RMUserAPI(getRMRestWrapper()); - } -} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/FilePlanComponentMixIn.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/FilePlanComponentMixIn.java index 71b1e25186..3761268de0 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/FilePlanComponentMixIn.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/FilePlanComponentMixIn.java @@ -28,10 +28,10 @@ package org.alfresco.rest.rm.community.util; import com.fasterxml.jackson.annotation.JsonUnwrapped; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties; +import org.alfresco.rest.rm.community.model.record.RecordProperties; /** - * Mix class for FilePlanComponent POJO class + * Mix class for Record POJO class * Mix-in annotations are: a way to associate annotations with classes * without modifying (target) classes themselves. * @@ -45,6 +45,5 @@ public abstract class FilePlanComponentMixIn * Its properties are instead included as properties of its containing Object */ @JsonUnwrapped - abstract FilePlanComponentProperties getProperties(); - + abstract RecordProperties getProperties(); } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/PojoUtility.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/PojoUtility.java index ba4f7960c7..39c7363191 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/PojoUtility.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/PojoUtility.java @@ -26,12 +26,12 @@ */ package org.alfresco.rest.rm.community.util; +import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; + import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; - /** * Utility class for creating the json object * @@ -41,51 +41,48 @@ import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent public class PojoUtility { /** - * Converting object to JSON string - * - * @param model The java object model to convert + * see {@link #toJson(Object, Class, Class)} */ public static String toJson(Object model) { - ObjectMapper mapper = new ObjectMapper(); - //include only values that differ from default settings to be included - mapper.setSerializationInclusion(Include.NON_DEFAULT); - try - { - //return the json object - return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(model); - } - catch (JsonProcessingException e) - { - return e.toString(); - } + mandatoryObject("model", model); + + return toJson(model, null, null); } - /** - * Converting object to JSON string for electronic records + * Converting object to JSON string * * @param model The java object model to convert - * @throws JsonProcessingException Throws exceptions if the given object doesn't match to the POJO class model + * @param target Class (or interface) whose annotations to effectively override + * @param mixinSource Class (or interface) whose annotations are to be "added" to target's annotations, overriding as necessary + * @return The converted java object as JSON string + * @throws JsonProcessingException Throws exceptions if the given object doesn't match to the POJO class model */ - public static String toJsonElectronicRecord(Object model) + public static String toJson(Object model, Class target, Class mixinSource) { + mandatoryObject("model", model); + ObjectMapper mapper = new ObjectMapper(); - //inject the "mix-in" annotations from FilePlanComponentMix to - // FilePlanComponent POJO class when converting to json - mapper.addMixIn(FilePlanComponent.class, FilePlanComponentMixIn.class); + if (target != null && mixinSource != null) + { + //inject the "mix-in" annotations from FilePlanComponentMix to + // FilePlanComponent POJO class when converting to json + mapper.addMixIn(target, mixinSource); + } //include only values that differ from default settings to be included mapper.setSerializationInclusion(Include.NON_DEFAULT); + + //return the json object try { - //return the json object return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(model); } - catch (JsonProcessingException e) + catch (JsonProcessingException error) { - return e.toString(); + return error.toString(); } } } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/ReviewPeriodSerializer.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/ReviewPeriodSerializer.java index f4065cb748..2cc2b62d07 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/ReviewPeriodSerializer.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/ReviewPeriodSerializer.java @@ -33,15 +33,15 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentReviewPeriod; +import org.alfresco.rest.rm.community.model.common.ReviewPeriod; /** - * Utility class for serializing the Review Period type + * Utility class for serializing @{FilePlanComponentReviewPeriod} * * @author Rodica Sutu * @since 2.6 */ -public class ReviewPeriodSerializer extends JsonSerializer +public class ReviewPeriodSerializer extends JsonSerializer { /** * @param value The Review Period value that is being serialized. @@ -51,7 +51,7 @@ public class ReviewPeriodSerializer extends JsonSerializer aspects = filePlanComponentsAPI.getFilePlanComponent(recordToClose.getId()).getAspectNames(); + RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); + List aspects = recordsAPI.getRecord(recordId).getAspectNames(); + // this operation is only valid for records assertTrue(aspects.contains(RECORD_TYPE)); - // a record mustn't be closed - assertFalse(aspects.contains(ASPECTS_CLOSED_RECORD)); + // a record mustn't be completed + assertFalse(aspects.contains(ASPECTS_COMPLETED_RECORD)); + // add completed record aspect + aspects.add(ASPECTS_COMPLETED_RECORD); - // add closed record aspect - aspects.add(ASPECTS_CLOSED_RECORD); - - FilePlanComponent updatedComponent = filePlanComponentsAPI.updateFilePlanComponent(FilePlanComponent.builder().aspectNames(aspects).build(), - recordToClose.getId()); + Record updateRecord = recordsAPI.updateRecord(Record.builder().aspectNames(aspects).build(), recordId); assertStatusCode(OK); - return updatedComponent; + + return updateRecord; } /** * Helper method to create a randomly-named / structure in file plan * * @param user The user under whose privileges this structure is going to be created - * @param parentId parent container id - * @return record folder + * @return {@link RecordCategoryChild} which represents the record folder * @throws Exception on failed creation */ - public FilePlanComponent createCategoryFolderInFilePlan(UserModel user) throws Exception + public RecordCategoryChild createCategoryFolderInFilePlan(UserModel user) throws Exception { // create root category - FilePlanComponent recordCategory = createCategory(user, FILE_PLAN_ALIAS, "Category " + getRandomAlphanumeric()); + RecordCategory recordCategory = createRootCategory(user, "Category " + getRandomAlphanumeric()); // and return a folder underneath return createFolder(user, recordCategory.getId(), "Folder " + getRandomAlphanumeric()); @@ -324,38 +424,41 @@ public class BaseRMRestTest extends RestTest /** * Helper method to create a randomly-named / structure in file plan as the admin user * - * @param parentId parent container id - * @return record folder + * @return {@link RecordCategoryChild} which represents the record folder * @throws Exception on failed creation */ - public FilePlanComponent createCategoryFolderInFilePlan() throws Exception + public RecordCategoryChild createCategoryFolderInFilePlan() throws Exception { return createCategoryFolderInFilePlan(getAdminUser()); } - /** - * Helper method to retrieve a file plan component with user's privilege - * - * @param user user under whose privileges a component is to be read - * @param componentId id of the component to read - * @return {@link FilePlanComponent} for given componentId - * @throws Exception if user doesn't have sufficient privileges - */ - public FilePlanComponent getFilePlanComponentAsUser(UserModel user, String componentId) throws Exception + public UnfiledContainer getUnfiledContainerAsUser(UserModel user, String componentId) throws Exception { - return getRestAPIFactory().getFilePlanComponentsAPI(user).getFilePlanComponent(componentId); + return getRestAPIFactory().getUnfiledContainersAPI(user).getUnfiledContainer(componentId); } - /** - * Helper method to retrieve a file plan component with user's privilege as the admin user - * - * @param componentId id of the component to read - * @return {@link FilePlanComponent} for given componentId - * @throws Exception if user doesn't have sufficient privileges - */ - public FilePlanComponent getFilePlanComponent(String componentId) throws Exception + public UnfiledContainer getUnfiledContainer(String componentId) throws Exception { - return getFilePlanComponentAsUser(getAdminUser(), componentId); + return getUnfiledContainerAsUser(getAdminUser(), componentId); } + public TransferContainer getTransferContainerAsUser(UserModel user, String componentId) throws Exception + { + return getRestAPIFactory().getTransferContainerAPI(user).getTransferContainer(componentId); + } + + public TransferContainer getTransferContainer(String componentId) throws Exception + { + return getTransferContainerAsUser(getAdminUser(), componentId); + } + + public FilePlan getFilePlanAsUser(UserModel user, String componentId) throws Exception + { + return getRestAPIFactory().getFilePlansAPI(user).getFilePlan(componentId); + } + + public FilePlan getFilePlan(String componentId) throws Exception + { + return getFilePlanAsUser(getAdminUser(), componentId); + } } diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/TestData.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/TestData.java index b2f08c7d2f..16c611a200 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/TestData.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/TestData.java @@ -27,14 +27,11 @@ package org.alfresco.rest.rm.community.base; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.HOLDS_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.FILE_PLAN_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.FOLDER_TYPE; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.HOLD_CONTAINER_TYPE; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.HOLD_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.TRANSFER_CONTAINER_TYPE; @@ -74,12 +71,11 @@ public interface TestData * @return file plan component alias */ @DataProvider - public static Object[][] getContainers() + public static String[][] getContainers() { - return new Object[][] { + return new String[][] { { FILE_PLAN_ALIAS }, { TRANSFERS_ALIAS }, - { HOLDS_ALIAS }, { UNFILED_RECORDS_CONTAINER_ALIAS }, }; } @@ -91,43 +87,42 @@ public interface TestData * @return file plan component alias */ @DataProvider - public static Object[][] getContainersAndTypes() + public static String[][] getContainersAndTypes() { - return new Object[][] { + return new String[][] { { FILE_PLAN_ALIAS, FILE_PLAN_TYPE }, { TRANSFERS_ALIAS, TRANSFER_CONTAINER_TYPE }, - { HOLDS_ALIAS, HOLD_CONTAINER_TYPE }, { UNFILED_RECORDS_CONTAINER_ALIAS, UNFILED_CONTAINER_TYPE }, }; } /** - * The default CATEGORY name used when creating categories + * The default record category name used when creating categories */ - public static String CATEGORY_NAME = "CATEGORY NAME" + getRandomAlphanumeric(); + public static String RECORD_CATEGORY_NAME = "CATEGORY NAME" + getRandomAlphanumeric(); /** - * The default CATEGORY title used when creating categories + * The default record category title used when creating categories */ - public static String CATEGORY_TITLE = "CATEGORY TITLE" + getRandomAlphanumeric(); + public static String RECORD_CATEGORY_TITLE = "CATEGORY TITLE" + getRandomAlphanumeric(); /** - * The default FOLDER name used when creating folders + * The default record folder name used when creating folders */ - public static String FOLDER_NAME = "FOLDER NAME" + getRandomAlphanumeric(); + public static String RECORD_FOLDER_NAME = "FOLDER NAME" + getRandomAlphanumeric(); /** - * The default FOLDER title used when creating folders + * The default record folder title used when creating folders */ - public static String FOLDER_TITLE = "FOLDER TITLE" + getRandomAlphanumeric(); + public static String RECORD_FOLDER_TITLE = "FOLDER TITLE" + getRandomAlphanumeric(); /** - * The default electronic record name used when creating electronic records + * The default electronic record name used when creating electronic records */ public static String ELECTRONIC_RECORD_NAME = "Record electronic" + getRandomAlphanumeric(); /** - * The default Non electronic record name used when creating non-electronic records + * The default non-electronic record name used when creating non-electronic records */ public static String NONELECTRONIC_RECORD_NAME = "Record nonelectronic" + getRandomAlphanumeric(); @@ -138,22 +133,18 @@ public interface TestData * @return file plan component alias */ @DataProvider - public static Object[][] childrenNotAllowedForCategory() + public static String[][] childrenNotAllowedForCategory() { - return new Object[][] { + return new String[][] { { FILE_PLAN_TYPE }, { TRANSFER_CONTAINER_TYPE }, - { HOLD_CONTAINER_TYPE }, { UNFILED_CONTAINER_TYPE }, { UNFILED_RECORD_FOLDER_TYPE }, - { HOLD_TYPE }, { TRANSFER_TYPE }, { CONTENT_TYPE } }; } - - /** * Data Provider with: * with the object types for creating a Record Folder @@ -161,9 +152,9 @@ public interface TestData * @return file plan component alias */ @DataProvider - public static Object[][] folderTypes() + public static String[][] folderTypes() { - return new Object[][] { + return new String[][] { { RECORD_FOLDER_TYPE }, { FOLDER_TYPE } }; @@ -176,9 +167,9 @@ public interface TestData * @return file plan component alias */ @DataProvider - public static Object[][] categoryTypes() + public static String[][] categoryTypes() { - return new Object[][] { + return new String[][] { { FOLDER_TYPE }, { RECORD_CATEGORY_TYPE } }; diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/DeleteRecordTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/DeleteRecordTests.java index 50811acfdd..4980112bac 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/DeleteRecordTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/DeleteRecordTests.java @@ -27,9 +27,19 @@ package org.alfresco.rest.rm.community.fileplancomponents; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.NON_ELECTRONIC_RECORD_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_CONTAINER_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; +import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING; +import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_POWER_USER; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.IMAGE_FILE; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicRecordModel; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createNonElectronicRecordModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicUnfiledContainerChildModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createNonElectronicUnfiledContainerChildModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.getFile; +import static org.alfresco.utility.constants.UserRole.SiteCollaborator; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.NOT_FOUND; @@ -37,22 +47,21 @@ import static org.springframework.http.HttpStatus.NO_CONTENT; import static org.springframework.http.HttpStatus.OK; import org.alfresco.rest.rm.community.base.BaseRMRestTest; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.user.UserPermissions; -import org.alfresco.rest.rm.community.model.user.UserRoles; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI; import org.alfresco.test.AlfrescoTest; -import org.alfresco.utility.constants.UserRole; +import org.alfresco.utility.data.RandomData; import org.alfresco.utility.model.SiteModel; import org.alfresco.utility.model.UserModel; import org.testng.annotations.Test; /** - * Create/File electronic records tests - *
- * These tests only test the creation and filing of electronic records, update at - * present isn't implemented in the API under test. - *

+ * Delete records tests + * * @author Kristijan Conkas * @since 2.6 */ @@ -60,15 +69,12 @@ public class DeleteRecordTests extends BaseRMRestTest { /** *

-     * Given a record
+     * Given an electronic record
      * And that I have the "Delete Record" capability
      * And write permissions
      * When I delete the record
      * Then it is deleted from the file plan
      * 
- * - * @param container - * @throws Exception */ @Test ( @@ -76,26 +82,43 @@ public class DeleteRecordTests extends BaseRMRestTest description = "Admin user can delete an electronic record" ) @AlfrescoTest(jira="RM-4363") - public void adminCanDeleteElectronicRecord(FilePlanComponent container) throws Exception + public void adminCanDeleteElectronicRecord(String folderId, String type) throws Exception { - FilePlanComponent newRecord = getRestAPIFactory().getFilePlanComponentsAPI().createElectronicRecord(createElectronicRecordModel(), IMAGE_FILE, container.getId()); + // Create record + String recordId; + if(RECORD_FOLDER_TYPE.equalsIgnoreCase(type)) + { + recordId = getRestAPIFactory().getRecordFolderAPI().createRecord(createElectronicRecordModel(), folderId, getFile(IMAGE_FILE)).getId(); + } + else if(UNFILED_CONTAINER_TYPE.equalsIgnoreCase(type)) + { + recordId = getRestAPIFactory().getUnfiledContainersAPI().uploadRecord(createElectronicUnfiledContainerChildModel(), folderId, getFile(IMAGE_FILE)).getId(); + } + else if(UNFILED_RECORD_FOLDER_TYPE.equalsIgnoreCase(type)) + { + recordId = getRestAPIFactory().getUnfiledRecordFoldersAPI().uploadRecord(createElectronicUnfiledContainerChildModel(), folderId, getFile(IMAGE_FILE)).getId(); + } + else + { + throw new Exception("Unsuported type = " + type); + } + + // Assert status assertStatusCode(CREATED); - deleteAndVerify(newRecord); + // Delete record and verify successful deletion + deleteAndVerify(recordId); } /** *
-     * Given a record
+     * Given a non-electronic record
      * And that I have the "Delete Record" capability
      * And write permissions
      * When I delete the record
      * Then it is deleted from the file plan
      * 
- * - * @param container - * @throws Exception */ @Test ( @@ -103,27 +126,43 @@ public class DeleteRecordTests extends BaseRMRestTest description = "Admin user can delete a non-electronic record" ) @AlfrescoTest(jira="RM-4363") - public void adminCanDeleteNonElectronicRecord(FilePlanComponent container) throws Exception + public void adminCanDeleteNonElectronicRecord(String folderId, String type) throws Exception { - // create a non-electronic record - FilePlanComponent newRecord = getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(createNonElectronicRecordModel(), container.getId()); + // Create record + String nonElectronicRecordId; + if(RECORD_FOLDER_TYPE.equalsIgnoreCase(type)) + { + nonElectronicRecordId = getRestAPIFactory().getRecordFolderAPI().createRecord(createNonElectronicRecordModel(), folderId).getId(); + } + else if(UNFILED_CONTAINER_TYPE.equalsIgnoreCase(type)) + { + nonElectronicRecordId = getRestAPIFactory().getUnfiledContainersAPI().createUnfiledContainerChild(createNonElectronicUnfiledContainerChildModel(), folderId).getId(); + } + else if(UNFILED_RECORD_FOLDER_TYPE.equalsIgnoreCase(type)) + { + nonElectronicRecordId = getRestAPIFactory().getUnfiledRecordFoldersAPI().createUnfiledRecordFolderChild(createNonElectronicUnfiledContainerChildModel(), folderId).getId(); + } + else + { + throw new Exception("Unsuported type = " + type); + } + + // Assert status assertStatusCode(CREATED); - deleteAndVerify(newRecord); + // Delete record and verify successful deletion + deleteAndVerify(nonElectronicRecordId); } /** *
-     * Given a record
+     * Given a non-electronic record
      * And that I don't have write permissions
      * When I try to delete the record
      * Then nothing happens
      * And error gets reported
      * 
- * - * @param container - * @throws Exception */ @Test ( @@ -132,23 +171,27 @@ public class DeleteRecordTests extends BaseRMRestTest @AlfrescoTest(jira="RM-4363") public void userWithoutWritePermissionsCantDeleteRecord() throws Exception { - // create a non-electronic record in unfiled records - FilePlanComponent newRecord = getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(createNonElectronicRecordModel(), UNFILED_RECORDS_CONTAINER_ALIAS); + // Create a non-electronic record in unfiled records + UnfiledContainerChild nonElectronicRecord = UnfiledContainerChild.builder() + .name("Record " + RandomData.getRandomAlphanumeric()) + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .build(); + UnfiledContainerChild newRecord = getRestAPIFactory().getUnfiledContainersAPI().createUnfiledContainerChild(nonElectronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS); assertStatusCode(CREATED); - // create test user and add it with collab. privileges + // Create test user and add it with collaboration privileges UserModel deleteUser = getDataUser().createRandomTestUser("delnoperm"); - deleteUser.setUserRole(UserRole.SiteCollaborator); - logger.info("test user: " + deleteUser.getUsername()); - getDataUser().addUserToSite(deleteUser, new SiteModel(getRestAPIFactory().getRMSiteAPI().getSite().getId()), UserRole.SiteCollaborator); + String username = deleteUser.getUsername(); + logger.info("Test user: " + username); + getDataUser().addUserToSite(deleteUser, new SiteModel(getRestAPIFactory().getRMSiteAPI().getSite().getId()), SiteCollaborator); - // add RM role to user - getRestAPIFactory().getRMUserAPI().assignRoleToUser(deleteUser.getUsername(), UserRoles.ROLE_RM_POWER_USER); + // Add RM role to user + getRestAPIFactory().getRMUserAPI().assignRoleToUser(username, ROLE_RM_POWER_USER); assertStatusCode(OK); - // try to delete newRecord - getRestAPIFactory().getFilePlanComponentsAPI(deleteUser).deleteFilePlanComponent(newRecord.getId()); + // Try to delete newRecord + getRestAPIFactory().getRecordsAPI(deleteUser).deleteRecord(newRecord.getId()); assertStatusCode(FORBIDDEN); } @@ -160,9 +203,6 @@ public class DeleteRecordTests extends BaseRMRestTest * Then nothing happens * And error gets reported * - * - * @param container - * @throws Exception */ @Test ( @@ -171,56 +211,55 @@ public class DeleteRecordTests extends BaseRMRestTest @AlfrescoTest(jira="RM-4363") public void userWithoutDeleteRecordsCapabilityCantDeleteRecord() throws Exception { - // create test user and add it with collab. privileges + // Create test user and add it with collaboration privileges UserModel deleteUser = getDataUser().createRandomTestUser("delnoperm"); - deleteUser.setUserRole(UserRole.SiteCollaborator); - getDataUser().addUserToSite(deleteUser, new SiteModel(getRestAPIFactory().getRMSiteAPI().getSite().getId()), UserRole.SiteCollaborator); - logger.info("test user: " + deleteUser.getUsername()); + getDataUser().addUserToSite(deleteUser, new SiteModel(getRestAPIFactory().getRMSiteAPI().getSite().getId()), SiteCollaborator); + String username = deleteUser.getUsername(); + logger.info("Test user: " + username); - // add RM role to user, RM Power User doesn't have the Delete Record capabilities - getRestAPIFactory().getRMUserAPI().assignRoleToUser(deleteUser.getUsername(), UserRoles.ROLE_RM_POWER_USER); + // Add RM role to user, RM Power User doesn't have the "Delete Record" capabilities + getRestAPIFactory().getRMUserAPI().assignRoleToUser(username, ROLE_RM_POWER_USER); assertStatusCode(OK); - // create random folder - FilePlanComponent randomFolder = createCategoryFolderInFilePlan(); - logger.info("random folder:" + randomFolder.getName()); + // Create random folder + RecordCategoryChild recordFolder = createCategoryFolderInFilePlan(); + logger.info("Random folder:" + recordFolder.getName()); - // grant deleteUser Filing privileges on randomFolder category, this will be - // inherited to randomFolder - FilePlanComponentAPI filePlanComponentsAPIAsAdmin = getRestAPIFactory().getFilePlanComponentsAPI(); - getRestAPIFactory().getRMUserAPI().addUserPermission(filePlanComponentsAPIAsAdmin.getFilePlanComponent(randomFolder.getParentId()), - deleteUser, UserPermissions.PERMISSION_FILING); + // Grant "deleteUser" filing permissions on "randomFolder" parent, this will be inherited to randomFolder + RecordCategoryAPI recordCategoryAPI = getRestAPIFactory().getRecordCategoryAPI(); + getRestAPIFactory().getRMUserAPI().addUserPermission(recordCategoryAPI.getRecordCategory(recordFolder.getParentId()).getId(), deleteUser, PERMISSION_FILING); assertStatusCode(OK); - // create a non-electronic record in randomFolder - FilePlanComponent newRecord = filePlanComponentsAPIAsAdmin.createFilePlanComponent(createNonElectronicRecordModel(), randomFolder.getId()); + // Create a non-electronic record in "randomFolder" + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + Record newRecord = recordFolderAPI.createRecord(createNonElectronicRecordModel(), recordFolder.getId()); assertStatusCode(CREATED); - // verify the user can see the newRecord - FilePlanComponentAPI filePlanComponentsAPIAsUser = getRestAPIFactory().getFilePlanComponentsAPI(deleteUser); - filePlanComponentsAPIAsUser.getFilePlanComponent(newRecord.getId()); + // Verify the user can see "newRecord" + RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(deleteUser); + recordsAPI.getRecord(newRecord.getId()); assertStatusCode(OK); - // try to delete newRecord - filePlanComponentsAPIAsUser.deleteFilePlanComponent(newRecord.getId()); + // Try to delete "newRecord" + recordsAPI.deleteRecord(newRecord.getId()); assertStatusCode(FORBIDDEN); } /** * Utility method to delete a record and verify successful deletion - * @param record - * @throws Exception + * + * @param recordId The id of the record */ - private void deleteAndVerify(FilePlanComponent record) throws Exception + private void deleteAndVerify(String recordId) { - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); + RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); - // delete it and verify status - filePlanComponentsAPI.deleteFilePlanComponent(record.getId()); + // Delete record and verify status + recordsAPI.deleteRecord(recordId); assertStatusCode(NO_CONTENT); - // try to get deleted file plan component - filePlanComponentsAPI.getFilePlanComponent(record.getId()); + // Try to get deleted record + recordsAPI.deleteRecord(recordId); assertStatusCode(NOT_FOUND); } } diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/ElectronicRecordTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/ElectronicRecordTests.java index 9cae3f6215..b4f0dacb94 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/ElectronicRecordTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/ElectronicRecordTests.java @@ -26,18 +26,20 @@ */ package org.alfresco.rest.rm.community.fileplancomponents; -import static org.alfresco.rest.rm.community.base.TestData.CATEGORY_NAME; import static org.alfresco.rest.rm.community.base.TestData.ELECTRONIC_RECORD_NAME; -import static org.alfresco.rest.rm.community.base.TestData.FOLDER_NAME; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.HOLDS_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_CONTAINER_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.IMAGE_FILE; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicRecordModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicUnfiledContainerChildModel; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createTempFile; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.getFile; import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.CREATED; @@ -46,40 +48,40 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import org.alfresco.rest.rm.community.base.BaseRMRestTest; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentContent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.record.RecordContent; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledRecordFolderAPI; import org.alfresco.utility.report.Bug; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** * Create/File electronic records tests - *
- * These tests only test the creation and filing of electronic records, update at - * present isn't implemented in the API under test. - *

+ * * @author Kristijan Conkas * @since 2.6 */ public class ElectronicRecordTests extends BaseRMRestTest { - /** Valid root containers where electronic records can be created */ + private static final String TEXT_PLAIN_VALUE = "text/plain"; + + /** Invalid parent containers where electronic records can't be created */ @DataProvider(name = "invalidParentContainers") - public Object[][] invalidContainers() throws Exception + public String[][] invalidParentContainers() throws Exception { - return new Object[][] + return new String[][] { // record category - { getFilePlanComponent(createCategoryFolderInFilePlan().getParentId()) }, + { createCategoryFolderInFilePlan().getParentId() }, // file plan root - { getFilePlanComponent(FILE_PLAN_ALIAS) }, + { FILE_PLAN_ALIAS }, // transfers - { getFilePlanComponent(TRANSFERS_ALIAS) }, - // holds - { getFilePlanComponent(HOLDS_ALIAS) }, + { TRANSFERS_ALIAS } }; } @@ -90,21 +92,21 @@ public class ElectronicRecordTests extends BaseRMRestTest * Then nothing happens * And an error is reported * - * @param container - * @throws Exception + * @param container The parent container + * @throws Exception if record can't be created */ @Test ( dataProvider = "invalidParentContainers", description = "Electronic records can't be created in invalid parent containers" ) - public void cantCreateElectronicRecordsInInvalidContainers(FilePlanComponent container) throws Exception + public void cantCreateElectronicRecordsInInvalidContainers(String container) throws Exception { - // Build object the filePlan, this should throw an IllegalArgumentException - getRestAPIFactory().getFilePlanComponentsAPI().createElectronicRecord(createElectronicRecordModel(), IMAGE_FILE, container.getId()); + // Create an electronic record in the given container, this should throw an IllegalArgumentException + getRestAPIFactory().getRecordFolderAPI().createRecord(createElectronicRecordModel(), container, getFile(IMAGE_FILE)); - // verify the create request status code - assertStatusCode(UNPROCESSABLE_ENTITY); + // Verify the create request status code + assertStatusCode(BAD_REQUEST); } /** @@ -115,7 +117,7 @@ public class ElectronicRecordTests extends BaseRMRestTest * Then nothing happens * And an error is reported * - * @throws Exception + * @throws Exception if record can't be created */ @Test ( @@ -123,18 +125,18 @@ public class ElectronicRecordTests extends BaseRMRestTest ) public void cantCreateElectronicRecordInClosedFolder() throws Exception { - FilePlanComponent recordFolder = createCategoryFolderInFilePlan(); + RecordCategoryChild recordFolder = createCategoryFolderInFilePlan(); - // the folder should be open + // The folder should be open assertFalse(recordFolder.getProperties().getIsClosed()); - // close the folder + // Close the folder closeFolder(recordFolder.getId()); - // try to create it, this should throw IllegalArgumentException - getRestAPIFactory().getFilePlanComponentsAPI().createElectronicRecord(createElectronicRecordModel(), IMAGE_FILE, recordFolder.getId()); + // Try to create an electronic record, this should throw IllegalArgumentException + getRestAPIFactory().getRecordFolderAPI().createRecord(createElectronicRecordModel(), recordFolder.getId(), getFile(IMAGE_FILE)); - // verify the status code + // Verify the status code assertStatusCode(UNPROCESSABLE_ENTITY); } @@ -149,39 +151,57 @@ public class ElectronicRecordTests extends BaseRMRestTest * * and *

+     *
+     *
      * Given a parent container that is an unfiled record folder or the root unfiled record container
      * When I try to create an electronic record within the parent container
      * And I do not provide all the required mandatory property values
      * Then nothing happens
      * And an error is reported
      * 
- * @param container - * @throws Exception + * @param folderid The folder, which the record will be created in + * @param type The type of the record folder, which the record will be created in + * @throws Exception if record can't be created */ @Test ( dataProvider = "validRootContainers", description = "Electronic record can only be created if all mandatory properties are given" ) - public void canCreateElectronicRecordOnlyWithMandatoryProperties(FilePlanComponent container) throws Exception + public void canCreateElectronicRecordOnlyWithMandatoryProperties(String folderId, String type) throws Exception { - logger.info("Root container:\n" + toJson(container)); + logger.info("Root container:\n" + toJson(folderId)); - if (container.getNodeType().equals(RECORD_FOLDER_TYPE)) + if (RECORD_FOLDER_TYPE.equalsIgnoreCase(type)) { - // only record folders can be open or closed - assertFalse(container.getProperties().getIsClosed()); + // Only record folders can be opened or closed + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + assertFalse(recordFolderAPI.getRecordFolder(folderId).getProperties().getIsClosed()); + + // Record without name + Record recordModel = Record.builder().nodeType(CONTENT_TYPE).build(); + + // Try to create it + recordFolderAPI.createRecord(recordModel, folderId); + } + else if(UNFILED_CONTAINER_TYPE.equalsIgnoreCase(type)) + { + UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI(); + UnfiledContainerChild recordModel = UnfiledContainerChild.builder().nodeType(CONTENT_TYPE).build(); + unfiledContainersAPI.createUnfiledContainerChild(recordModel, folderId); + } + else if(UNFILED_RECORD_FOLDER_TYPE.equalsIgnoreCase(type)) + { + UnfiledRecordFolderAPI unfiledRecordFoldersAPI = getRestAPIFactory().getUnfiledRecordFoldersAPI(); + UnfiledContainerChild recordModel = UnfiledContainerChild.builder().nodeType(CONTENT_TYPE).build(); + unfiledRecordFoldersAPI.createUnfiledRecordFolderChild(recordModel, folderId); + } + else + { + throw new Exception("Unsuported type = " + type); } - // component without name - FilePlanComponent record = FilePlanComponent.builder() - .nodeType(CONTENT_TYPE) - .build(); - - // try to create it - getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(record, container.getId()); - - // verify the status code is BAD_REQUEST + // Verify the status code is BAD_REQUEST assertStatusCode(BAD_REQUEST); } @@ -195,151 +215,185 @@ public class ElectronicRecordTests extends BaseRMRestTest * * and *
+     *
      * Given a parent container that is an unfiled record folder or the root unfiled record container
      * When I try to create an electronic record within the parent container
      * Then the electronic record is created
      * And the details of the new record are returned
      * 
- * @throws Exception + * @param folderId The folder, which the record will be created in + * @param type The type of the folder, which the record will be created in + * @throws Exception if record can't be created */ @Test ( dataProvider = "validRootContainers", - description = "Electronic records can be created in unfiled record folder or unfiled record root" + description = "Electronic records can be created in record folders, unfiled record folders or unfiled record folder root" ) - public void canCreateElectronicRecordsInValidContainers(FilePlanComponent container) throws Exception + public void canCreateElectronicRecordsInValidContainers(String folderId, String type) throws Exception { - FilePlanComponent record = createElectronicRecordModel(); - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - String newRecordId = filePlanComponentsAPI.createElectronicRecord(record, IMAGE_FILE, container.getId()).getId(); - - // verify the create request status code + String newRecordId; + String expectedName; + if (RECORD_FOLDER_TYPE.equalsIgnoreCase(type)) + { + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + Record recordModel = createElectronicRecordModel(); + newRecordId = recordFolderAPI.createRecord(recordModel, folderId, getFile(IMAGE_FILE)).getId(); + expectedName = recordModel.getName(); + } + else if(UNFILED_CONTAINER_TYPE.equalsIgnoreCase(type)) + { + UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI(); + UnfiledContainerChild recordModel = createElectronicUnfiledContainerChildModel(); + newRecordId = unfiledContainersAPI.uploadRecord(recordModel, folderId, getFile(IMAGE_FILE)).getId(); + expectedName = recordModel.getName(); + } + else if(UNFILED_RECORD_FOLDER_TYPE.equalsIgnoreCase(type)) + { + UnfiledRecordFolderAPI unfiledRecordFoldersAPI = getRestAPIFactory().getUnfiledRecordFoldersAPI(); + UnfiledContainerChild recordModel = createElectronicUnfiledContainerChildModel(); + newRecordId = unfiledRecordFoldersAPI.uploadRecord(recordModel, folderId, getFile(IMAGE_FILE)).getId(); + expectedName = recordModel.getName(); + } + else + { + throw new Exception("Unsuported type = " + type); + } + // Verify the create request status code assertStatusCode(CREATED); - // get newly created electronic record and verify its properties - FilePlanComponent electronicRecord = filePlanComponentsAPI.getFilePlanComponent(newRecordId); - // created record will have record identifier inserted in its name but will be prefixed with - // the name it was created as - assertTrue(electronicRecord.getName().startsWith(record.getName())); - assertTrue(electronicRecord.getName().contains(electronicRecord.getProperties().getRmIdentifier())); + // Get newly created electronic record and verify its properties + RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); + Record record = recordsAPI.getRecord(newRecordId); + String recordName = record.getName(); + + // Created record will have record identifier inserted in its name but will be prefixed with the name it was created as + assertTrue(recordName.startsWith(expectedName)); + assertTrue(recordName.contains(record.getProperties().getIdentifier())); } /** - * This test verified that in the test client implementation if record name isn't specified it - * defaults to filed file name. - * @param container valid record container - * @throws Exception if record creation failed + *
+     * Given that a record name isn't specified
+     * When I create an electronic record
+     * Then the record name defaults to filed file name.
+     * 
+ * @param folderId The folder, which the record will be created in + * @param type The type of the folder, which the record will be created in + * @throws Exception if record can't be created */ @Test ( dataProvider = "validRootContainers", description = "Electronic records can be created in unfiled record folder or unfiled record root" ) - public void recordNameDerivedFromFileName(FilePlanComponent container) throws Exception + public void recordNameDerivedFromFileName(String folderId, String type) throws Exception { - // record object without name set - FilePlanComponent record = FilePlanComponent.builder() - .nodeType(CONTENT_TYPE) - .build(); + String newRecordId; + if (RECORD_FOLDER_TYPE.equalsIgnoreCase(type)) + { + // Create a record model without a name + Record recordModel = Record.builder().nodeType(CONTENT_TYPE).build(); - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - String newRecordId = filePlanComponentsAPI.createElectronicRecord(record, IMAGE_FILE, container.getId()).getId(); - - // verify the create request status code + // Create an electronic record + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + newRecordId = recordFolderAPI.createRecord(recordModel, folderId, getFile(IMAGE_FILE)).getId(); + } + else if(UNFILED_CONTAINER_TYPE.equalsIgnoreCase(type)) + { + UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI(); + UnfiledContainerChild recordModel = UnfiledContainerChild.builder().nodeType(CONTENT_TYPE).build(); + newRecordId = unfiledContainersAPI.uploadRecord(recordModel, folderId, getFile(IMAGE_FILE)).getId(); + } + else if(UNFILED_RECORD_FOLDER_TYPE.equalsIgnoreCase(type)) + { + UnfiledRecordFolderAPI unfiledRecordFoldersAPI = getRestAPIFactory().getUnfiledRecordFoldersAPI(); + UnfiledContainerChild recordModel = UnfiledContainerChild.builder().nodeType(CONTENT_TYPE).build(); + newRecordId = unfiledRecordFoldersAPI.uploadRecord(recordModel, folderId, getFile(IMAGE_FILE)).getId(); + } + else + { + throw new Exception("Unsuported type = " + type); + } + // Verify the create request status code assertStatusCode(CREATED); - // get newly created electonic record and verify its properties - FilePlanComponent electronicRecord = filePlanComponentsAPI.getFilePlanComponent(newRecordId); - // record will have record identifier inserted in its name but will for sure start with file name - // and end with its extension + // Get newly created electronic record and verify its properties + Record electronicRecord = getRestAPIFactory().getRecordsAPI().getRecord(newRecordId); + + // Record will have record identifier inserted in its name but will for sure start with file name and end with its extension assertTrue(electronicRecord.getName().startsWith(IMAGE_FILE.substring(0, IMAGE_FILE.indexOf(".")))); - assertTrue(electronicRecord.getName().contains(electronicRecord.getProperties().getRmIdentifier())); + assertTrue(electronicRecord.getName().contains(electronicRecord.getProperties().getIdentifier())); } @Test @Bug (id = "RM-4568") /** - * Given I want to create an electronic record - * When I use the path relative to the filePlanComponentid + *
+     * Given that I want to create an electronic record in one unfiled record folder
+     * When I use the path relative to the one unfiled record folder
      * Then the containers in the relativePath that don't exist are created before creating the electronic record
+     * 
      */
     public void createElectronicRecordWithRelativePath() throws Exception
     {
-        //the containers specified on the RELATIVE_PATH parameter don't exist on server
-        String RELATIVE_PATH = CATEGORY_NAME + "/" + CATEGORY_NAME + "/" + FOLDER_NAME;
-        FilePlanComponent electronicRecord = FilePlanComponent.builder()
-                                                              .name(ELECTRONIC_RECORD_NAME)
-                                                              .nodeType(CONTENT_TYPE.toString())
-                                                              .content(FilePlanComponentContent
-                                                                      .builder()
-                                                                      .mimeType("text/plain")
-                                                                      .build()
-                                                                      )
-                                                              .properties(FilePlanComponentProperties
-                                                                        .builder()
-                                                                        .description(ELECTRONIC_RECORD_NAME)
-                                                                        .build()
-                                                                          )
-                                                              .relativePath(RELATIVE_PATH)
-                                                              .build();
+        // The containers specified on the relativePath parameter don't exist on server
+        String parentUbnfiledRecordFolderName = "ParentUnfiledRecordFolder" + getRandomAlphanumeric();
+        String unfiledRecordFolderPathEl1 = "UnfiledRecordFolderPathEl1" + getRandomAlphanumeric();
+        String unfiledRecordFolderPathEl2 = "UnfiledRecordFolderPathEl2" + getRandomAlphanumeric();
+        String unfiledRecordFolderPathEl3 = "UnfiledRecordFolderPathEl3" + getRandomAlphanumeric();
 
-        FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI();
-        FilePlanComponent recordCreated = filePlanComponentsAPI.createElectronicRecord(electronicRecord, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME), FILE_PLAN_ALIAS);
+        String parentUnfiledRecordFolderId = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, parentUbnfiledRecordFolderName, UNFILED_RECORD_FOLDER_TYPE).getId();
+
+        String relativePath = unfiledRecordFolderPathEl1 + "/" + unfiledRecordFolderPathEl2 + "/" + unfiledRecordFolderPathEl3;
+        UnfiledContainerChild unfiledContainerChildModel= UnfiledContainerChild.builder()
+                                                                                .name(ELECTRONIC_RECORD_NAME)
+                                                                                .nodeType(CONTENT_TYPE)
+                                                                                .content(RecordContent.builder()
+                                                                                        .mimeType(TEXT_PLAIN_VALUE)
+                                                                                        .build())
+                                                                                .relativePath(relativePath)
+                                                                                .build();
+
+
+
+        UnfiledRecordFolderAPI unfiledRecordFoldersAPI = getRestAPIFactory().getUnfiledRecordFoldersAPI();
+        UnfiledContainerChild recordCreated = unfiledRecordFoldersAPI.uploadRecord(unfiledContainerChildModel, parentUnfiledRecordFolderId, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME));
+
+        // Verify the create request status code
+        assertStatusCode(CREATED);
+
+        // Get newly created electronic record and verify its properties
+        RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
+        Record record = recordsAPI.getRecord(recordCreated.getId());
+
+        assertTrue(record.getName().startsWith(ELECTRONIC_RECORD_NAME));
+        assertTrue(unfiledRecordFoldersAPI.getUnfiledRecordFolder(record.getParentId()).getName().equals(unfiledRecordFolderPathEl3));
+
+        // The first relative path element exists and the second one does not exist
+        String unfiledRecordFolderPathEl4 = "UnfiledRecordFolderPathEl4" + getRandomAlphanumeric();
+        relativePath = unfiledRecordFolderPathEl1 + "/" + unfiledRecordFolderPathEl4;
+        unfiledContainerChildModel.setRelativePath(relativePath);
+        recordCreated = unfiledRecordFoldersAPI.uploadRecord(unfiledContainerChildModel, parentUnfiledRecordFolderId, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME));
         // verify the create request status code
         assertStatusCode(CREATED);
 
         // get newly created electronic record and verify its properties
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(recordCreated.getId())
-                                        .getName().startsWith(ELECTRONIC_RECORD_NAME));
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(recordCreated.getId())
-                                        .getProperties().getDescription().equals(ELECTRONIC_RECORD_NAME));
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(recordCreated.getParentId())
-                                        .getName().equals(FOLDER_NAME));
-        //get newly created electronic record using the relativePath
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(FILE_PLAN_ALIAS, FilePlanComponentFields.RELATIVE_PATH + "=" + RELATIVE_PATH + "/" + recordCreated.getName())
-                                        .getId().equals(recordCreated.getId()));
+        record = recordsAPI.getRecord(recordCreated.getId());
 
-        //the category specified via the RELATIVE_PATH exist, folder doesn't exist
-        RELATIVE_PATH = CATEGORY_NAME + "/" + FOLDER_NAME;
-        electronicRecord.setRelativePath(RELATIVE_PATH);
-        recordCreated = filePlanComponentsAPI.createElectronicRecord(electronicRecord, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME), FILE_PLAN_ALIAS);
-        // verify the create request status code
-        assertStatusCode(CREATED);
-        // get newly created electronic record and verify its properties
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(recordCreated.getId())
-                                        .getName().startsWith(ELECTRONIC_RECORD_NAME));
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(recordCreated.getParentId())
-                                        .getName().startsWith(FOLDER_NAME));
-        //get newly created electronic record using the relativePath
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(FILE_PLAN_ALIAS, FilePlanComponentFields.RELATIVE_PATH + "=" + RELATIVE_PATH + "/" + recordCreated.getName())
-                                        .getId().equals(recordCreated.getId()));
+        assertTrue(record.getName().startsWith(ELECTRONIC_RECORD_NAME));
+        assertTrue(unfiledRecordFoldersAPI.getUnfiledRecordFolder(record.getParentId()).getName().equals(unfiledRecordFolderPathEl4));
 
         //the containers from the RELATIVE PATH exists
-        electronicRecord.setName(ELECTRONIC_RECORD_NAME + getRandomAlphanumeric());
-        recordCreated = filePlanComponentsAPI.createElectronicRecord(electronicRecord, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME), FILE_PLAN_ALIAS);
+        unfiledContainerChildModel.setName(ELECTRONIC_RECORD_NAME + getRandomAlphanumeric());
+        recordCreated = unfiledRecordFoldersAPI.uploadRecord(unfiledContainerChildModel, parentUnfiledRecordFolderId, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME));
         // verify the create request status code
         assertStatusCode(CREATED);
         // get newly created electronic record and verify its properties
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(recordCreated.getId())
-                                        .getName().startsWith(ELECTRONIC_RECORD_NAME));
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(recordCreated.getParentId())
-                                        .getName().startsWith(FOLDER_NAME));
-        //get newly created electronic record using the relativePath
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(FILE_PLAN_ALIAS, FilePlanComponentFields.RELATIVE_PATH + "=" + RELATIVE_PATH + "/" + recordCreated.getName())
-                                        .getId().equals(recordCreated.getId()));
+        record = recordsAPI.getRecord(recordCreated.getId());
 
-        //create the container structure relative to the  categoryId
-        String categoryId = filePlanComponentsAPI.getFilePlanComponent(FILE_PLAN_ALIAS, FilePlanComponentFields.RELATIVE_PATH + "=" + CATEGORY_NAME)
-                                                 .getId();
-        RELATIVE_PATH = CATEGORY_NAME + CATEGORY_NAME + "/" + FOLDER_NAME;
-        electronicRecord.setRelativePath(RELATIVE_PATH);
-        recordCreated = filePlanComponentsAPI.createElectronicRecord(electronicRecord, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME), categoryId);
-        // verify the create request status code
-        assertStatusCode(CREATED);
-        // get newly created electronic record and verify its properties
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(recordCreated.getId())
-                                        .getName().startsWith(ELECTRONIC_RECORD_NAME));
-        assertTrue(filePlanComponentsAPI.getFilePlanComponent(recordCreated.getParentId())
-                                        .getName().startsWith(FOLDER_NAME));
+        assertTrue(record.getName().startsWith(ELECTRONIC_RECORD_NAME));
+
+        assertTrue(unfiledRecordFoldersAPI.getUnfiledRecordFolder(record.getParentId()).getName().equals(unfiledRecordFolderPathEl4));
     }
 }
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/FilePlanTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/FilePlanTests.java
index f1aee9ce2e..950b116341 100644
--- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/FilePlanTests.java
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/FilePlanTests.java
@@ -27,35 +27,30 @@
 package org.alfresco.rest.rm.community.fileplancomponents;
 
 import static java.util.Arrays.asList;
-
 import static org.alfresco.rest.rm.community.base.AllowableOperations.CREATE;
 import static org.alfresco.rest.rm.community.base.AllowableOperations.DELETE;
 import static org.alfresco.rest.rm.community.base.AllowableOperations.UPDATE;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.ALLOWABLE_OPERATIONS;
-import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
 import static org.springframework.http.HttpStatus.FORBIDDEN;
 import static org.springframework.http.HttpStatus.NOT_FOUND;
 import static org.springframework.http.HttpStatus.OK;
-import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
 import org.alfresco.rest.rm.community.base.BaseRMRestTest;
 import org.alfresco.rest.rm.community.base.TestData;
-import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent;
-import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties;
-import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI;
-import org.alfresco.rest.rm.community.requests.igCoreAPI.RMSiteAPI;
+import org.alfresco.rest.rm.community.model.fileplan.FilePlan;
+import org.alfresco.rest.rm.community.model.transfercontainer.TransferContainer;
+import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainer;
+import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI;
 import org.alfresco.utility.model.UserModel;
-import org.alfresco.utility.report.Bug;
 import org.testng.annotations.Test;
 
 /**
- * This class contains the tests for
- * the File Plan CRUD API
+ * This class contains the tests for the File Plan CRUD API
  *
  * @author Rodica Sutu
  * @since 2.6
@@ -63,17 +58,19 @@ import org.testng.annotations.Test;
 public class FilePlanTests extends BaseRMRestTest
 {
     /**
+     * 
      * Given that the RM site doesn't exist
      * When I use the API to get the File Plan/Holds/Unfiled Records Container/Transfers
      * Then I get the 404 response code
+     * 
*/ @Test ( - description = "Check the GET response code when the RM site doesn't exist", + description = "Check the GET response for the special contianers when the RM site doesn't exist", dataProviderClass = TestData.class, dataProvider = "getContainers" ) - public void getFilePlanComponentWhenRMIsNotCreated(String filePlanComponentAlias) throws Exception + public void getContainersWhenRMIsNotCreated(String containerAlias) throws Exception { RMSiteAPI rmSiteAPI = getRestAPIFactory().getRMSiteAPI(); @@ -84,217 +81,156 @@ public class FilePlanTests extends BaseRMRestTest rmSiteAPI.deleteRMSite(); } - // Get the file plan component - getRestAPIFactory().getFilePlanComponentsAPI().getFilePlanComponent(filePlanComponentAlias); + if (FILE_PLAN_ALIAS.equalsIgnoreCase(containerAlias)) + { + getRestAPIFactory().getFilePlansAPI().getFilePlan(containerAlias); + } + else if(TRANSFERS_ALIAS.equalsIgnoreCase(containerAlias)) + { + getRestAPIFactory().getTransferContainerAPI().getTransferContainer(containerAlias); + } + else + { + getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(containerAlias); + } - //check the response code is NOT_FOUND + // Check the response code is NOT_FOUND assertStatusCode(NOT_FOUND); } /** + *
      * Given that a file plan exists
      * When I ask the API for the details of the file plan
      * Then I am given the details of the file plan
+     * 
*/ @Test ( - description = "Check the GET response for special file plan components when the RM site exit", + description = "Check the GET response for the special containers when the RM site exit", dataProviderClass = TestData.class, dataProvider = "getContainersAndTypes" ) - public void getFilePlanComponentWhenRMIsCreated(String filePlanComponentAlias, String filePlanComponentType) throws Exception + public void getContainersWhenRMIsCreated(String containerAlias, String containerType) throws Exception { // Create RM Site if doesn't exist createRMSiteIfNotExists(); // Get the file plan special container - FilePlanComponent filePlanComponent = getRestAPIFactory().getFilePlanComponentsAPI().getFilePlanComponent(filePlanComponentAlias); + FilePlan filePlan = null; + TransferContainer transferContainer = null; + UnfiledContainer unfiledContainer = null; + + if (FILE_PLAN_ALIAS.equalsIgnoreCase(containerAlias)) + { + filePlan = getRestAPIFactory().getFilePlansAPI().getFilePlan(containerAlias); + } + else if(TRANSFERS_ALIAS.equalsIgnoreCase(containerAlias)) + { + transferContainer = getRestAPIFactory().getTransferContainerAPI().getTransferContainer(containerAlias); + } + else + { + unfiledContainer = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(containerAlias); + } // Check the response code assertStatusCode(OK); // Check the response contains the right node type - assertEquals(filePlanComponent.getNodeType(), filePlanComponentType); + if (FILE_PLAN_ALIAS.equalsIgnoreCase(containerAlias)) + { + assertEquals(filePlan.getNodeType(), containerType); + } + else if(TRANSFERS_ALIAS.equalsIgnoreCase(containerAlias)) + { + assertEquals(transferContainer.getNodeType(), containerType); + } + else + { + assertEquals(unfiledContainer.getNodeType(), containerType); + } } /** + *
      * Given that a file plan exists
      * When I ask the API for the details of the file plan to include the allowableOperations property
      * Then I am given the allowableOperations property with the update and create operations.
+     * 
*/ @Test ( - description = "Check the allowableOperations list returned ", + description = "Check the allowableOperations list returned", dataProviderClass = TestData.class, dataProvider = "getContainers" ) - public void includeAllowableOperations(String specialContainerAlias) throws Exception + public void includeAllowableOperations(String containerAlias) throws Exception { // Create RM Site if doesn't exist createRMSiteIfNotExists(); // Get the file plan special containers with the optional parameter allowableOperations - FilePlanComponent filePlanComponent = getRestAPIFactory().getFilePlanComponentsAPI().getFilePlanComponent(specialContainerAlias, "include=" + ALLOWABLE_OPERATIONS); + FilePlan filePlan = null; + TransferContainer transferContainer = null; + UnfiledContainer unfiledContainer = null; - // Check the list of allowableOperations returned - if(specialContainerAlias.equals(TRANSFERS_ALIAS)) + if (FILE_PLAN_ALIAS.equalsIgnoreCase(containerAlias)) { - assertTrue(filePlanComponent.getAllowableOperations().containsAll(asList(UPDATE)), - "Wrong list of the allowable operations is return" + filePlanComponent.getAllowableOperations().toString()); + // Check the list of allowableOperations returned + filePlan = getRestAPIFactory().getFilePlansAPI().getFilePlan(containerAlias, "include=" + ALLOWABLE_OPERATIONS); + + assertTrue(filePlan.getAllowableOperations().containsAll(asList(UPDATE, CREATE)), + "Wrong list of the allowable operations is return" + filePlan.getAllowableOperations().toString()); + + // Check the list of allowableOperations doesn't contain DELETE operation + assertFalse(filePlan.getAllowableOperations().contains(DELETE), + "The list of allowable operations contains delete option" + filePlan.getAllowableOperations().toString()); + } + else if (TRANSFERS_ALIAS.equalsIgnoreCase(containerAlias)) + { + // Check the list of allowableOperations returned + transferContainer = getRestAPIFactory().getTransferContainerAPI().getTransferContainer(containerAlias, "include=" + ALLOWABLE_OPERATIONS); + + assertTrue(transferContainer.getAllowableOperations().containsAll(asList(UPDATE)), + "Wrong list of the allowable operations is return" + transferContainer.getAllowableOperations().toString()); + + // Check the list of allowableOperations doesn't contain DELETE operation + assertFalse(transferContainer.getAllowableOperations().contains(DELETE), + "The list of allowable operations contains delete option" + transferContainer.getAllowableOperations().toString()); + + // Check the list of allowableOperations doesn't contain DELETE operation + assertFalse(transferContainer.getAllowableOperations().contains(CREATE), + "The list of allowable operations contains delete option" + transferContainer.getAllowableOperations().toString()); } else { - assertTrue(filePlanComponent.getAllowableOperations().containsAll(asList(UPDATE, CREATE)), - "Wrong list of the allowable operations is return" + filePlanComponent.getAllowableOperations().toString()); + unfiledContainer = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(containerAlias, "include=" + ALLOWABLE_OPERATIONS); + + // Check the list of allowableOperations returned + assertTrue(unfiledContainer.getAllowableOperations().containsAll(asList(UPDATE, CREATE)), + "Wrong list of the allowable operations is return" + unfiledContainer.getAllowableOperations().toString()); + + // Check the list of allowableOperations doesn't contain DELETE operation + assertFalse(unfiledContainer.getAllowableOperations().contains(DELETE), + "The list of allowable operations contains delete option" + unfiledContainer.getAllowableOperations().toString()); } - - // Check the list of allowableOperations doesn't contains DELETE operation - assertFalse(filePlanComponent.getAllowableOperations().contains(DELETE), - "The list of allowable operations contains delete option" + filePlanComponent.getAllowableOperations().toString()); - } - - /** - * Given that a file plan exists - * When I ask the API to modify the details of the file plan - * Then the details of the file are modified - * Note: the details of the file plan are limited to title and description. - */ - @Test - @Bug (id = "RM-4295") - public void updateFilePlan() throws Exception - { - String FILE_PLAN_DESCRIPTION = "Description updated " + getRandomAlphanumeric(); - String FILE_PLAN_TITLE = "Title updated " + getRandomAlphanumeric(); - - // Create RM Site if doesn't exist - createRMSiteIfNotExists(); - - // Build object for updating the filePlan - FilePlanComponent filePlanComponent = FilePlanComponent.builder() - .properties(FilePlanComponentProperties.builder() - .title(FILE_PLAN_TITLE) - .description(FILE_PLAN_DESCRIPTION) - .build()) - .build(); - - // Update the record category - FilePlanComponent renamedFilePlanComponent = getRestAPIFactory().getFilePlanComponentsAPI().updateFilePlanComponent(filePlanComponent, FILE_PLAN_ALIAS); - - // Verify the response status code - assertStatusCode(OK); - - // Verify the returned description field for the file plan component - assertEquals(renamedFilePlanComponent.getProperties().getDescription(), FILE_PLAN_DESCRIPTION); - - // Verify the returned title field for the file plan component - assertEquals(renamedFilePlanComponent.getProperties().getTitle(), FILE_PLAN_TITLE); - } - - /** - * Given that a file plan exists - * When I ask the API to delete the file plan - * Then the 422 response code is returned. - */ - @Test - ( - description = "Check the response code when deleting the special file plan components", - dataProviderClass = TestData.class, - dataProvider = "getContainers" - ) - public void deleteFilePlanSpecialComponents(String filePlanComponentAlias) throws Exception - { - // Create RM Site if doesn't exist - createRMSiteIfNotExists(); - - // Delete the file plan component - getRestAPIFactory().getFilePlanComponentsAPI().deleteFilePlanComponent(filePlanComponentAlias); - - // Check the DELETE response status code - assertStatusCode(UNPROCESSABLE_ENTITY); - } - - /** - * Given that a file plan exists and I am a non RM user - * When I ask the API to delete the file plan - * Then the 403 response code is returned. - */ - @Test - ( - description = "Check the response code when deleting the special file plan components with non RM user", - dataProviderClass = TestData.class, - dataProvider = "getContainers" - ) - public void deleteFilePlanSpecialComponentsNonRMUser(String filePlanComponentAlias) throws Exception - { - // Create RM Site if doesn't exist - createRMSiteIfNotExists(); - - // Create a random user - UserModel nonRMuser = getDataUser().createRandomTestUser("testUser"); - - // Delete the file plan component - getRestAPIFactory().getFilePlanComponentsAPI(nonRMuser).deleteFilePlanComponent(filePlanComponentAlias); - - // Check the DELETE response status code - assertStatusCode(FORBIDDEN); } /** + *
      * Given that RM site exists
-     * When I ask to create the file plan
-     * Then the 403 response code is returned.
-     */
-    @Test
-    (
-        description = "Check the response code when creating the special file plan components",
-        dataProviderClass = TestData.class,
-        dataProvider = "getContainersAndTypes"
-    )
-    @Bug(id = "RM-4296")
-    public void createFilePlanSpecialContainerWhenExists(String filePlanComponentAlias, String filePlanComponentType) throws Exception
-    {
-        // Create RM Site if doesn't exist
-        createRMSiteIfNotExists();
-
-        // Get the RM site ID
-        String rmSiteId = getRestAPIFactory().getRMSiteAPI().getSite().getGuid();
-        String name = filePlanComponentAlias + getRandomAlphanumeric();
-
-        // Build the file plan root properties
-        FilePlanComponent filePlanComponent = FilePlanComponent.builder()
-                .name(name)
-                .nodeType(filePlanComponentType)
-                .properties(FilePlanComponentProperties.builder()
-                                .build())
-                .build();
-
-        FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI();
-
-        // Create the special containers into RM site - parent folder
-        filePlanComponentsAPI.createFilePlanComponent(filePlanComponent, rmSiteId);
-        assertStatusCode(UNPROCESSABLE_ENTITY);
-
-        // Create the special containers into RM site - parent folder
-        filePlanComponentsAPI.createFilePlanComponent(filePlanComponent, FILE_PLAN_ALIAS);
-        assertStatusCode(UNPROCESSABLE_ENTITY);
-
-        // Create the special containers into the root of special containers containers
-        filePlanComponentsAPI.createFilePlanComponent(filePlanComponent, filePlanComponentAlias);
-        assertStatusCode(UNPROCESSABLE_ENTITY);
-    }
-
-    /**
-     * Given that RM site  exists
-     * When a non-RM user ask the API for the details of the file plan
+     * When a non-RM user asks the API for the details of the file plan
      * Then the status code 403 (Permission denied) is return
+     * 
*/ @Test ( - description = "Check the response code when the RM site containers are get with non rm users", + description = "Check the response code when the RM site containers are get with non-RM users", dataProviderClass = TestData.class, dataProvider = "getContainers" ) - public void getSpecialFilePlanComponentsWithNonRMuser(String filePlanComponentAlias) throws Exception + public void getContainersWithNonRMuser(String containerAlias) throws Exception { // Create RM Site if doesn't exist createRMSiteIfNotExists(); @@ -303,7 +239,18 @@ public class FilePlanTests extends BaseRMRestTest UserModel nonRMuser = getDataUser().createRandomTestUser("testUser"); // Get the special file plan components - getRestAPIFactory().getFilePlanComponentsAPI(nonRMuser).getFilePlanComponent(filePlanComponentAlias); + if (FILE_PLAN_ALIAS.equalsIgnoreCase(containerAlias)) + { + getRestAPIFactory().getFilePlansAPI(nonRMuser).getFilePlan(containerAlias); + } + else if(TRANSFERS_ALIAS.equalsIgnoreCase(containerAlias)) + { + getRestAPIFactory().getTransferContainerAPI(nonRMuser).getTransferContainer(containerAlias); + } + else + { + getRestAPIFactory().getUnfiledContainersAPI(nonRMuser).getUnfiledContainer(containerAlias); + } // Check the response status code is FORBIDDEN assertStatusCode(FORBIDDEN); diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/FileRecordsTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/FileRecordsTests.java index 57a15021f6..02e2686707 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/FileRecordsTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/FileRecordsTests.java @@ -29,11 +29,11 @@ package org.alfresco.rest.rm.community.fileplancomponents; import static org.alfresco.rest.rm.community.base.TestData.ELECTRONIC_RECORD_NAME; import static org.alfresco.rest.rm.community.base.TestData.NONELECTRONIC_RECORD_NAME; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.HOLDS_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.NON_ELECTRONIC_RECORD_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createTempFile; import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; import static org.springframework.http.HttpStatus.BAD_REQUEST; @@ -43,13 +43,21 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.AssertJUnit.assertTrue; +import java.util.List; + import org.alfresco.rest.rm.community.base.BaseRMRestTest; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentContent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties; -import org.alfresco.rest.rm.community.model.fileplancomponents.RecordBodyFile; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI; -import org.alfresco.rest.rm.community.requests.igCoreAPI.RecordsAPI; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.record.RecordBodyFile; +import org.alfresco.rest.rm.community.model.record.RecordContent; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChildCollection; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChildEntry; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildProperties; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledRecordFolderAPI; import org.alfresco.utility.report.Bug; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -63,14 +71,14 @@ import org.testng.annotations.Test; */ public class FileRecordsTests extends BaseRMRestTest { - private FilePlanComponent electronicRecord = FilePlanComponent.builder() + private UnfiledContainerChild electronicRecord = UnfiledContainerChild.builder() .name(ELECTRONIC_RECORD_NAME) .nodeType(CONTENT_TYPE.toString()) - .content(FilePlanComponentContent.builder().mimeType("text/plain").build()) + .content(RecordContent.builder().mimeType("text/plain").build()) .build(); - private FilePlanComponent nonelectronicRecord = FilePlanComponent.builder() - .properties(FilePlanComponentProperties.builder() + private UnfiledContainerChild nonelectronicRecord = UnfiledContainerChild.builder() + .properties(UnfiledContainerChildProperties.builder() .description(NONELECTRONIC_RECORD_NAME) .title("Title") .build()) @@ -78,126 +86,170 @@ public class FileRecordsTests extends BaseRMRestTest .nodeType(NON_ELECTRONIC_RECORD_TYPE.toString()) .build(); - /** - * Unfiled containers from where record can be filed - */ - @DataProvider (name = "unfiledContainer") - public Object[][] getUnfiledContainer() throws Exception - { - return new Object[][] { - //unfiled container - { getFilePlanComponent(UNFILED_RECORDS_CONTAINER_ALIAS).getId() }, - // an arbitrary unfiled records folder - { createUnfiledRecordsFolder(UNFILED_RECORDS_CONTAINER_ALIAS.toString(), "Unfiled Folder " + getRandomAlphanumeric()).getId() } - }; - } - /** * Invalid containers where electronic and non-electronic records can be filed */ @DataProvider (name = "invalidContainersForFile") - public Object[][] getFolderContainers() throws Exception + public String[][] getFolderContainers() throws Exception { - return new Object[][] { - { getFilePlanComponent(FILE_PLAN_ALIAS).getId()}, - { getFilePlanComponent(UNFILED_RECORDS_CONTAINER_ALIAS).getId()}, - { getFilePlanComponent(HOLDS_ALIAS).getId() }, - { getFilePlanComponent(TRANSFERS_ALIAS).getId() }, + return new String[][] { + { FILE_PLAN_ALIAS}, + { UNFILED_RECORDS_CONTAINER_ALIAS}, + { TRANSFERS_ALIAS }, // an arbitrary record category - { createCategory(getAdminUser(), FILE_PLAN_ALIAS, "Category " + getRandomAlphanumeric()).getId()}, + { createRootCategory(getAdminUser(), "Category " + getRandomAlphanumeric()).getId()}, // an arbitrary unfiled records folder - { createUnfiledRecordsFolder(UNFILED_RECORDS_CONTAINER_ALIAS.toString(), "Unfiled Folder " + getRandomAlphanumeric()).getId() } + { createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, "Unfiled Folder " + getRandomAlphanumeric(), UNFILED_RECORD_FOLDER_TYPE).getId() } }; } - + /** - * Given an unfiled record in the root unfiled record container or a unfiled record folder + * Given an unfiled record in the root unfiled record container * And an open record folder * When I file the unfiled record into the record folder * Then the record is filed */ @Test - ( - dataProvider = "unfiledContainer", - description = "File record from unfiled containers " - ) - public void fileRecordIntoExistingFolder(String unfiledContainerId) throws Exception + public void fileRecordIntoExistingFolderFromUnfiledContainer() throws Exception { // get API instances - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); - + // create a record folder String folderId = createCategoryFolderInFilePlan().getId(); // create records - FilePlanComponent recordElectronic = filePlanComponentAPI.createElectronicRecord(electronicRecord, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME), unfiledContainerId); - FilePlanComponent recordNonElectId = filePlanComponentAPI.createFilePlanComponent(nonelectronicRecord, unfiledContainerId); + UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI(); + UnfiledContainerChild recordElectronic = unfiledContainersAPI.uploadRecord(electronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS, + createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME)); + UnfiledContainerChild recordNonElect = unfiledContainersAPI.createUnfiledContainerChild(nonelectronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS); // file the record into the folder created RecordBodyFile recordBodyFile = RecordBodyFile.builder().targetParentId(folderId).build(); - FilePlanComponent recordFiled = recordsAPI.fileRecord(recordBodyFile, recordElectronic.getId()); + Record recordFiled = recordsAPI.fileRecord(recordBodyFile, recordElectronic.getId()); // check the response status assertStatusCode(CREATED); // check the parent id for the record returned assertEquals(recordFiled.getParentId(),folderId); - + // check the record is filed into the record folder - assertTrue(filePlanComponentAPI.listChildComponents(folderId) + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + assertTrue(recordFolderAPI.getRecordFolderChildren(folderId) .getEntries() .stream() - .anyMatch(c->c.getFilePlanComponentModel().getId().equals(recordElectronic.getId()))); - + .anyMatch(c -> c.getEntry().getId().equals(recordElectronic.getId()))); + // check the record doesn't exist into unfiled record container - assertFalse(filePlanComponentAPI.listChildComponents(unfiledContainerId) - .getEntries() - .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordElectronic.getId()))); - + assertFalse(unfiledContainersAPI.getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(recordElectronic.getId()))); + // file the non-electronic record into the folder created - FilePlanComponent nonElectRecordFiled = recordsAPI.fileRecord(recordBodyFile, recordNonElectId.getId()); + Record nonElectRecordFiled = recordsAPI.fileRecord(recordBodyFile, recordNonElect.getId()); // check the response status code assertStatusCode(CREATED); // check the parent id for the record returned assertEquals(nonElectRecordFiled.getParentId(), folderId); - + // check the record is added into the record folder - assertTrue(filePlanComponentAPI.listChildComponents(folderId) + assertTrue(recordFolderAPI.getRecordFolderChildren(folderId) .getEntries() .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordNonElectId.getId()))); - + .anyMatch(c -> c.getEntry().getId().equals(recordNonElect.getId()))); + // check the record doesn't exist into unfiled record container - assertFalse(filePlanComponentAPI.listChildComponents(unfiledContainerId) - .getEntries() - .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordNonElectId.getId()))); + assertFalse(unfiledContainersAPI.getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(recordNonElect.getId()))); } /** - * Given an unfiled record in the root unfiled record container or a unfiled record folder + * Given an unfiled record in a unfiled record folder + * And an open record folder + * When I file the unfiled record into the record folder + * Then the record is filed + */ + @Test + public void fileRecordIntoExistingFolderFromUnfiledRecordFolder() throws Exception + { + // get API instances + RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); + + // create a record folder + String folderId = createCategoryFolderInFilePlan().getId(); + + // create records + UnfiledRecordFolderAPI unfiledRecordFoldersAPI = getRestAPIFactory().getUnfiledRecordFoldersAPI(); + + String unfiledRecordFolderId = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, "Unfiled Folder " + getRandomAlphanumeric(), UNFILED_RECORD_FOLDER_TYPE).getId(); + + UnfiledContainerChild recordElectronic = unfiledRecordFoldersAPI.uploadRecord(electronicRecord, unfiledRecordFolderId, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME)); + UnfiledContainerChild recordNonElect = unfiledRecordFoldersAPI.createUnfiledRecordFolderChild(nonelectronicRecord, unfiledRecordFolderId); + + // file the record into the folder created + RecordBodyFile recordBodyFile = RecordBodyFile.builder().targetParentId(folderId).build(); + Record recordFiled = recordsAPI.fileRecord(recordBodyFile, recordElectronic.getId()); + // check the response status + assertStatusCode(CREATED); + // check the parent id for the record returned + assertEquals(recordFiled.getParentId(),folderId); + + // check the record is filed into the record folder + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + assertTrue(recordFolderAPI.getRecordFolderChildren(folderId) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(recordElectronic.getId()))); + + // check the record doesn't exist into unfiled record folder + assertFalse(unfiledRecordFoldersAPI.getUnfiledRecordFolderChildren(unfiledRecordFolderId) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(recordElectronic.getId()))); + + // file the non-electronic record into the folder created + Record nonElectRecordFiled = recordsAPI.fileRecord(recordBodyFile, recordNonElect.getId()); + // check the response status code + assertStatusCode(CREATED); + // check the parent id for the record returned + assertEquals(nonElectRecordFiled.getParentId(), folderId); + + // check the record is added into the record folder + assertTrue(recordFolderAPI.getRecordFolderChildren(folderId) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(recordNonElect.getId()))); + + // check the record doesn't exist into unfiled record folder + assertFalse(unfiledRecordFoldersAPI.getUnfiledRecordFolderChildren(unfiledRecordFolderId) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(recordNonElect.getId()))); + } + + /** + * Given an unfiled record in the root unfiled record container * And a closed record folder * When I file the unfiled record into the record folder * Then I get an unsupported operation exception * */ @Test - ( - dataProvider = "unfiledContainer", - description = "File record from unfiled containers into a closed folder " - ) - public void fileRecordIntoCloseFolder(String unfiledContainerId) throws Exception + public void fileRecordIntoCloseFolderFromUnfiledContainer() throws Exception { // get API instances - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); - + // create a record folder String folderId = createCategoryFolderInFilePlan().getId(); closeFolder(folderId); // create records - FilePlanComponent recordElectronic = filePlanComponentAPI.createElectronicRecord(electronicRecord, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME), unfiledContainerId); - FilePlanComponent recordNonElectId = filePlanComponentAPI.createFilePlanComponent(nonelectronicRecord, unfiledContainerId); + UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI(); + + UnfiledContainerChild recordElectronic = unfiledContainersAPI.uploadRecord(electronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME)); + UnfiledContainerChild recordNonElect = unfiledContainersAPI.createUnfiledContainerChild(nonelectronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS); // file the record into the folder created RecordBodyFile recordBodyFile = RecordBodyFile.builder().targetParentId(folderId).build(); @@ -205,33 +257,93 @@ public class FileRecordsTests extends BaseRMRestTest // check the response status assertStatusCode(FORBIDDEN); - // check the record is filed into the record folder - assertFalse(filePlanComponentAPI.listChildComponents(folderId) + // check the record is not filed into the record folder + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + assertFalse(recordFolderAPI.getRecordFolderChildren(folderId) .getEntries() .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordElectronic.getId()))); - - // check the record doesn't exist into unfiled record container - assertTrue(filePlanComponentAPI.listChildComponents(unfiledContainerId) - .getEntries() - .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordElectronic.getId()))); - + .anyMatch(c -> c.getEntry().getId().equals(recordElectronic.getId()))); + + // check the record exist into unfiled record container + assertTrue(unfiledContainersAPI.getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(recordElectronic.getId()))); + // file the non-electronic record into the folder created - recordsAPI.fileRecord(recordBodyFile, recordNonElectId.getId()); + recordsAPI.fileRecord(recordBodyFile, recordNonElect.getId()); // check the response status code assertStatusCode(FORBIDDEN); - - // check the record is added into the record folder - assertFalse(filePlanComponentAPI.listChildComponents(folderId) + + // check the record is not added into the record folder + assertFalse(recordFolderAPI.getRecordFolderChildren(folderId) .getEntries() .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordNonElectId.getId()))); - - // check the record doesn't exist into unfiled record container - assertTrue(filePlanComponentAPI.listChildComponents(unfiledContainerId) - .getEntries().stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordNonElectId.getId()))); + .anyMatch(c -> c.getEntry().getId().equals(recordNonElect.getId()))); + + // check the record exist into unfiled record container + assertTrue(unfiledContainersAPI.getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS) + .getEntries().stream() + .anyMatch(c -> c.getEntry().getId().equals(recordNonElect.getId()))); + } + + /** + * Given an unfiled record in a unfiled record folder + * And a closed record folder + * When I file the unfiled record into the record folder + * Then I get an unsupported operation exception + * + */ + @Test + public void fileRecordIntoCloseFolderFromUnfiledRecordFolder() throws Exception + { + // get API instances + RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); + + // create a record folder + String folderId = createCategoryFolderInFilePlan().getId(); + closeFolder(folderId); + // create records + UnfiledRecordFolderAPI unfiledRecordFoldersAPI = getRestAPIFactory().getUnfiledRecordFoldersAPI(); + + String unfiledRecordFolderId = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, "Unfiled Folder " + getRandomAlphanumeric(), UNFILED_RECORD_FOLDER_TYPE).getId(); + UnfiledContainerChild recordElectronic = unfiledRecordFoldersAPI.uploadRecord(electronicRecord, unfiledRecordFolderId, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME)); + UnfiledContainerChild recordNonElect = unfiledRecordFoldersAPI.createUnfiledRecordFolderChild(nonelectronicRecord, unfiledRecordFolderId); + + // file the record into the folder created + RecordBodyFile recordBodyFile = RecordBodyFile.builder().targetParentId(folderId).build(); + recordsAPI.fileRecord(recordBodyFile, recordElectronic.getId()); + // check the response status + assertStatusCode(FORBIDDEN); + + // check the record is not filed into the record folder + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + assertFalse(recordFolderAPI.getRecordFolderChildren(folderId) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(recordElectronic.getId()))); + + // check the record exist into unfiled record folder + assertTrue(unfiledRecordFoldersAPI.getUnfiledRecordFolderChildren(unfiledRecordFolderId) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(recordElectronic.getId()))); + + // file the non-electronic record into the folder created + recordsAPI.fileRecord(recordBodyFile, recordNonElect.getId()); + // check the response status code + assertStatusCode(FORBIDDEN); + + // check the record is not added into the record folder + assertFalse(recordFolderAPI.getRecordFolderChildren(folderId) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(recordNonElect.getId()))); + + // check the record exist into unfiled record folder + assertTrue(unfiledRecordFoldersAPI.getUnfiledRecordFolderChildren(unfiledRecordFolderId) + .getEntries().stream() + .anyMatch(c -> c.getEntry().getId().equals(recordNonElect.getId()))); } /** @@ -245,20 +357,20 @@ public class FileRecordsTests extends BaseRMRestTest public void linkRecordInto() throws Exception { // get API instances - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); - + // create a record folder String parentFolderId = createCategoryFolderInFilePlan().getId(); // create records - FilePlanComponent recordElectronic = filePlanComponentAPI.createElectronicRecord(electronicRecord, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME), UNFILED_RECORDS_CONTAINER_ALIAS); - FilePlanComponent recordNonElect = filePlanComponentAPI.createFilePlanComponent(nonelectronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS); + UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI(); + UnfiledContainerChild recordElectronic = unfiledContainersAPI.uploadRecord(electronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME)); + UnfiledContainerChild recordNonElect = unfiledContainersAPI.createUnfiledContainerChild(nonelectronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS); // file the record into the folder created RecordBodyFile recordBodyFile = RecordBodyFile.builder().targetParentId(parentFolderId).build(); - FilePlanComponent recordFiled = recordsAPI.fileRecord(recordBodyFile, recordElectronic.getId()); - FilePlanComponent nonElectronicFiled = recordsAPI.fileRecord(recordBodyFile, recordNonElect.getId()); + Record recordFiled = recordsAPI.fileRecord(recordBodyFile, recordElectronic.getId()); + Record nonElectronicFiled = recordsAPI.fileRecord(recordBodyFile, recordNonElect.getId()); // check the response status assertStatusCode(CREATED); @@ -269,41 +381,43 @@ public class FileRecordsTests extends BaseRMRestTest // check the response status assertStatusCode(CREATED); // link the electronic record - FilePlanComponent recordLink = recordsAPI.fileRecord(recordBodyFile, recordElectronic.getId()); + Record recordLink = recordsAPI.fileRecord(recordBodyFile, recordElectronic.getId()); assertTrue(recordLink.getParentId().equals(parentFolderId)); // check the response status code assertStatusCode(CREATED); // link the nonelectronic record - FilePlanComponent nonElectronicLink = recordsAPI.fileRecord(recordBodyFile, nonElectronicFiled.getId()); + Record nonElectronicLink = recordsAPI.fileRecord(recordBodyFile, nonElectronicFiled.getId()); assertStatusCode(CREATED); assertTrue(nonElectronicLink.getParentId().equals(parentFolderId)); // check the record is added into the record folder - assertTrue(filePlanComponentAPI.listChildComponents(parentFolderId) + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + assertTrue(recordFolderAPI.getRecordFolderChildren(parentFolderId) .getEntries() .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordFiled.getId()) && - c.getFilePlanComponentModel().getParentId().equals(parentFolderId))); - + .anyMatch(c -> c.getEntry().getId().equals(recordFiled.getId()) && + c.getEntry().getParentId().equals(parentFolderId))); + // check the record doesn't exist into unfiled record container // TODO add a check after the issue will be fixed RM-4578 - assertTrue(filePlanComponentAPI.listChildComponents(folderToLink) + assertTrue(recordFolderAPI.getRecordFolderChildren(folderToLink) .getEntries().stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordFiled.getId()))); + .anyMatch(c -> c.getEntry().getId().equals(recordFiled.getId()))); // check the record is added into the record folder - assertTrue(filePlanComponentAPI.listChildComponents(parentFolderId) - .getEntries().stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(nonElectronicFiled.getId()) && - c.getFilePlanComponentModel().getParentId().equals(parentFolderId))); - + assertTrue(recordFolderAPI.getRecordFolderChildren(parentFolderId) + .getEntries() + .stream() + .anyMatch(c -> c.getEntry().getId().equals(nonElectronicFiled.getId()) && + c.getEntry().getParentId().equals(parentFolderId))); + // check the record doesn't exist into unfiled record container // TODO add a check after the issue will be fixed RM-4578 - assertTrue(filePlanComponentAPI.listChildComponents(folderToLink) + assertTrue(recordFolderAPI.getRecordFolderChildren(folderToLink) .getEntries().stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(nonElectronicFiled.getId()))); + .anyMatch(c -> c.getEntry().getId().equals(nonElectronicFiled.getId()))); } - + /** * Given an unfiled or filed record * And a container that is NOT a record folder @@ -313,17 +427,18 @@ public class FileRecordsTests extends BaseRMRestTest @Test ( dataProvider = "invalidContainersForFile", - description = "File the unfiled record to the container that is not a record foldr" + description = "File the unfiled record to the container that is not a record folder" ) public void invalidContainerToFile(String containerId) throws Exception { // get API instances - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); - + // create records - FilePlanComponent recordElectronic = filePlanComponentAPI.createElectronicRecord(electronicRecord, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME), UNFILED_RECORDS_CONTAINER_ALIAS); - FilePlanComponent recordNonElect = filePlanComponentAPI.createFilePlanComponent(nonelectronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS); + UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI(); + + UnfiledContainerChild recordElectronic = unfiledContainersAPI.uploadRecord(electronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME)); + UnfiledContainerChild recordNonElect = unfiledContainersAPI.createUnfiledContainerChild(nonelectronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS); // file the record into the folder created RecordBodyFile recordBodyFile = RecordBodyFile.builder().targetParentId(containerId).build(); @@ -334,70 +449,4 @@ public class FileRecordsTests extends BaseRMRestTest // check the response status assertStatusCode(BAD_REQUEST); } - - /** - * Given an unfiled record in the root unfiled record container or a unfiled record folder - * When I file the unfiled record into the record folder using the relativePath - * Then the filePlan structure from relativePath is created and the record is filed into the specified path - */ - @Test - ( - dataProvider = "unfiledContainer", - description = "File record from unfiled containers " - ) - public void fileRecordIntoRelativePath(String unfiledContainerId) throws Exception - { - // get API instances - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); - - // create a record folder - String RELATIVE_PATH = "CATEGORY" + getRandomAlphanumeric() + "/FOLDER"; - - // create records - FilePlanComponent recordElectronic = filePlanComponentAPI.createElectronicRecord(electronicRecord, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME), unfiledContainerId); - FilePlanComponent recordNonElectId = filePlanComponentAPI.createFilePlanComponent(nonelectronicRecord, unfiledContainerId); - - // file the record into the folder created - RecordBodyFile recordBodyFile = RecordBodyFile.builder().relativePath(RELATIVE_PATH).build(); - FilePlanComponent recordFiled = recordsAPI.fileRecord(recordBodyFile, recordElectronic.getId()); - - // check the response status - assertStatusCode(CREATED); - - // get the folder ID created - String folderId = filePlanComponentAPI.getFilePlanComponent(FILE_PLAN_ALIAS, "relativePath="+RELATIVE_PATH).getId(); - // check the parent id for the record returned - assertEquals(recordFiled.getParentId(), folderId); - // check the record is filed into the record folder - assertTrue(filePlanComponentAPI.listChildComponents(folderId) - .getEntries() - .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordElectronic.getId()))); - - // check the record doesn't exist into unfiled record container - assertFalse(filePlanComponentAPI.listChildComponents(unfiledContainerId) - .getEntries() - .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordElectronic.getId()))); - - // file the non-electronic record into the folder created - FilePlanComponent nonElectRecordFiled = recordsAPI.fileRecord(recordBodyFile, recordNonElectId.getId()); - // check the response status code - assertStatusCode(CREATED); - // check the parent id for the record returned - assertEquals(nonElectRecordFiled.getParentId(), folderId); - - // check the record is added into the record folder - assertTrue(filePlanComponentAPI.listChildComponents(folderId) - .getEntries() - .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordNonElectId.getId()))); - - // check the record doesn't exist into unfiled record container - assertFalse(filePlanComponentAPI.listChildComponents(unfiledContainerId) - .getEntries() - .stream() - .anyMatch(c -> c.getFilePlanComponentModel().getId().equals(recordNonElectId.getId()))); - } } diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/NonElectronicRecordTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/NonElectronicRecordTests.java index 681ef8d1f2..fb7c8afd4c 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/NonElectronicRecordTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/NonElectronicRecordTests.java @@ -26,32 +26,37 @@ */ package org.alfresco.rest.rm.community.fileplancomponents; +import static java.lang.Integer.MAX_VALUE; import static java.util.Arrays.asList; - -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.HOLDS_ALIAS; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.NON_ELECTRONIC_RECORD_TYPE; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_CONTAINER_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createNonElectronicRecordModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createFullNonElectronicUnfiledContainerChildRecordModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createFullNonElectronicRecordModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.verifyFullNonElectronicRecord; import static org.alfresco.utility.constants.UserRole.SiteManager; import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; -import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; import java.util.Random; import org.alfresco.rest.rm.community.base.BaseRMRestTest; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.record.RecordProperties; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildProperties; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledRecordFolderAPI; import org.alfresco.utility.constants.UserRole; import org.alfresco.utility.model.SiteModel; import org.alfresco.utility.model.UserModel; @@ -65,45 +70,6 @@ import org.testng.annotations.Test; */ public class NonElectronicRecordTests extends BaseRMRestTest { - /** - *
-     * Given a parent container that is NOT a record folder or an unfiled record folder
-     * When I try to create a non-electronic record within the parent container
-     * Then nothing happens
-     * And an error is reported
-     * 
- * @throws Exception if prerequisites can't be created - */ - @Test(description = "Non-electronic record can't be created as a child of invalid parent Id") - public void cantCreateForInvalidParentIds() throws Exception - { - // create record category, non-electronic records can't be its children - FilePlanComponent recordCategoryModel = FilePlanComponent.builder() - .name("Category " + getRandomAlphanumeric()) - .nodeType(RECORD_CATEGORY_TYPE) - .build(); - - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - FilePlanComponent recordCategory = filePlanComponentsAPI.createFilePlanComponent(recordCategoryModel, FILE_PLAN_ALIAS); - - // iterate through all invalid parent containers and try to create/file an electronic record - asList(FILE_PLAN_ALIAS, TRANSFERS_ALIAS, HOLDS_ALIAS, recordCategory.getId()) - .stream() - .forEach(id -> - { - try - { - filePlanComponentsAPI.createFilePlanComponent(createNonElectronicRecordModel(), id); - } - catch (Exception error) - { - } - - // Verify the status code - assertStatusCode(UNPROCESSABLE_ENTITY); - }); - } - /** *
      * Given a parent container that is a record folder
@@ -114,6 +80,8 @@ public class NonElectronicRecordTests extends BaseRMRestTest
      * 
      * and
      * 
+     *
+     *
      * Given a parent container that is an unfiled record folder or the root unfiled record container
      * When I try to create a non-electronic record within the parent container
      * Then the non-electronic record is created
@@ -126,67 +94,68 @@ public class NonElectronicRecordTests extends BaseRMRestTest
         dataProvider = "validRootContainers",
         description = "Non-electronic records can be created in valid containers"
     )
-    public void canCreateInValidContainers(FilePlanComponent container) throws Exception
+    public void canCreateInValidContainers(String folderId, String type) throws Exception
     {
-        logger.info("Root container:\n" + toJson(container));
+        logger.info("Root container:\n" + toJson(folderId));
 
-        if (container.getNodeType().equals(RECORD_FOLDER_TYPE))
-        {
-            // only record folders can be open or closed
-            assertFalse(container.getProperties().getIsClosed());
-        }
-
-        // use these properties for non-electronic record to be created
+        // Use these properties for non-electronic record to be created
         String title = "Title " + getRandomAlphanumeric();
         String description = "Description " + getRandomAlphanumeric();
         String box = "Box "+ getRandomAlphanumeric();
         String file = "File " + getRandomAlphanumeric();
         String shelf = "Shelf " + getRandomAlphanumeric();
-        String location = "Location " + getRandomAlphanumeric();
+        String storageLocation = "Storage Location " + getRandomAlphanumeric();
         String name = "Record " + getRandomAlphanumeric();
 
         Random random = new Random();
-        Integer copies = random.nextInt(Integer.MAX_VALUE);
-        Integer size = random.nextInt(Integer.MAX_VALUE);
+        Integer numberOfCopies = random.nextInt(MAX_VALUE);
+        Integer physicalSize = random.nextInt(MAX_VALUE);
 
-        // set values of all available properties for the non electronic records
-        FilePlanComponent filePlanComponent = FilePlanComponent.builder()
-                                                           .name(name)
-                                                           .nodeType(NON_ELECTRONIC_RECORD_TYPE)
-                                                           .properties(FilePlanComponentProperties.builder()
-                                                                                                  .title(title)
-                                                                                                  .description(description)
-                                                                                                  .box(box)
-                                                                                                  .file(file)
-                                                                                                  .shelf(shelf)
-                                                                                                  .location(location)
-                                                                                                  .numberOfCopies(copies)
-                                                                                                  .physicalSize(size)
-                                                                                                  .build())
-                                                           .build();
+        String nonElectronicId;
+        if (RECORD_FOLDER_TYPE.equalsIgnoreCase(type))
+        {
+            // Only record folders can be opened or closed
+            RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI();
+            assertFalse(recordFolderAPI.getRecordFolder(folderId).getProperties().getIsClosed());
 
-        // create non-electronic record
-        FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI();
-        String nonElectronicId = filePlanComponentsAPI.createFilePlanComponent(
-            filePlanComponent,
-            container.getId()).getId();
+            // Set values of all available properties for the non electronic records
+            Record nonElectrinicRecordModel = createFullNonElectronicRecordModel(name, title, description, box, file, shelf, storageLocation, numberOfCopies, physicalSize);
 
-        // verify the create request status code
+            // Create non-electronic record
+            nonElectronicId = recordFolderAPI.createRecord(nonElectrinicRecordModel, folderId).getId();
+        }
+        else if(UNFILED_CONTAINER_TYPE.equalsIgnoreCase(type))
+        {
+            // Set values of all available properties for the non electronic records
+            UnfiledContainerChild nonElectrinicRecordModel = createFullNonElectronicUnfiledContainerChildRecordModel(name, title, description, box, file, shelf,
+                                                                                                                     storageLocation, numberOfCopies, physicalSize);
+
+            // Create non-electronic record
+            UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI();
+            nonElectronicId = unfiledContainersAPI.createUnfiledContainerChild(nonElectrinicRecordModel, folderId).getId();
+        }
+        else if(UNFILED_RECORD_FOLDER_TYPE.equalsIgnoreCase(type))
+        {
+            // Set values of all available properties for the non electronic records
+            UnfiledContainerChild nonElectrinicRecordModel = createFullNonElectronicUnfiledContainerChildRecordModel(name, title, description, box, file, shelf,
+                                                                                                                     storageLocation, numberOfCopies, physicalSize);
+
+            // Create non-electronic record
+            UnfiledRecordFolderAPI unfiledRecordFoldersAPI = getRestAPIFactory().getUnfiledRecordFoldersAPI();
+            nonElectronicId = unfiledRecordFoldersAPI.createUnfiledRecordFolderChild(nonElectrinicRecordModel, folderId).getId();
+        }
+        else
+        {
+            throw new Exception("Unsuported type = " + type);
+        }
+
+        // Verify the create request status code
         assertStatusCode(CREATED);
 
-        // get newly created non-electonic record and verify its properties
-        FilePlanComponent nonElectronicRecord = filePlanComponentsAPI.getFilePlanComponent(nonElectronicId);
-
-        assertEquals(title, nonElectronicRecord.getProperties().getTitle());
-        assertEquals(description, nonElectronicRecord.getProperties().getDescription());
-        assertEquals(box, nonElectronicRecord.getProperties().getBox());
-        assertEquals(file, nonElectronicRecord.getProperties().getFile());
-        assertEquals(shelf, nonElectronicRecord.getProperties().getShelf());
-        assertEquals(location, nonElectronicRecord.getProperties().getLocation());
-        assertEquals(copies, nonElectronicRecord.getProperties().getNumberOfCopies());
-        assertEquals(size, nonElectronicRecord.getProperties().getPhysicalSize());
-        assertTrue(nonElectronicRecord.getName().contains(nonElectronicRecord.getProperties().getRmIdentifier()));
-        assertTrue(nonElectronicRecord.getName().contains(name));
+        // Get newly created non-electronic record and verify its properties
+        RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
+        Record nonElectronicRecord = recordsAPI.getRecord(nonElectronicId);
+        verifyFullNonElectronicRecord(nonElectronicRecord, name, title, description, box, file, shelf, storageLocation, numberOfCopies, physicalSize);
     }
 
     /**
@@ -197,23 +166,23 @@ public class NonElectronicRecordTests extends BaseRMRestTest
      * Then nothing happens
      * And an error is reported
      * 
- * @throws Exception if prerequisites can't be created + * @throws Exception if record can't be created */ @Test(description = "Non-electronic record can't be created in closed record folder") public void cantCreateInClosedFolder() throws Exception { - FilePlanComponent recordFolder = createCategoryFolderInFilePlan(); + RecordCategoryChild recordFolder = createCategoryFolderInFilePlan(); - // the folder should be open + // The folder should be open assertFalse(recordFolder.getProperties().getIsClosed()); - // close the folder + // Close the folder closeFolder(recordFolder.getId()); - // try to create it, this should fail and throw an exception - getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(createNonElectronicRecordModel(), recordFolder.getId()); + // Try to create it, this should fail and throw an exception + getRestAPIFactory().getRecordFolderAPI().createRecord(createNonElectronicRecordModel(), recordFolder.getId()); - // verify the status code + // Verify the status code assertStatusCode(UNPROCESSABLE_ENTITY); } @@ -228,61 +197,139 @@ public class NonElectronicRecordTests extends BaseRMRestTest *
* and *
+     *
      * Given a parent container that is an unfiled record folder or the root unfiled record container
      * When I try to create a non-electronic record within the parent container
      * And I do not provide all the required mandatory property values
      * Then nothing happens
      * And an error is reported
      * 
- * @throws Exception if prerequisites can't be created + * @throws Exception if record can't be created */ @Test ( dataProvider = "validRootContainers", description = "Non-electronic record can only be created if all mandatory properties are given" ) - public void allMandatoryPropertiesRequired(FilePlanComponent container) throws Exception + public void allMandatoryPropertiesRequired(String folderId, String type) throws Exception { - logger.info("Root container:\n" + toJson(container)); - if (container.getNodeType().equals(RECORD_FOLDER_TYPE)) + logger.info("Root container:\n" + toJson(folderId)); + + if (type.equals(RECORD_FOLDER_TYPE)) { - // only record folders can be open or closed - assertFalse(container.getProperties().getIsClosed()); + // Only record folders can be opened or closed + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + assertFalse(recordFolderAPI.getRecordFolder(folderId).getProperties().getIsClosed()); + // Component without name and title + Record noNameOrTitle = Record.builder().nodeType(NON_ELECTRONIC_RECORD_TYPE).build(); + + // Component with title only + Record titleOnly = Record.builder() + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .properties(RecordProperties.builder() + .title("Title " + getRandomAlphanumeric()) + .build()) + .build(); + + // Try to create invalid components + asList(noNameOrTitle, titleOnly).stream().forEach(c -> + { + try + { + logger.info("Creating non-electronic record with body:\n" + toJson(c)); + } + catch (Exception error) + { + } + + // This should fail and throw an exception + try + { + getRestAPIFactory().getRecordFolderAPI().createRecord(c, folderId); + } + catch (Exception e) + { + } + + // Verify the status code is BAD_REQUEST + assertStatusCode(BAD_REQUEST); + }); } - - // component without name and title - FilePlanComponent noNameOrTitle = getDummyNonElectronicRecord(); - - // component with title only - FilePlanComponent titleOnly = getDummyNonElectronicRecord(); - FilePlanComponentProperties properties = FilePlanComponentProperties.builder() - .title("Title " + getRandomAlphanumeric()) - .build(); - titleOnly.setProperties(properties); - - // try to create invalid components - asList(noNameOrTitle, titleOnly).stream().forEach(c -> + else if(UNFILED_CONTAINER_TYPE.equalsIgnoreCase(type)) { - try - { - logger.info("Creating non-electronic record with body:\n" + toJson(c)); - } - catch (Exception error) - { - } + // Component without name and title + UnfiledContainerChild noNameOrTitle = UnfiledContainerChild.builder().nodeType(NON_ELECTRONIC_RECORD_TYPE).build(); - // this should fail and throw an exception - try - { - getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(c, container.getId()); - } - catch (Exception e) - { - } + // Component with title only + UnfiledContainerChild titleOnly = UnfiledContainerChild.builder() + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .properties(UnfiledContainerChildProperties.builder() + .title("Title " + getRandomAlphanumeric()) + .build()) + .build(); - // verify the status code is BAD_REQUEST - assertStatusCode(BAD_REQUEST); - }); + // Try to create invalid components + asList(noNameOrTitle, titleOnly).stream().forEach(c -> + { + try + { + logger.info("Creating non-electronic record with body:\n" + toJson(c)); + } + catch (Exception error) + { + } + + // This should fail and throw an exception + try + { + getRestAPIFactory().getUnfiledContainersAPI().createUnfiledContainerChild(c, folderId); + } + catch (Exception e) + { + } + + // Verify the status code is BAD_REQUEST + assertStatusCode(BAD_REQUEST); + }); + } + else + { + //we have unfiled record folder type + // Component without name and title + UnfiledContainerChild noNameOrTitle = UnfiledContainerChild.builder().nodeType(NON_ELECTRONIC_RECORD_TYPE).build(); + + // Component with title only + UnfiledContainerChild titleOnly = UnfiledContainerChild.builder() + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .properties(UnfiledContainerChildProperties.builder() + .title("Title " + getRandomAlphanumeric()) + .build()) + .build(); + + // Try to create invalid components + asList(noNameOrTitle, titleOnly).stream().forEach(c -> + { + try + { + logger.info("Creating non-electronic record with body:\n" + toJson(c)); + } + catch (Exception error) + { + } + + // This should fail and throw an exception + try + { + getRestAPIFactory().getUnfiledRecordFoldersAPI().createUnfiledRecordFolderChild(c, folderId); + } + catch (Exception e) + { + } + + // Verify the status code is BAD_REQUEST + assertStatusCode(BAD_REQUEST); + }); + } } /** @@ -292,62 +339,93 @@ public class NonElectronicRecordTests extends BaseRMRestTest * Then nothing happens * And an error is reported *
- * @throws Exception + * @throws Exception if record can't be created */ @Test ( dataProvider = "validRootContainers", description = "Non-electronic record can't be created if user doesn't have RM privileges" ) - public void cantCreateIfNoRmPrivileges(FilePlanComponent container) throws Exception + public void cantCreateIfNoRmPrivileges(String folderId, String type) throws Exception { UserModel user = createUserWithRole("zzzuser", SiteManager); - // try to create a fileplan component - FilePlanComponent record = FilePlanComponent.builder() - .properties(FilePlanComponentProperties.builder() - .description("Description") - .title("Title") - .build()) - .name("Record Name") - .nodeType(NON_ELECTRONIC_RECORD_TYPE) - .build(); - - - // this should fail and throw an exception - try + if (type.equals(RECORD_FOLDER_TYPE)) { - getRestAPIFactory().getFilePlanComponentsAPI(user).createFilePlanComponent(record, container.getId()); - } - catch (Exception e) - { - } + // Try to create a record model + Record recordModel = Record.builder() + .properties(RecordProperties.builder() + .description("Description") + .title("Title") + .build()) + .name("Record Name") + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .build(); - // user who isn't an RM site member can't access the container path + // This should fail and throw an exception + try + { + getRestAPIFactory().getRecordFolderAPI(user).createRecord(recordModel, folderId); + } + catch (Exception e) + { + } + } + else if(UNFILED_CONTAINER_TYPE.equalsIgnoreCase(type)) + { + // Try to create a record model + UnfiledContainerChild recordModel = UnfiledContainerChild.builder() + .properties(UnfiledContainerChildProperties.builder() + .description("Description") + .title("Title") + .build()) + .name("Record Name") + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .build(); + + // This should fail and throw an exception + try + { + getRestAPIFactory().getUnfiledContainersAPI(user).createUnfiledContainerChild(recordModel, folderId); + } + catch (Exception e) + { + } + } + else + { + // Try to create a record model + UnfiledContainerChild recordModel = UnfiledContainerChild.builder() + .properties(UnfiledContainerChildProperties.builder() + .description("Description") + .title("Title") + .build()) + .name("Record Name") + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .build(); + + // This should fail and throw an exception + try + { + getRestAPIFactory().getUnfiledRecordFoldersAPI(user).createUnfiledRecordFolderChild(recordModel, folderId); + } + catch (Exception e) + { + } + } + // User who isn't an RM site member can't access the container path assertStatusCode(FORBIDDEN); } - /** - * Helper function to return an empty FilePlanComponent for non-electronic record - * @return - */ - private FilePlanComponent getDummyNonElectronicRecord() - { - FilePlanComponent component = FilePlanComponent.builder() - .nodeType(NON_ELECTRONIC_RECORD_TYPE) - .build(); - return component; - } - /** * Create user with given role and add it to RM site *
* Checks whether the user exists in RM site and creates it if required, with password identical - * to username. Note the role is a Core API role, not an RM role. + * to user name. Note the role is a Core API role, not an RM role. *
* For already existing users, no site membership or role verification is performed. *

- * @param userName username to add + * @param userName user name to add * @param userRole user's role * @throws Exception */ @@ -355,17 +433,15 @@ public class NonElectronicRecordTests extends BaseRMRestTest { String siteId = getRestAPIFactory().getRMSiteAPI().getSite().getId(); - // check if user exists + // Check if user exists UserModel user = new UserModel(); user.setUsername(userName); user.setPassword(userName); if (!getDataUser().isUserInRepo(userName)) { - // user doesn't exist, create it + // User doesn't exist, create it user = getDataUser().createUser(userName, userName); - user.setUserRole(userRole); - getDataUser().addUserToSite(user, new SiteModel(siteId), userRole); } diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/ReadRecordTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/ReadRecordTests.java index d46ace9914..9545a142fd 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/ReadRecordTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/ReadRecordTests.java @@ -26,17 +26,23 @@ */ package org.alfresco.rest.rm.community.fileplancomponents; -import static org.alfresco.rest.rm.community.base.TestData.FOLDER_NAME; +import static org.alfresco.rest.rm.community.base.TestData.RECORD_CATEGORY_NAME; +import static org.alfresco.rest.rm.community.base.TestData.RECORD_CATEGORY_TITLE; +import static org.alfresco.rest.rm.community.base.TestData.RECORD_FOLDER_NAME; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.HOLDS_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.IS_COMPLETED; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.CONTENT; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PATH; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.NON_ELECTRONIC_RECORD_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.IMAGE_FILE; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createRecordCategoryModel; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createTempFile; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.getFile; import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.OK; @@ -46,55 +52,56 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.fail; import static org.testng.AssertJUnit.assertTrue; -import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.NoSuchElementException; -import com.google.common.io.Resources; - import org.alfresco.rest.rm.community.base.BaseRMRestTest; import org.alfresco.rest.rm.community.base.TestData; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentContent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentsCollection; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI; -import org.alfresco.rest.rm.community.requests.igCoreAPI.RecordsAPI; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.record.RecordContent; +import org.alfresco.rest.rm.community.model.record.RecordProperties; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; +import org.alfresco.rest.rm.community.model.recordfolder.RecordFolderCollection; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildCollection; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildProperties; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledRecordFolderAPI; import org.apache.commons.codec.digest.DigestUtils; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** - * This class contains the tests for - * Read Records API + * This class contains the tests for Read Records API * * @author Rodica Sutu * @since 2.6 */ public class ReadRecordTests extends BaseRMRestTest { - String CATEGORY_NAME=TestData.CATEGORY_NAME +getRandomAlphanumeric(); + public static final String CATEGORY_NAME = TestData.RECORD_CATEGORY_NAME + getRandomAlphanumeric(); - String ELECTRONIC_RECORD_NAME = "Record electronic" + getRandomAlphanumeric(); - String NONELECTRONIC_RECORD_NAME = "Record nonelectronic" + getRandomAlphanumeric(); + public static final String ELECTRONIC_RECORD_NAME = "Record electronic" + getRandomAlphanumeric(); + public static final String NONELECTRONIC_RECORD_NAME = "Record nonelectronic" + getRandomAlphanumeric(); - private FilePlanComponent electronicRecord = FilePlanComponent.builder() - .name(ELECTRONIC_RECORD_NAME) - .nodeType(CONTENT_TYPE.toString()) - .content(FilePlanComponentContent.builder().mimeType("text/plain").build()) - .build(); - - private FilePlanComponent nonelectronicRecord = FilePlanComponent.builder() - .properties(FilePlanComponentProperties.builder() - .description(NONELECTRONIC_RECORD_NAME) - .title("Title") - .build()) - .name(NONELECTRONIC_RECORD_NAME) - .nodeType(NON_ELECTRONIC_RECORD_TYPE.toString()) - .build(); + private Record electronicRecord = Record.builder() + .name(ELECTRONIC_RECORD_NAME) + .nodeType(CONTENT_TYPE) + .build(); + private Record nonelectronicRecord = Record.builder() + .properties(RecordProperties.builder() + .description(NONELECTRONIC_RECORD_NAME) + .title("Title") + .build()) + .name(NONELECTRONIC_RECORD_NAME) + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .build(); /** * Given a record category or a container which can't contain records @@ -102,12 +109,11 @@ public class ReadRecordTests extends BaseRMRestTest * Then I receive an empty list */ @DataProvider(name="invalidContainersForRecords") - public Object[][] getInvalidContainersForRecords() throws Exception + public String[][] getInvalidContainersForRecords() throws Exception { - return new Object[][] { + return new String[][] { { FILE_PLAN_ALIAS }, { TRANSFERS_ALIAS }, - { HOLDS_ALIAS }, { createCategoryFolderInFilePlan().getParentId()} }; } @@ -118,31 +124,54 @@ public class ReadRecordTests extends BaseRMRestTest ) public void readRecordsFromInvalidContainers(String container) throws Exception { - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - FilePlanComponent electronicRecord = FilePlanComponent.builder() - .name(ELECTRONIC_RECORD_NAME) - .nodeType(CONTENT_TYPE) - .content(FilePlanComponentContent.builder().mimeType("text/plain").build()) - .build(); - FilePlanComponent nonelectronicRecord = FilePlanComponent.builder() - .properties(FilePlanComponentProperties.builder() - .description("Description") - .title("Title") - .build()) - .name(NONELECTRONIC_RECORD_NAME) - .nodeType(NON_ELECTRONIC_RECORD_TYPE) - .build(); + Record electronicRecord = Record.builder() + .name(ELECTRONIC_RECORD_NAME) + .nodeType(CONTENT_TYPE) + .content(RecordContent.builder().mimeType("text/plain").build()) + .build(); + Record nonelectronicRecord = Record.builder() + .properties(RecordProperties.builder() + .description("Description") + .title("Title") + .build()) + .name(NONELECTRONIC_RECORD_NAME) + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .build(); //create records - filePlanComponentAPI.createFilePlanComponent(electronicRecord, container); - filePlanComponentAPI.createFilePlanComponent(nonelectronicRecord, container); + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + recordFolderAPI.createRecord(electronicRecord, container); + assertStatusCode(BAD_REQUEST); + recordFolderAPI.createRecord(nonelectronicRecord, container); + assertStatusCode(BAD_REQUEST); - - // List children from API - filePlanComponentAPI.listChildComponents(container, "where=(isFile=true)") - .assertThat()//check the list returned is empty - .entriesListIsEmpty().assertThat().paginationExist(); - //check response status code - assertStatusCode(OK); + if(FILE_PLAN_ALIAS.equals(container)) + { + getRestAPIFactory().getFilePlansAPI().getRootRecordCategories(container, "") + .assertThat()//check the list returned is not empty + .entriesListIsNotEmpty().assertThat().paginationExist(); + //check response status code + assertStatusCode(OK); + } + else if(TRANSFERS_ALIAS.equals(container)) + { + getRestAPIFactory().getTransferContainerAPI().getTransfers(container, "where=(isFile=true)") + .assertThat()//check the list returned is empty + .entriesListIsEmpty().assertThat().paginationExist(); + //check response status code + assertStatusCode(OK); + } + else + { + String recordCategoryId = getRestAPIFactory().getRecordCategoryAPI().getRecordCategory(container).getId(); + assertStatusCode(OK); + getRestAPIFactory().getRecordCategoryAPI().getRecordCategoryChildren(recordCategoryId) + .assertThat()//check the list returned is empty + .entriesListCountIs(1).assertThat().paginationExist(); + String nodeType = getRestAPIFactory().getRecordCategoryAPI().getRecordCategoryChildren(recordCategoryId).getEntries().get(0).getEntry().getNodeType(); + assertEquals(nodeType, RECORD_FOLDER_TYPE); + //check response status code + assertStatusCode(OK); + } } @@ -155,51 +184,49 @@ public class ReadRecordTests extends BaseRMRestTest public void readRecordMetadata() throws Exception { String RELATIVE_PATH = "/" + CATEGORY_NAME + getRandomAlphanumeric() + "/folder"; - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - //create the containers from the relativePath - FilePlanComponent recordFolder = FilePlanComponent.builder() - .name(FOLDER_NAME) - .nodeType(RECORD_FOLDER_TYPE) - .relativePath(RELATIVE_PATH) - .build(); - String folderId = filePlanComponentAPI.createFilePlanComponent(recordFolder, FILE_PLAN_ALIAS).getId(); - //create electronic record - String recordWithContentId = filePlanComponentAPI.createElectronicRecord(electronicRecord, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME), folderId).getId(); + RecordCategory recordCategoryModel = createRecordCategoryModel(RECORD_CATEGORY_NAME, RECORD_CATEGORY_TITLE); + String recordCategoryId = getRestAPIFactory().getFilePlansAPI().createRootRecordCategory(recordCategoryModel, FILE_PLAN_ALIAS).getId(); + + //create the containers from the relativePath + RecordCategoryChild recordFolderModel = RecordCategoryChild.builder() + .name(RECORD_FOLDER_NAME) + .nodeType(RECORD_FOLDER_TYPE) + .relativePath(RELATIVE_PATH) + .build(); + + String recordFolderId = getRestAPIFactory().getRecordCategoryAPI().createRecordCategoryChild(recordFolderModel, recordCategoryId, "include=" + PATH).getId(); + + //create electronic record + String recordWithContentId = getRestAPIFactory().getRecordFolderAPI().createRecord(electronicRecord, recordFolderId, getFile(IMAGE_FILE)).getId(); + //Get the record created - FilePlanComponent recordWithContent= filePlanComponentAPI.getFilePlanComponent(recordWithContentId, "include = "+IS_COMPLETED); + Record recordWithContent= getRestAPIFactory().getRecordsAPI().getRecord(recordWithContentId, "include="+IS_COMPLETED +"," + CONTENT); + //Check the metadata returned assertTrue(recordWithContent.getName().startsWith(ELECTRONIC_RECORD_NAME)); - assertTrue(recordWithContent.getIsFile()); - assertFalse(recordWithContent.getIsCategory()); - assertFalse(recordWithContent.getIsRecordFolder()); assertNotNull(recordWithContent.getContent().getEncoding()); assertEquals(recordWithContent.getNodeType(),CONTENT_TYPE); assertNotNull(recordWithContent.getContent().getEncoding()); assertNotNull(recordWithContent.getContent().getMimeType()); assertNotNull(recordWithContent.getAspectNames()); assertFalse(recordWithContent.getName().equals(ELECTRONIC_RECORD_NAME)); - assertTrue(recordWithContent.getName().contains(recordWithContent.getProperties().getRmIdentifier())); + assertTrue(recordWithContent.getName().contains(recordWithContent.getProperties().getIdentifier())); assertStatusCode(OK); //create non-electronic record - String nonElectronicRecordId = filePlanComponentAPI.createFilePlanComponent(nonelectronicRecord, folderId).getId(); + String nonElectronicRecordId = getRestAPIFactory().getRecordFolderAPI().createRecord(nonelectronicRecord, recordFolderId).getId(); //Get the record created - FilePlanComponent nonElectronicRecord = filePlanComponentAPI.getFilePlanComponent(nonElectronicRecordId, "include = " + IS_COMPLETED); + Record nonElectronicRecord = getRestAPIFactory().getRecordsAPI().getRecord(nonElectronicRecordId, "include=" + IS_COMPLETED +"," + CONTENT); //Check the metadata returned assertTrue(nonElectronicRecord.getName().startsWith(NONELECTRONIC_RECORD_NAME)); - assertTrue(nonElectronicRecord.getIsFile()); - assertFalse(nonElectronicRecord.getIsCategory()); - assertFalse(nonElectronicRecord.getIsRecordFolder()); - assertNotNull(nonElectronicRecord.getContent().getEncoding()); + assertEquals(nonElectronicRecord.getContent(), null); assertEquals(nonElectronicRecord.getNodeType(), NON_ELECTRONIC_RECORD_TYPE); - assertNotNull(nonElectronicRecord.getContent().getEncoding()); - assertNotNull(nonElectronicRecord.getContent().getMimeType()); assertNotNull(nonElectronicRecord.getAspectNames()); assertEquals(nonElectronicRecord.getProperties().getDescription(), NONELECTRONIC_RECORD_NAME); assertFalse(nonElectronicRecord.getName().equals(NONELECTRONIC_RECORD_NAME)); - assertTrue(nonElectronicRecord.getName().contains(nonElectronicRecord.getProperties().getRmIdentifier())); + assertTrue(nonElectronicRecord.getName().contains(nonElectronicRecord.getProperties().getIdentifier())); assertStatusCode(OK); } @@ -211,43 +238,46 @@ public class ReadRecordTests extends BaseRMRestTest @Test public void readRecordContent() throws Exception { - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(); String RECORD_ELECTRONIC = "Record " + getRandomAlphanumeric(); String RECORD_ELECTRONIC_BINARY = "Binary Record" + getRandomAlphanumeric(); + String existentRecordCategoryId = createCategoryFolderInFilePlan().getParentId(); + String RELATIVE_PATH = "/" + CATEGORY_NAME + getRandomAlphanumeric() + "/folder"; // create the containers from the relativePath - FilePlanComponent recordFolder = FilePlanComponent.builder() - .name(FOLDER_NAME) + RecordCategoryChild recordFolder = RecordCategoryChild.builder() + .name(RECORD_FOLDER_NAME) .nodeType(RECORD_FOLDER_TYPE) .relativePath(RELATIVE_PATH) .build(); - String folderId = filePlanComponentAPI.createFilePlanComponent(recordFolder, FILE_PLAN_ALIAS).getId(); + RecordCategoryAPI recordCategoryAPI = getRestAPIFactory().getRecordCategoryAPI(); + String folderId = recordCategoryAPI.createRecordCategoryChild(recordFolder, existentRecordCategoryId).getId(); // text file as an electronic record - FilePlanComponent recordText = FilePlanComponent.builder() - .name(RECORD_ELECTRONIC) - .nodeType(CONTENT_TYPE) - .build(); - String recordId = filePlanComponentAPI.createElectronicRecord(recordText, createTempFile(RECORD_ELECTRONIC, RECORD_ELECTRONIC), folderId).getId(); + Record recordText = Record.builder() + .name(RECORD_ELECTRONIC) + .nodeType(CONTENT_TYPE) + .build(); + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + String recordId = recordFolderAPI.createRecord(recordText, folderId, createTempFile(RECORD_ELECTRONIC, RECORD_ELECTRONIC)).getId(); assertEquals(recordsAPI.getRecordContent(recordId).asString(), RECORD_ELECTRONIC); // Check status code assertStatusCode(OK); // binary file as an electronic record - FilePlanComponent recordBinary = FilePlanComponent.builder() + Record recordBinary = Record.builder() .name(RECORD_ELECTRONIC_BINARY) .nodeType(CONTENT_TYPE) .build(); - String binaryRecordId = filePlanComponentAPI.createElectronicRecord(recordBinary, IMAGE_FILE, folderId).getId(); + String binaryRecordId = recordFolderAPI.createRecord(recordBinary, folderId, getFile(IMAGE_FILE)).getId(); // binary content, therefore compare respective SHA1 checksums in order to verify this is identical content try ( InputStream recordContentStream = recordsAPI.getRecordContent(binaryRecordId).asInputStream(); - FileInputStream localFileStream = new FileInputStream(new File(Resources.getResource(IMAGE_FILE).getFile())); + FileInputStream localFileStream = new FileInputStream(getFile(IMAGE_FILE)); ) { assertEquals(DigestUtils.sha1(recordContentStream), DigestUtils.sha1(localFileStream)); @@ -255,11 +285,11 @@ public class ReadRecordTests extends BaseRMRestTest assertStatusCode(OK); // electronic record with no content - FilePlanComponent recordNoContent = FilePlanComponent.builder() + Record recordNoContent = Record.builder() .name(RECORD_ELECTRONIC) .nodeType(CONTENT_TYPE) .build(); - String recordNoContentId = filePlanComponentAPI.createFilePlanComponent(recordNoContent,folderId).getId(); + String recordNoContentId = recordFolderAPI.createRecord(recordNoContent,folderId).getId(); assertTrue(recordsAPI.getRecordContent(recordNoContentId).asString().isEmpty()); assertStatusCode(OK); } @@ -273,18 +303,17 @@ public class ReadRecordTests extends BaseRMRestTest { String NONELECTRONIC_RECORD_NAME = "Record nonelectronic" + getRandomAlphanumeric(); - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - FilePlanComponent record = FilePlanComponent.builder() - .name(NONELECTRONIC_RECORD_NAME) - .nodeType(NON_ELECTRONIC_RECORD_TYPE) - .relativePath("/" + CATEGORY_NAME + getRandomAlphanumeric() + "/" + FOLDER_NAME) - .build(); + String folderId = createCategoryFolderInFilePlan().getId(); + Record record = Record.builder() + .name(NONELECTRONIC_RECORD_NAME) + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .build(); - String nonElectronicRecord = filePlanComponentAPI.createFilePlanComponent(record, FILE_PLAN_ALIAS).getId(); + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + String nonElectronicRecord = recordFolderAPI.createRecord(record, folderId).getId(); - - assertTrue(getRestAPIFactory().getRecordsAPI().getRecordContent(nonElectronicRecord).asString().isEmpty()); - assertStatusCode(OK); + getRestAPIFactory().getRecordsAPI().getRecordContent(nonElectronicRecord); + assertStatusCode(BAD_REQUEST); } /** @@ -292,10 +321,18 @@ public class ReadRecordTests extends BaseRMRestTest * When I try to read the content * Then I receive an error */ + @DataProvider(name="noContentNodes") + public String[][] getNonRecordTypes() throws Exception + { + return new String[][] { + { getFilePlan(FILE_PLAN_ALIAS).getId() }, + { getTransferContainer(TRANSFERS_ALIAS).getId() }, + { createCategoryFolderInFilePlan().getParentId()} + }; + } @Test ( - dataProvider = "getContainers", - dataProviderClass = TestData.class, + dataProvider = "noContentNodes", description = "Reading records from invalid containers" ) public void readContentFromInvalidContainers(String container) throws Exception @@ -305,64 +342,49 @@ public class ReadRecordTests extends BaseRMRestTest } /** - * Given a container that is a record/unfiled folder + * Given a container that is a record folder * When I try to record the containers records - * Then I receive a list of all the records contained within the record/unfiled folder + * Then I receive a list of all the records contained within the record folder */ - - /** Valid root containers where electronic and non-electronic records can be created */ - @DataProvider (name = "folderContainers") - public Object[][] getFolderContainers() throws Exception - { - return new Object[][] { - // an arbitrary record folder - { createCategoryFolderInFilePlan().getId()}, - // an arbitrary unfiled records folder - { createUnfiledRecordsFolder(UNFILED_RECORDS_CONTAINER_ALIAS.toString(), "Unfiled Folder " + getRandomAlphanumeric()).getId() } - }; - } - @Test - ( - dataProvider ="folderContainers", - description ="List the records from record folder/unfiled record folder" - ) - public void readRecordsFromFolders(String containerId) throws Exception + public void readRecordsFromRecordFolder() throws Exception { final int NUMBER_OF_RECORDS = 5; - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); + String containerId = createCategoryFolderInFilePlan().getId(); + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); // Create Electronic Records - ArrayList children = new ArrayList(); + ArrayList children = new ArrayList(); for (int i = 0; i < NUMBER_OF_RECORDS; i++) { //build the electronic record - FilePlanComponent record = FilePlanComponent.builder() - .name(ELECTRONIC_RECORD_NAME +i) - .nodeType(CONTENT_TYPE) - .build(); + Record record = Record.builder() + .name(ELECTRONIC_RECORD_NAME + i) + .nodeType(CONTENT_TYPE) + .build(); //create a child - FilePlanComponent child = filePlanComponentAPI.createElectronicRecord(record, createTempFile(ELECTRONIC_RECORD_NAME + i, ELECTRONIC_RECORD_NAME + i ), containerId); + Record child = recordFolderAPI.createRecord(record, containerId, createTempFile(ELECTRONIC_RECORD_NAME + i, ELECTRONIC_RECORD_NAME + i )); + children.add(child); } //Create NonElectronicRecords for (int i = 0; i < NUMBER_OF_RECORDS; i++) { - FilePlanComponent nonelectronicRecord = FilePlanComponent.builder() - .properties(FilePlanComponentProperties.builder() - .description("Description") - .title("Title") - .build()) - .name(NONELECTRONIC_RECORD_NAME+i) - .nodeType(NON_ELECTRONIC_RECORD_TYPE) - .build(); + Record nonelectronicRecord = Record.builder() + .properties(RecordProperties.builder() + .description("Description") + .title("Title") + .build()) + .name(NONELECTRONIC_RECORD_NAME+i) + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .build(); //create records - FilePlanComponent child = filePlanComponentAPI.createFilePlanComponent(nonelectronicRecord, containerId); + Record child = recordFolderAPI.createRecord(nonelectronicRecord, containerId); + children.add(child); } // List children from API - FilePlanComponentsCollection apiChildren = - (FilePlanComponentsCollection) filePlanComponentAPI.listChildComponents(containerId).assertThat().entriesListIsNotEmpty(); + RecordFolderCollection apiChildren = (RecordFolderCollection) recordFolderAPI.getRecordFolderChildren(containerId).assertThat().entriesListIsNotEmpty(); // Check status code assertStatusCode(OK); @@ -370,106 +392,123 @@ public class ReadRecordTests extends BaseRMRestTest // Check listed children against created list apiChildren.getEntries().forEach(c -> + { + Record record = c.getEntry(); + assertNotNull(record.getId()); + logger.info("Checking child " + record.getId()); + + try { - FilePlanComponent filePlanComponent = c.getFilePlanComponentModel(); - assertNotNull(filePlanComponent.getId()); - logger.info("Checking child " + filePlanComponent.getId()); + // Find this child in created children list + Record createdComponent = children.stream() + .filter(child -> child.getId().equals(record.getId())) + .findFirst() + .get(); - try - { - // Find this child in created children list - FilePlanComponent createdComponent = children.stream() - .filter(child -> child.getId().equals(filePlanComponent.getId())) - .findFirst() - .get(); + // Created by + assertEquals(record.getCreatedByUser().getId(), getAdminUser().getUsername()); - // Created by - assertEquals(filePlanComponent.getCreatedByUser().getId(), getAdminUser().getUsername()); + // Is parent Id set correctly + assertEquals(record.getParentId(), containerId); - // Is parent Id set correctly - assertEquals(filePlanComponent.getParentId(), containerId); - assertTrue(filePlanComponent.getIsFile()); + //check the record name + assertTrue(record.getName().equals(createdComponent.getName())); + assertTrue(createdComponent.getName().contains(createdComponent.getProperties().getIdentifier())); + assertEquals(createdComponent.getNodeType(), record.getNodeType()); - // Boolean properties related to node type - assertFalse(filePlanComponent.getIsRecordFolder()); - assertFalse(filePlanComponent.getIsCategory()); - - //check the record name - assertTrue(filePlanComponent.getName().equals(createdComponent.getName())); - assertTrue(createdComponent.getName().contains(createdComponent.getProperties().getRmIdentifier())); - assertEquals(createdComponent.getNodeType(), filePlanComponent.getNodeType()); - - } - catch (NoSuchElementException e) - { - fail("No child element for " + filePlanComponent.getId()); - } } - ); + catch (NoSuchElementException e) + { + fail("No child element for " + record.getId()); + } + }); } /** - * Given a record - * When I try to read the children - * Then I receive error + * Given a container that is a unfiled record folder + * When I try to record the containers records + * Then I receive a list of all the records contained within the unfiled record folder */ @Test - public void readChildrenOnRecords() throws Exception + public void readRecordsFromUnfiledRecordFolder() throws Exception { - String RELATIVE_PATH = "CATEGORY" + getRandomAlphanumeric() + "/FOLDER"; - FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - FilePlanComponent electRecord = FilePlanComponent.builder() - .name(ELECTRONIC_RECORD_NAME) - .nodeType(CONTENT_TYPE) - .content(FilePlanComponentContent.builder().mimeType("text/plain").build()) - .build(); - FilePlanComponent nonElectronic = FilePlanComponent.builder() - .properties(FilePlanComponentProperties.builder() - .description(NONELECTRONIC_RECORD_NAME) - .title("Title") - .build()) - .name(NONELECTRONIC_RECORD_NAME) - .nodeType(NON_ELECTRONIC_RECORD_TYPE) - .build(); + final int NUMBER_OF_RECORDS = 5; + String containerId = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, "Unfiled Folder " + getRandomAlphanumeric(), UNFILED_RECORD_FOLDER_TYPE).getId(); + //we have unfiled record folder + UnfiledRecordFolderAPI unfiledRecordFoldersAPI = getRestAPIFactory().getUnfiledRecordFoldersAPI(); + // Create Electronic Records + ArrayList children = new ArrayList(); + for (int i = 0; i < NUMBER_OF_RECORDS; i++) + { + //build the electronic record + UnfiledContainerChild record = UnfiledContainerChild.builder() + .name(ELECTRONIC_RECORD_NAME + i) + .nodeType(CONTENT_TYPE) + .build(); + //create a child + UnfiledContainerChild child = unfiledRecordFoldersAPI.uploadRecord(record, containerId, createTempFile(ELECTRONIC_RECORD_NAME + i, ELECTRONIC_RECORD_NAME + i )); - //create records in Unfiled Container - FilePlanComponent recordElecInUnfiled = filePlanComponentAPI.createFilePlanComponent(electRecord, UNFILED_RECORDS_CONTAINER_ALIAS); - FilePlanComponent recordNonElecInUnfiled = filePlanComponentAPI.createFilePlanComponent(nonElectronic, UNFILED_RECORDS_CONTAINER_ALIAS); + children.add(child); + } + //Create NonElectronicRecords + for (int i = 0; i < NUMBER_OF_RECORDS; i++) + { + UnfiledContainerChild nonelectronicRecord = UnfiledContainerChild.builder() + .properties(UnfiledContainerChildProperties.builder() + .description("Description") + .title("Title") + .build()) + .name(NONELECTRONIC_RECORD_NAME+i) + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .build(); + //create records + UnfiledContainerChild child = unfiledRecordFoldersAPI.createUnfiledRecordFolderChild(nonelectronicRecord, containerId); + + children.add(child); + } + + // List children from API + UnfiledContainerChildCollection apiChildren = (UnfiledContainerChildCollection) unfiledRecordFoldersAPI.getUnfiledRecordFolderChildren(containerId).assertThat().entriesListIsNotEmpty(); - // List children for the electronic Record - filePlanComponentAPI.listChildComponents(recordElecInUnfiled.getId(), "where=(isFile=true)") - //check the list returned is empty - .assertThat().entriesListIsEmpty().assertThat().paginationExist(); // Check status code assertStatusCode(OK); - // List children for the nonElectronic Record - filePlanComponentAPI.listChildComponents(recordNonElecInUnfiled.getId(), "where=(isFile=true)") - //check the list returned is empty - .assertThat().entriesListIsEmpty().assertThat().paginationExist(); - // Check status code - assertStatusCode(OK); - //Update the Records objects - electRecord.setRelativePath(RELATIVE_PATH); - nonElectronic.setRelativePath(RELATIVE_PATH); + // Check listed children against created list + apiChildren.getEntries().forEach(c -> + { + UnfiledContainerChild record = c.getEntry(); + assertNotNull(record.getId()); + logger.info("Checking child " + record.getId()); - //create records in Unfiled Container - FilePlanComponent recordElecFromRecordFolder = filePlanComponentAPI.createFilePlanComponent(electRecord, FILE_PLAN_ALIAS); - FilePlanComponent recordNonElecFromRecordFolder = filePlanComponentAPI.createFilePlanComponent(nonElectronic, FILE_PLAN_ALIAS); + try + { + // Find this child in created children list + UnfiledContainerChild createdComponent = children.stream() + .filter(child -> child.getId().equals(record.getId())) + .findFirst() + .get(); - // List children for the electronic Record - filePlanComponentAPI.listChildComponents(recordElecFromRecordFolder.getId(), "where=(isFile=true)") - //check the list returned is empty - .assertThat().entriesListIsEmpty().assertThat().paginationExist(); - // Check status code - assertStatusCode(OK); + // Created by + assertEquals(record.getCreatedByUser().getId(), getAdminUser().getUsername()); - // List children for the nonElectronic Record - getRestAPIFactory().getFilePlanComponentsAPI().listChildComponents(recordNonElecFromRecordFolder.getId(), "where=(isFile=true)") - //check the list returned is empty - .assertThat().entriesListIsEmpty().assertThat().paginationExist(); - // Check status code - assertStatusCode(OK); + // Is parent Id set correctly + assertEquals(record.getParentId(), containerId); + assertTrue(record.getIsRecord()); + + // Boolean properties related to node type + assertFalse(record.getIsUnfiledRecordFolder()); + + //check the record name + assertTrue(record.getName().equals(createdComponent.getName())); + assertTrue(createdComponent.getName().contains(createdComponent.getProperties().getIdentifier())); + assertEquals(createdComponent.getNodeType(), record.getNodeType()); + + } + catch (NoSuchElementException e) + { + fail("No child element for " + record.getId()); + } + }); } } diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordCategoryTest.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordCategoryTest.java deleted file mode 100644 index 51483fffd3..0000000000 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordCategoryTest.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * #%L - * Alfresco Records Management Module - * %% - * Copyright (C) 2005 - 2017 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * - - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.rest.rm.community.fileplancomponents; - -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE; -import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; -import static org.springframework.http.HttpStatus.CREATED; -import static org.springframework.http.HttpStatus.NOT_FOUND; -import static org.springframework.http.HttpStatus.NO_CONTENT; -import static org.springframework.http.HttpStatus.OK; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -import java.util.ArrayList; -import java.util.NoSuchElementException; - -import org.alfresco.rest.rm.community.base.BaseRMRestTest; -import org.alfresco.rest.rm.community.base.TestData; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentsCollection; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI; -import org.alfresco.utility.report.Bug; -import org.testng.annotations.Test; - -/** - * Record category related API tests - * - * @author Kristijan Conkas - * @author Tuna Aksoy - * @since 2.6 - */ -public class RecordCategoryTest extends BaseRMRestTest -{ - // Number of children (for children creation test) - private static final int NUMBER_OF_CHILDREN = 10; - - /** - *

-     * Given that a file plan exists
-     * When I ask the API to create a root record category
-     * Then it is created as a root record category
-     *
-     *
-     * Given that a file plan exists
-     * When I use the API to create a folder (cm:folder type) into the fileplan
-     * Then the folder is converted to rma:recordCategory
-     * (see RM-4572 comments)
-     *
-     */
-    @Test
-    (
-        description = "Create root category",
-        dataProviderClass= TestData.class,
-        dataProvider = "categoryTypes"
-    )
-    public void createCategoryTest(String nodeType) throws Exception
-    {
-        String categoryName = "Category name " + getRandomAlphanumeric();
-        String categoryTitle = "Category title " + getRandomAlphanumeric();
-
-        // Build the record category properties
-        FilePlanComponent recordCategory = FilePlanComponent.builder()
-            .name(categoryName)
-            .nodeType(nodeType)
-            .properties(
-                    FilePlanComponentProperties.builder()
-                        .title(categoryTitle)
-                        .build())
-            .build();
-
-        // Create the record category
-        FilePlanComponent filePlanComponent = getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(recordCategory, FILE_PLAN_ALIAS);
-
-        // Verify the status code
-        assertStatusCode(CREATED);
-
-        // Verify the returned file plan component
-        assertTrue(filePlanComponent.getIsCategory());
-        assertFalse(filePlanComponent.getIsFile());
-        assertFalse(filePlanComponent.getIsRecordFolder());
-
-        assertEquals(filePlanComponent.getName(), categoryName);
-        assertEquals(filePlanComponent.getNodeType(), RECORD_CATEGORY_TYPE);
-
-        assertEquals(filePlanComponent.getCreatedByUser().getId(), getAdminUser().getUsername());
-
-        // Verify the returned file plan component properties
-        FilePlanComponentProperties filePlanComponentProperties = filePlanComponent.getProperties();
-        assertEquals(filePlanComponentProperties.getTitle(), categoryTitle);
-        assertNotNull(filePlanComponentProperties.getRmIdentifier());
-        logger.info("Aspects: " + filePlanComponent.getAspectNames());
-    }
-
-    /**
-     * 
-     * Given that a record category exists
-     * When I ask the API to update the details of the record category
-     * Then the details of the record category are updated
-     */
-    @Test
-    (
-        description = "Rename root category"
-    )
-    public void renameCategory() throws Exception
-    {
-        // Create record category first
-        String categoryName = "Category name " + getRandomAlphanumeric();
-        String categoryTitle = "Category title " + getRandomAlphanumeric();
-
-        // Build the record category properties
-        FilePlanComponent recordCategory = FilePlanComponent.builder()
-            .name(categoryName)
-            .nodeType(RECORD_CATEGORY_TYPE)
-            .properties(
-                    FilePlanComponentProperties.builder()
-                        .title(categoryTitle)
-                        .build())
-            .build();
-
-        // Create the record category
-        FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI();
-        FilePlanComponent filePlanComponent = filePlanComponentsAPI.createFilePlanComponent(recordCategory, FILE_PLAN_ALIAS);
-
-        String newCategoryName = "Rename " + categoryName;
-
-        // Build the properties which will be updated
-        FilePlanComponent recordCategoryUpdated = FilePlanComponent.builder().name(newCategoryName).build();
-
-        // Update the record category
-        FilePlanComponent renamedFilePlanComponent = filePlanComponentsAPI.updateFilePlanComponent(recordCategoryUpdated, filePlanComponent.getId());
-
-        // Verify the status code
-        assertStatusCode(OK);
-
-        // Verify the returned file plan component
-        assertEquals(renamedFilePlanComponent.getName(), newCategoryName);
-
-        // Get actual FILE_PLAN_ALIAS id
-        FilePlanComponent parentComponent = filePlanComponentsAPI.getFilePlanComponent(FILE_PLAN_ALIAS);
-
-        // verify renamed component still has this parent
-        assertEquals(renamedFilePlanComponent.getParentId(), parentComponent.getId());
-    }
-
-    /**
-     * 
-     * Given that a record category exists
-     * When I ask the API to delete the record category
-     * Then the record category and all its contents is deleted
-     */
-    @Test
-    (
-        description = "Delete category"
-    )
-    public void deleteCategory() throws Exception
-    {
-        // Create record category first
-        String categoryName = "Category name " + getRandomAlphanumeric();
-        String categoryTitle = "Category title " + getRandomAlphanumeric();
-
-        // Build the record category properties
-        FilePlanComponent recordCategory = FilePlanComponent.builder()
-                .name(categoryName)
-                .nodeType(RECORD_CATEGORY_TYPE)
-                .properties(
-                        FilePlanComponentProperties.builder()
-                            .title(categoryTitle)
-                            .build())
-                .build();
-
-        // Create the record category
-        FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI();
-        FilePlanComponent filePlanComponent = filePlanComponentsAPI.createFilePlanComponent(recordCategory, FILE_PLAN_ALIAS);
-
-        // Delete the record category
-        filePlanComponentsAPI.deleteFilePlanComponent(filePlanComponent.getId());
-
-        // Verify the status code
-        assertStatusCode(NO_CONTENT);
-
-        // Deleted component should no longer be retrievable
-        filePlanComponentsAPI.getFilePlanComponent(filePlanComponent.getId());
-        assertStatusCode(NOT_FOUND);
-    }
-
-    /**
-     * 
-     * Given that a record category exists
-     * When I ask the API to create a record category
-     * Then it is created within the record category
-     */
-    @Test
-    (
-        description = "Create child category"
-    )
-    public void createSubcategory() throws Exception
-    {
-        // Create root level category
-        FilePlanComponent rootCategory = createCategory(FILE_PLAN_ALIAS, getRandomAlphanumeric());
-        assertNotNull(rootCategory.getId());
-
-        // Create subcategory as a child of rootCategory
-        FilePlanComponent childCategory = createCategory(rootCategory.getId(), getRandomAlphanumeric());
-
-        // Child category created?
-        assertNotNull(childCategory.getId());
-
-        // Verify child category
-        assertEquals(childCategory.getParentId(), rootCategory.getId());
-        assertTrue(childCategory.getIsCategory());
-        assertFalse(childCategory.getIsFile());
-        assertFalse(childCategory.getIsRecordFolder());
-        assertEquals(childCategory.getNodeType(), RECORD_CATEGORY_TYPE);
-    }
-
-    /**
-     * 
-     * Given that a record category exists
-     * And contains a number of record categories and record folders
-     * When I ask the APi to get me the children of the record category
-     * Then I am returned the contained record categories and record folders
-     * And their details
-     */
-    @Test
-    (
-        description = "List children of a category"
-    )
-    public void listChildren() throws Exception
-    {
-        // Create root level category
-        FilePlanComponent rootCategory = createCategory(FILE_PLAN_ALIAS, getRandomAlphanumeric());
-        assertNotNull(rootCategory.getId());
-
-        // Add child categories/folders
-        ArrayList children = new ArrayList();
-        for (int i=0; i < NUMBER_OF_CHILDREN; i++)
-        {
-            // Create a child
-            FilePlanComponent child = createComponent(rootCategory.getId(),
-                getRandomAlphanumeric(),
-                // half of the children should be subcategories, the other subfolders
-                (i <= NUMBER_OF_CHILDREN / 2) ? RECORD_CATEGORY_TYPE : RECORD_FOLDER_TYPE);
-            assertNotNull(child.getId());
-            children.add(child);
-        }
-
-        // List children from API
-        FilePlanComponentsCollection apiChildren = getRestAPIFactory().getFilePlanComponentsAPI().listChildComponents(rootCategory.getId());
-
-        // Check status code
-        assertStatusCode(OK);
-        logger.info("parent: " + rootCategory.getId());
-
-        // Check listed children against created list
-        apiChildren.getEntries().forEach(c ->
-        {
-            FilePlanComponent filePlanComponent = c.getFilePlanComponentModel();
-            assertNotNull(filePlanComponent.getId());
-            logger.info("Checking child " + filePlanComponent.getId());
-
-            try
-            {
-                // Find this child in created children list
-                FilePlanComponent createdComponent = children.stream()
-                    .filter(child -> child.getId().equals(filePlanComponent.getId()))
-                    .findFirst()
-                    .get();
-
-                // Created by
-                assertEquals(filePlanComponent.getCreatedByUser().getId(), getAdminUser().getUsername());
-
-                // Is parent Id set correctly?
-                assertEquals(filePlanComponent.getParentId(), rootCategory.getId());
-
-                // Only categories or folders have been created
-                assertFalse(filePlanComponent.getIsFile());
-
-                // Boolean properties related to node type
-                // Only RECORD_CATEGORY_TYPE and RECORD_FOLDER_TYPE have been created
-                if (filePlanComponent.getNodeType().equals(RECORD_CATEGORY_TYPE))
-                {
-                    assertTrue(filePlanComponent.getIsCategory());
-                    assertFalse(filePlanComponent.getIsRecordFolder());
-                }
-                else
-                {
-                    assertTrue(filePlanComponent.getIsRecordFolder());
-                    assertFalse(filePlanComponent.getIsCategory());
-                }
-
-                // Does returned object have the same contents as the created one?
-                assertEquals(createdComponent.getName(), filePlanComponent.getName());
-                assertEquals(createdComponent.getNodeType(), filePlanComponent.getNodeType());
-
-                // Verify properties
-                // FIXME: Verify properties
-                assertNotNull(createdComponent.getProperties().getRmIdentifier());
-            }
-            catch (NoSuchElementException e)
-            {
-                fail("No child element for " + filePlanComponent.getId());
-            }
-        });
-    }
-
-    /**
-     * Given that a record category exists
-     * When I ask to create a  object type which is not  a record category or a record folder as a child
-     * Then the children are not created  and the 422 response code is returned
-     */
-    @Test
-    (
-        description = "Create node types not allowed inside a category",
-        dataProviderClass = TestData.class,
-        dataProvider = "childrenNotAllowedForCategory"
-
-    )
-    @Bug (id="RM-4367, RM-4572")
-    public void createTypesNotAllowedInCategory(String nodeType) throws Exception
-    {
-        String COMPONENT_NAME = "Component"+getRandomAlphanumeric();
-
-        //Create the category
-        FilePlanComponent category = createCategory(FILE_PLAN_ALIAS, COMPONENT_NAME);
-
-        //Build node  properties
-        FilePlanComponent recordCategory = FilePlanComponent.builder()
-                .name(COMPONENT_NAME)
-                .nodeType(nodeType)
-                .properties(
-                        FilePlanComponentProperties.builder()
-                            .title("Title for " + COMPONENT_NAME)
-                            .build())
-                .build();
-
-        //create the invalid node type
-        getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(recordCategory, category.getId());
-        assertStatusCode(UNPROCESSABLE_ENTITY);
-    }
-
-
-    /**
-     * Helper method to create child category
-     *
-     * @param parentCategoryId The id of the parent category
-     * @param categoryName The name of the category
-     * @return The created category
-     * @throws Exception on unsuccessful component creation
-     */
-    public FilePlanComponent createCategory(String parentCategoryId, String categoryName) throws Exception
-    {
-        return createComponent(parentCategoryId, categoryName, RECORD_CATEGORY_TYPE);
-    }
-
-    /**
-     * Helper method to create generic child component
-     *
-     * @param parentComponentId The id of the parent file plan component
-     * @param componentName The name of the file plan component
-     * @param componentType The name of the file plan component
-     * @return The created file plan component
-     * @throws Exception
-     */
-    private FilePlanComponent createComponent(String parentComponentId, String componentName, String componentType) throws Exception
-    {
-        // Build node  properties
-        FilePlanComponent component = FilePlanComponent.builder()
-                .name(componentName)
-                .nodeType(componentType)
-                .properties(FilePlanComponentProperties.builder()
-                        .title("Title for " + componentName)
-                        .build())
-                .build();
-
-        FilePlanComponent filePlanComponent = getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(component, parentComponentId);
-        assertStatusCode(CREATED);
-
-        return filePlanComponent;
-    }
-}
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordCategoryTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordCategoryTests.java
new file mode 100644
index 0000000000..0ee2456877
--- /dev/null
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordCategoryTests.java
@@ -0,0 +1,331 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.rm.community.fileplancomponents;
+
+import static org.alfresco.rest.rm.community.base.TestData.RECORD_CATEGORY_NAME;
+import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
+import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE;
+import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE;
+import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
+import static org.springframework.http.HttpStatus.CREATED;
+import static org.springframework.http.HttpStatus.NOT_FOUND;
+import static org.springframework.http.HttpStatus.NO_CONTENT;
+import static org.springframework.http.HttpStatus.OK;
+import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.alfresco.rest.rm.community.base.BaseRMRestTest;
+import org.alfresco.rest.rm.community.base.TestData;
+import org.alfresco.rest.rm.community.model.fileplan.FilePlan;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChildCollection;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryProperties;
+import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI;
+import org.alfresco.utility.report.Bug;
+import org.testng.annotations.Test;
+
+/**
+ * Record category related API tests
+ *
+ * @author Kristijan Conkas
+ * @author Tuna Aksoy
+ * @since 2.6
+ */
+public class RecordCategoryTests extends BaseRMRestTest
+{
+    /** Number of children (for children creation test) */
+    private static final int NUMBER_OF_CHILDREN = 10;
+
+    /**
+     * 
+     * Given that a file plan exists
+     * When I ask the API to create a root record category
+     * Then it is created as a root record category
+     * 
+ *
+     * Given that a file plan exists
+     * When I use the API to create a folder (cm:folder type) into the fileplan
+     * Then the folder is converted to rma:recordCategory
+     * (see RM-4572 comments)
+     * 
+ */ + @Test + ( + description = "Create root category", + dataProviderClass= TestData.class, + dataProvider = "categoryTypes" + ) + public void createCategoryTest(String nodeType) throws Exception + { + String categoryName = "Category name " + getRandomAlphanumeric(); + String categoryTitle = "Category title " + getRandomAlphanumeric(); + + // Create the root record category + RecordCategory rootRecordCategory = createRootCategory(categoryName, categoryTitle); + + // Verify the status code + assertStatusCode(CREATED); + + assertEquals(rootRecordCategory.getName(), categoryName); + assertEquals(rootRecordCategory.getNodeType(), RECORD_CATEGORY_TYPE); + + assertEquals(rootRecordCategory.getCreatedByUser().getId(), getAdminUser().getUsername()); + + // Verify the returned root record category properties + RecordCategoryProperties rootRecordCategoryProperties = rootRecordCategory.getProperties(); + assertEquals(rootRecordCategoryProperties.getTitle(), categoryTitle); + assertNotNull(rootRecordCategoryProperties.getIdentifier()); + logger.info("Aspects: " + rootRecordCategory.getAspectNames()); + } + + /** + *
+     * Given that a record category exists
+     * When I ask the API to update the details of the record category
+     * Then the details of the record category are updated
+     * 
+ */ + @Test + ( + description = "Rename root category" + ) + public void renameCategory() throws Exception + { + // Create record category first + String categoryName = "Category name " + getRandomAlphanumeric(); + String categoryTitle = "Category title " + getRandomAlphanumeric(); + + // Create the root record category + RecordCategory rootRecordCategory = createRootCategory(categoryName, categoryTitle); + + String newCategoryName = "Rename " + categoryName; + + // Build the properties which will be updated + RecordCategory recordCategoryUpdated = RecordCategory.builder().name(newCategoryName).build(); + + // Update the record category + RecordCategory renamedRecordCategory = getRestAPIFactory().getRecordCategoryAPI().updateRecordCategory(recordCategoryUpdated, rootRecordCategory.getId()); + + // Verify the status code + assertStatusCode(OK); + + // Verify the returned file plan component + assertEquals(renamedRecordCategory.getName(), newCategoryName); + + // Get actual FILE_PLAN_ALIAS id + FilePlan filePlan = getRestAPIFactory().getFilePlansAPI().getFilePlan(FILE_PLAN_ALIAS); + + // verify renamed component still has this parent + assertEquals(renamedRecordCategory.getParentId(), filePlan.getId()); + } + + /** + *
+     * Given that a record category exists
+     * When I ask the API to delete the record category
+     * Then the record category and all its contents are deleted
+     * 
+ */ + @Test + ( + description = "Delete category" + ) + public void deleteCategory() throws Exception + { + // Create record category first + String categoryName = "Category name " + getRandomAlphanumeric(); + String categoryTitle = "Category title " + getRandomAlphanumeric(); + + // Create the root record category + RecordCategory rootRecordCategory = createRootCategory(categoryName, categoryTitle); + + // Delete the record category + RecordCategoryAPI recordCategoryAPI = getRestAPIFactory().getRecordCategoryAPI(); + String recordCategoryId = rootRecordCategory.getId(); + recordCategoryAPI.deleteRecordCategory(recordCategoryId); + + // Verify the status code + assertStatusCode(NO_CONTENT); + + // Deleted component should no longer be retrievable + recordCategoryAPI.getRecordCategory(recordCategoryId); + assertStatusCode(NOT_FOUND); + } + + /** + *
+     * Given that a record category exists
+     * When I ask the API to create a record category
+     * Then it is created within the record category
+     * 
+ */ + @Test + ( + description = "Create child category" + ) + public void createSubcategory() throws Exception + { + // Create root level category + RecordCategory rootCategory = createRootCategory(getRandomAlphanumeric()); + assertNotNull(rootCategory.getId()); + + // Create sub-category as a child of rootCategory + RecordCategoryChild recordCategory = createRecordCategoryChild(rootCategory.getId(), RECORD_CATEGORY_NAME, RECORD_CATEGORY_TYPE); + + // Child category created? + assertNotNull(recordCategory.getId()); + + // Verify child category + assertEquals(recordCategory.getParentId(), rootCategory.getId()); + assertTrue(recordCategory.getIsRecordCategory()); + assertFalse(recordCategory.getIsRecordFolder()); + assertEquals(recordCategory.getNodeType(), RECORD_CATEGORY_TYPE); + } + + /** + *
+     * Given that a record category exists
+     * And contains a number of record categories and record folders
+     * When I ask the API to get me the children of the record category
+     * Then I am returned the contained record categories and record folders and their details
+     * 
+ */ + @Test + ( + description = "Get children of a record category" + ) + public void getRecordCategoryChildren() throws Exception + { + // Create root level category + RecordCategory rootRecordCategory = createRootCategory(getRandomAlphanumeric()); + assertNotNull(rootRecordCategory.getId()); + + // Add record category children + List children = new ArrayList(); + for (int i=0; i < NUMBER_OF_CHILDREN; i++) + { + // Create a record category child + RecordCategoryChild child = createRecordCategoryChild(rootRecordCategory.getId(), + getRandomAlphanumeric(), + // half of the children should be sub-categories, the other sub-folders + (i <= NUMBER_OF_CHILDREN / 2) ? RECORD_CATEGORY_TYPE : RECORD_FOLDER_TYPE); + assertNotNull(child.getId()); + children.add(child); + } + + // Get children from API + RecordCategoryChildCollection recordCategoryChildren = getRestAPIFactory().getRecordCategoryAPI().getRecordCategoryChildren(rootRecordCategory.getId(),"include=isRecordCategory,isRecordFolder"); + + // Check status code + assertStatusCode(OK); + logger.info("Parent: " + rootRecordCategory.getId()); + + // Check listed children against created list + recordCategoryChildren.getEntries().forEach(c -> + { + RecordCategoryChild recordCategoryChild = c.getEntry(); + String recordCategoryChildId = recordCategoryChild.getId(); + + assertNotNull(recordCategoryChildId); + logger.info("Checking child " + recordCategoryChildId); + + try + { + // Find this child in created children list + RecordCategoryChild createdComponent = children.stream() + .filter(child -> child.getId().equals(recordCategoryChildId)) + .findFirst() + .get(); + + // Created by + assertEquals(recordCategoryChild.getCreatedByUser().getId(), getAdminUser().getUsername()); + + // Is parent id set correctly? + assertEquals(recordCategoryChild.getParentId(), rootRecordCategory.getId()); + + // Boolean properties related to node type + // Only RECORD_CATEGORY_TYPE and RECORD_FOLDER_TYPE have been created + if (recordCategoryChild.getNodeType().equals(RECORD_CATEGORY_TYPE)) + { + assertTrue(recordCategoryChild.getIsRecordCategory()); + assertFalse(recordCategoryChild.getIsRecordFolder()); + } + else + { + assertTrue(recordCategoryChild.getIsRecordFolder()); + assertFalse(recordCategoryChild.getIsRecordCategory()); + } + + // Does returned object have the same contents as the created one? + assertEquals(createdComponent.getName(), recordCategoryChild.getName()); + assertEquals(createdComponent.getNodeType(), recordCategoryChild.getNodeType()); + + // FIXME: Verify properties + assertNotNull(createdComponent.getProperties().getIdentifier()); + } + catch (NoSuchElementException e) + { + fail("No child element for " + recordCategoryChildId); + } + }); + } + + /** + *
+     * Given that a record category exists
+     * When I ask to create an object type which is not a record category or a record folder as a child
+     * Then the children are not created and the 422 response code is returned
+     * 
+ */ + @Test + ( + description = "Create node types not allowed inside a category", + dataProviderClass = TestData.class, + dataProvider = "childrenNotAllowedForCategory" + ) + @Bug (id="RM-4367, RM-4572") + public void createTypesNotAllowedInCategory(String nodeType) throws Exception + { + String componentName = "Component" + getRandomAlphanumeric(); + + // Create the category + RecordCategory rootRecordCategory = createRootCategory(componentName); + + // Create the invalid node type + createRecordCategoryChild(rootRecordCategory.getId(), componentName, nodeType); + assertStatusCode(UNPROCESSABLE_ENTITY); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordFolderTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordFolderTests.java index 3f0737b925..92d1da8749 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordFolderTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/RecordFolderTests.java @@ -26,43 +26,51 @@ */ package org.alfresco.rest.rm.community.fileplancomponents; -import static org.alfresco.rest.rm.community.base.TestData.CATEGORY_NAME; -import static org.alfresco.rest.rm.community.base.TestData.FOLDER_NAME; -import static org.alfresco.rest.rm.community.base.TestData.FOLDER_TITLE; +import static java.time.LocalDateTime.now; +import static org.alfresco.rest.rm.community.base.TestData.RECORD_CATEGORY_NAME; +import static org.alfresco.rest.rm.community.base.TestData.RECORD_FOLDER_NAME; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.IS_CLOSED; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PATH; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.TITLE_PREFIX; import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; +import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.NO_CONTENT; import static org.springframework.http.HttpStatus.OK; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.fail; import static org.testng.AssertJUnit.assertTrue; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.NoSuchElementException; import org.alfresco.rest.rm.community.base.BaseRMRestTest; import org.alfresco.rest.rm.community.base.TestData; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentReviewPeriod; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentsCollection; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI; +import org.alfresco.rest.rm.community.model.common.ReviewPeriod; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChildCollection; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChildProperties; +import org.alfresco.rest.rm.community.model.recordfolder.RecordFolder; +import org.alfresco.rest.rm.community.model.recordfolder.RecordFolderProperties; +import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; import org.alfresco.utility.report.Bug; +import org.testng.AssertJUnit; import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; /** - * This class contains the tests for the - * the Record Folder CRUD API + * This class contains the tests for the Record Folder CRUD API * * @author Rodica Sutu * @since 2.6 @@ -70,186 +78,185 @@ import org.testng.annotations.Test; public class RecordFolderTests extends BaseRMRestTest { private static final int NUMBER_OF_FOLDERS = 5; + /** + *
      * Given that a record category exists
      * When I use the API to create a new record folder
      * Then it is created within the record category
-     *
+     * 
+ *
      * Given that a record category exists
      * When I use the API to create a folder (cm:folder type)
      * Then the folder is converted to rma:recordFolder within the record category
      * (see RM-4572 comments)
-     *
+     * 
*/ @Test ( - description = "Create a folder into a record category.", + description = "Create a record folder into a record category.", dataProviderClass = TestData.class, dataProvider = "folderTypes" ) @Bug (id = "RM-4572") public void createFolderTest(String folderType) throws Exception { - String CATEGORY = CATEGORY_NAME + getRandomAlphanumeric(); - // Authenticate with admin user - FilePlanComponent filePlanComponent = createCategory(FILE_PLAN_ALIAS, CATEGORY); - - FilePlanComponent recordFolder = FilePlanComponent.builder() - .name(FOLDER_NAME) - .nodeType(folderType) - .properties(FilePlanComponentProperties.builder() - .title(FOLDER_TITLE) - .build()) - .build(); + RecordCategory rootRecordCategory = createRootCategory(RECORD_CATEGORY_NAME + getRandomAlphanumeric()); // Create the record folder - FilePlanComponent folder = getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(recordFolder, filePlanComponent.getId()); + RecordCategoryChild recordFolder = createRecordCategoryChild(rootRecordCategory.getId(), RECORD_FOLDER_NAME, folderType); + // Assert status code assertStatusCode(CREATED); - // Check folder has been created within the category created - assertEquals(filePlanComponent.getId(),folder.getParentId()); - // Verify the returned properties for the file plan component - record folder - assertFalse(folder.getIsCategory()); - assertFalse(folder.getIsFile()); - assertTrue(folder.getIsRecordFolder()); + // Check record folder has been created within the record category + AssertJUnit.assertEquals(rootRecordCategory.getId(), recordFolder.getParentId()); - assertEquals(folder.getName(), FOLDER_NAME); - assertEquals(folder.getNodeType(), RECORD_FOLDER_TYPE); - assertEquals(folder.getCreatedByUser().getId(), getAdminUser().getUsername()); + // Verify the returned values for the record folder + assertFalse(recordFolder.getIsRecordCategory()); + assertTrue(recordFolder.getIsRecordFolder()); + AssertJUnit.assertEquals(recordFolder.getName(), RECORD_FOLDER_NAME); + AssertJUnit.assertEquals(recordFolder.getNodeType(), RECORD_FOLDER_TYPE); + AssertJUnit.assertEquals(recordFolder.getCreatedByUser().getId(), getAdminUser().getUsername()); - // Verify the returned file plan component properties - FilePlanComponentProperties folderProperties = folder.getProperties(); - assertEquals(folderProperties.getTitle(), FOLDER_TITLE); - assertNotNull(folderProperties.getRmIdentifier()); + // Verify the returned record folder properties + RecordCategoryChildProperties folderProperties = recordFolder.getProperties(); + AssertJUnit.assertEquals(folderProperties.getTitle(), TITLE_PREFIX + RECORD_FOLDER_NAME); + assertNotNull(folderProperties.getIdentifier()); } /** + *
      * Given that RM site is created
-     * When I use the API to create a new record folder into transfers container/holds container/unfiled
+     * When I use the API to create a new record folder into transfers/holds/unfiled containers
      * Then the operation fails
+     * 
*/ @Test ( - description = "Create a folder into hold/transfers/unfiled/file plan container", + description = "Create a record folder into transfers/unfiled/file plan container", dataProviderClass = TestData.class, dataProvider = "getContainers" ) @Bug(id="RM-4327") - public void createFolderIntoSpecialContainers(String filePlanComponent) throws Exception + public void createRecordFolderIntoSpecialContainers(String containerAlias) throws Exception { - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - String componentID = filePlanComponentsAPI.getFilePlanComponent(filePlanComponent).getId(); - - // Build the record category properties - FilePlanComponent recordFolder = FilePlanComponent.builder() - .name(FOLDER_NAME) - .nodeType(RECORD_FOLDER_TYPE) - .properties(FilePlanComponentProperties.builder() - .title(FOLDER_TITLE) - .build()) - .build(); + String containerId; + if (FILE_PLAN_ALIAS.equalsIgnoreCase(containerAlias)) + { + containerId = getRestAPIFactory().getFilePlansAPI().getFilePlan(containerAlias).getId(); + } + else if(TRANSFERS_ALIAS.equalsIgnoreCase(containerAlias)) + { + containerId = getRestAPIFactory().getTransferContainerAPI().getTransferContainer(containerAlias).getId(); + } + else + { + //is unfiled container + containerId = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(containerAlias).getId();; + } // Create a record folder - filePlanComponentsAPI.createFilePlanComponent(recordFolder, componentID); + createRecordFolder(containerId, RECORD_FOLDER_NAME); // Check the API Response code - assertStatusCode(UNPROCESSABLE_ENTITY); + assertStatusCode(BAD_REQUEST); } /** + *
      * Given that a record folder exists
      * When I ask for the details of a record folder
      * Then I am given the details of a record folder
+     * 
*/ @Test ( - description = "Check the details returned for a record folder" + description = "Check the details of a record folder" ) - public void checkTheRecordFolderProperties() throws Exception + public void checkRecordFolderDetails() throws Exception { - String CATEGORY = CATEGORY_NAME + getRandomAlphanumeric(); + // Create a category + RecordCategory rootRecordCategory = createRootCategory(RECORD_CATEGORY_NAME + getRandomAlphanumeric()); - FilePlanComponent category = createCategory(FILE_PLAN_ALIAS, CATEGORY); - FilePlanComponent folder = createFolder(category.getId(),FOLDER_NAME); + // Create a folder + RecordCategoryChild recordCategoryChild = createRecordFolder(rootRecordCategory.getId(), RECORD_FOLDER_NAME); - FilePlanComponent folderDetails = getRestAPIFactory().getFilePlanComponentsAPI().getFilePlanComponent(folder.getId(), "include=" + IS_CLOSED); + // Get the folder including extra information + RecordFolder recordFolder = getRestAPIFactory().getRecordFolderAPI().getRecordFolder(recordCategoryChild.getId(), "include=" + IS_CLOSED); - // Verify the returned properties for the file plan component - record folder - assertEquals(RECORD_FOLDER_TYPE, folderDetails.getNodeType()); - assertTrue(folderDetails.getIsRecordFolder()); - assertFalse(folderDetails.getIsCategory()); - assertFalse(folderDetails.getIsFile()); - assertFalse(folderDetails.getIsClosed()); - - assertEquals(FOLDER_NAME,folderDetails.getName()); - assertEquals(getAdminUser().getUsername(),folderDetails.getCreatedByUser().getId()); - assertEquals(getAdminUser().getUsername(), folderDetails.getModifiedByUser().getId()); - assertEquals(FOLDER_TITLE,folderDetails.getProperties().getTitle()); + // Verify the returned record folder details + AssertJUnit.assertEquals(recordFolder.getNodeType(), RECORD_FOLDER_TYPE); + assertTrue(RECORD_FOLDER_TYPE.equals(recordFolder.getNodeType())); + AssertJUnit.assertEquals(recordFolder.getName(), RECORD_FOLDER_NAME); + AssertJUnit.assertEquals(recordFolder.getCreatedByUser().getId(), getAdminUser().getUsername()); + AssertJUnit.assertEquals(recordFolder.getModifiedByUser().getId(), getAdminUser().getUsername()); + AssertJUnit.assertEquals(recordFolder.getProperties().getTitle(), TITLE_PREFIX + RECORD_FOLDER_NAME); } - /** + *
      * Given that a record folder exists
      * When I use the API to update its details
      * Then the details of the record folder are updated
-     * The above test does treat any  custom metadata
-     * Note: the details of the record folder includes any custom meta-data
+     * The above test does treat any custom metadata
+     * Note: The details of the record folder includes any custom meta-data
+     * 
*/ @Test ( - description = "Update the details returned for a record folder" + description = "Update the details of a record folder" ) - public void updateTheRecordFolderProperties() throws Exception + public void updateRecordFolderDetails() throws Exception { - String CATEGORY = CATEGORY_NAME + getRandomAlphanumeric(); + // Create a record category + RecordCategory rootRecordCategory = createRootCategory(RECORD_CATEGORY_NAME + getRandomAlphanumeric()); - //Create a record category - FilePlanComponent category = createCategory(FILE_PLAN_ALIAS, CATEGORY); - - //Create a record folder - FilePlanComponent folder = createFolder(category.getId(), FOLDER_NAME); + // Create a record folder + RecordCategoryChild recordCategoryChild = createRecordFolder(rootRecordCategory.getId(), RECORD_FOLDER_NAME); // Create record category first String folderDescription = "The folder description is updated" + getRandomAlphanumeric(); String folderName = "The folder name is updated" + getRandomAlphanumeric(); String folderTitle = "Update title " + getRandomAlphanumeric(); - String location = "Location"+getRandomAlphanumeric(); + String location = "Location "+ getRandomAlphanumeric(); - //Create the file plan component properties to update - FilePlanComponent recordFolder = FilePlanComponent.builder() - .name(folderName) - .properties(FilePlanComponentProperties.builder() - .title(folderTitle) - .description(folderDescription) - .vitalRecord(true) - .reviewPeriod(new FilePlanComponentReviewPeriod("month","1")) - .location(location) - .build()) - .build(); + // Create the record folder properties to update + RecordFolder recordFolder = RecordFolder.builder() + .name(folderName) + .properties(RecordFolderProperties.builder() + .title(folderTitle) + .description(folderDescription) + .vitalRecordIndicator(true) + .reviewPeriod(new ReviewPeriod("month","1")) + .location(location) + .build()) + .build(); - // Update the record category - FilePlanComponent folderUpdated = getRestAPIFactory().getFilePlanComponentsAPI().updateFilePlanComponent(recordFolder, folder.getId()); + // Update the record folder + RecordFolder updatedRecordFolder = getRestAPIFactory().getRecordFolderAPI().updateRecordFolder(recordFolder, recordCategoryChild.getId()); // Check the Response Status Code assertStatusCode(OK); - // Verify the returned properties for the file plan component - record folder - assertEquals(folderName, folderUpdated.getName()); - assertEquals(folderDescription, folderUpdated.getProperties().getDescription()); - assertEquals(folderTitle, folderUpdated.getProperties().getTitle()); - assertTrue(folderUpdated.getProperties().getVitalRecord()); - assertEquals(location, folderUpdated.getProperties().getLocation()); - assertNotNull(folderUpdated.getProperties().getReviewPeriod().getPeriodType()); - assertNotNull(folderUpdated.getProperties().getReviewPeriod().getExpression()); - + // Verify the returned details for the record folder + AssertJUnit.assertEquals(updatedRecordFolder.getName(), folderName); + RecordFolderProperties recordFolderProperties = updatedRecordFolder.getProperties(); + AssertJUnit.assertEquals(recordFolderProperties.getDescription(), folderDescription); + AssertJUnit.assertEquals(recordFolderProperties.getTitle(), folderTitle); + assertTrue(recordFolderProperties.getVitalRecordIndicator()); + AssertJUnit.assertEquals(recordFolderProperties.getLocation(), location); + assertNotNull(recordFolderProperties.getReviewPeriod().getPeriodType()); + assertNotNull(recordFolderProperties.getReviewPeriod().getExpression()); } /** + *
      * Given that a record folder exists
      * When I use the API to delete the record folder
-     * Then it deleted according to the normal rules governing the deletion of record folders
+     * Then it is deleted according to the normal rules governing the deletion of record folders
+     * 
*/ @Test ( @@ -257,95 +264,93 @@ public class RecordFolderTests extends BaseRMRestTest ) public void deleteRecordFolder() throws Exception { - String CATEGORY = CATEGORY_NAME + getRandomAlphanumeric(); - // Create the record category - FilePlanComponent category = createCategory(FILE_PLAN_ALIAS, CATEGORY); + RecordCategory rootRecordCategory = createRootCategory(RECORD_CATEGORY_NAME + getRandomAlphanumeric()); // Create the record folder - FilePlanComponent folder = createFolder(category.getId(), FOLDER_NAME); + RecordCategoryChild recordFolder = createRecordFolder(rootRecordCategory.getId(), RECORD_FOLDER_NAME); - // Delete the Record folder - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - filePlanComponentsAPI.deleteFilePlanComponent(folder.getId()); - // Check the Response Status Code + // Delete the record folder + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + String recordFolderId = recordFolder.getId(); + recordFolderAPI.deleteRecordFolder(recordFolderId); + + // Check the response status code assertStatusCode(NO_CONTENT); - // Check the File Plan Component is not found - filePlanComponentsAPI.getFilePlanComponent(folder.getId()); - // Check the Response Status Code + // Check the record folder is not found + recordFolderAPI.getRecordFolder(recordFolderId); + + // Check the response status code assertStatusCode(NOT_FOUND); } /** + *
      * Given that a record category exists
      * And contains several record folders
-     * When I use the APi to get the file plan component children for the existing category
-     * Then I am provided with a list of the contained record folders
-     * And their details
+     * When I use the API to get the record category children for an existing record category
+     * Then I am provided with a list of the contained record category children and their details
+     * 
*/ @Test ( - description = "List children of a category" + description = "Get children of a record category" ) - public void listFolders() throws Exception + public void getFolders() throws Exception { - - String CATEGORY = CATEGORY_NAME + getRandomAlphanumeric(); - // Authenticate with admin user - FilePlanComponent category = createCategory(FILE_PLAN_ALIAS, CATEGORY); + RecordCategory rootRecordCategory = createRootCategory(RECORD_CATEGORY_NAME + getRandomAlphanumeric()); - // Add child olders - ArrayList children = new ArrayList(); + // Add child folders + ArrayList children = new ArrayList(); for (int i = 0; i < NUMBER_OF_FOLDERS; i++) { - // Create a child - FilePlanComponent child = createFolder(category.getId(), - getRandomAlphanumeric()); - assertNotNull(child.getId()); - children.add(child); + // Create a record folder + RecordCategoryChild recordCategoryChild = createRecordFolder(rootRecordCategory.getId(), getRandomAlphanumeric()); + assertNotNull(recordCategoryChild.getId()); + children.add(recordCategoryChild); } - // List children from API - FilePlanComponentsCollection apiChildren = getRestAPIFactory().getFilePlanComponentsAPI().listChildComponents(category.getId()); + // Get record category children from API + RecordCategoryChildCollection recordCategoryChildren = getRestAPIFactory().getRecordCategoryAPI().getRecordCategoryChildren(rootRecordCategory.getId(), "include=isRecordCategory,isRecordFolder"); // Check status code assertStatusCode(OK); - // Check listed children against created list - apiChildren.getEntries().forEach(c -> + // Check children against created list + recordCategoryChildren.getEntries().forEach(c -> { - FilePlanComponent filePlanComponent = c.getFilePlanComponentModel(); - assertNotNull(filePlanComponent.getId()); - logger.info("Checking child " + filePlanComponent.getId()); + RecordCategoryChild recordCategoryChild = c.getEntry(); + String recordCategoryChildId = recordCategoryChild.getId(); + assertNotNull(recordCategoryChildId); + logger.info("Checking child " + recordCategoryChildId); try { // Find this child in created children list - FilePlanComponent createdComponent = children.stream() - .filter(child -> child.getId().equals(filePlanComponent.getId())) + RecordCategoryChild createdComponent = children.stream() + .filter(child -> child.getId().equals(recordCategoryChildId)) .findFirst() .get(); // Created by - assertEquals(filePlanComponent.getCreatedByUser().getId(), getAdminUser().getUsername()); + assertEquals(recordCategoryChild.getCreatedByUser().getId(), getAdminUser().getUsername()); - // Is parent Id set correctly - assertEquals(filePlanComponent.getParentId(), category.getId()); - assertFalse(filePlanComponent.getIsFile()); + // Is parent id set correctly + assertEquals(recordCategoryChild.getParentId(), rootRecordCategory.getId()); // Boolean properties related to node type - assertTrue(filePlanComponent.getIsRecordFolder()); - assertFalse(filePlanComponent.getIsCategory()); + assertTrue(recordCategoryChild.getIsRecordFolder()); + assertFalse(recordCategoryChild.getIsRecordCategory()); - assertEquals(createdComponent.getName(), filePlanComponent.getName()); - assertEquals(createdComponent.getNodeType(), filePlanComponent.getNodeType()); + assertEquals(createdComponent.getName(), recordCategoryChild.getName()); + assertEquals(createdComponent.getNodeType(), recordCategoryChild.getNodeType()); } catch (NoSuchElementException e) { - fail("No child element for " + filePlanComponent.getId()); + fail("No child element for " + recordCategoryChildId); } } ); @@ -353,92 +358,107 @@ public class RecordFolderTests extends BaseRMRestTest } /** + *
      * Given that I want to create a record folder
      * When I use the API with the relativePath
-     * Then the categories specified in the relativePath that don't exist are created within the record folder
-     *
-     * Containers in the relativePath that do not exist are created before the node is created
+     * Then the categories specified in the relativePath that don't exist are created
+     * 
*/ @Test ( - description = "Create a folder based on the relativePath. " + + description = "Create a folder using record-categories endpoint, based on the relativePath. " + "Containers in the relativePath that do not exist are created before the node is created" ) - public void createFolderWithRelativePath() throws Exception + public void createRecordFolderWithRelativePath() throws Exception { - //RelativePath specify the container structure to create relative to the record folder to be created - String relativePath = LocalDateTime.now().getYear() + "/" + LocalDateTime.now().getMonth() + "/" + LocalDateTime.now().getDayOfMonth(); + // The record category to be created + RecordCategory recordCategoryModel = RecordCategory.builder() + .name(RECORD_CATEGORY_NAME) + .nodeType(RECORD_CATEGORY_TYPE) + .build(); + FilePlanAPI filePlansAPI = getRestAPIFactory().getFilePlansAPI(); + RecordCategory createRootRecordCategory = filePlansAPI.createRootRecordCategory(recordCategoryModel, FILE_PLAN_ALIAS, "include=" + PATH); + // Check the API response code + assertStatusCode(CREATED); + String recordCategoryId = createRootRecordCategory.getId(); - //The record folder to be created - FilePlanComponent recordFolder = FilePlanComponent.builder() - .name(FOLDER_NAME) - .nodeType(RECORD_FOLDER_TYPE) - .relativePath(relativePath) - .build(); + // relativePath specify the container structure to create relative to the record folder to be created + String relativePath = now().getYear() + "/" + now().getMonth() + "/" + now().getDayOfMonth(); + + // The record folder to be created + RecordCategoryChild recordFolderModel = RecordCategoryChild.builder() + .name(RECORD_FOLDER_NAME) + .nodeType(RECORD_FOLDER_TYPE) + .relativePath(relativePath) + .build(); // Create the record folder - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - FilePlanComponent folder = filePlanComponentsAPI.createFilePlanComponent(recordFolder, FILE_PLAN_ALIAS, "include=" + PATH); - //Check the API response code + RecordCategoryAPI recordCategoryAPI = getRestAPIFactory().getRecordCategoryAPI(); + RecordCategoryChild recordCategoryChild = recordCategoryAPI.createRecordCategoryChild(recordFolderModel, recordCategoryId, "include=" + PATH); + + // Check the API response code assertStatusCode(CREATED); - // Verify the returned properties for the file plan component - record folder - assertFalse(folder.getIsCategory()); - assertFalse(folder.getIsFile()); - assertTrue(folder.getIsRecordFolder()); + // Verify the returned details for the record folder + assertFalse(recordCategoryChild.getIsRecordCategory()); + assertTrue(recordCategoryChild.getIsRecordFolder()); - //Check the path return contains the RELATIVE_PATH - assertTrue(folder.getPath().getName().contains(relativePath)); - //check the parent is a category - assertTrue(filePlanComponentsAPI.getFilePlanComponent(folder.getParentId()).getIsCategory()); + // Check the path return contains the relativePath + AssertJUnit.assertTrue(recordCategoryChild.getPath().getName().contains(relativePath)); - //check the created folder from the server - folder = filePlanComponentsAPI.getFilePlanComponent(folder.getId(), "include=" + PATH); - //Check the API response code + // Check the parent is a category + assertNotNull(recordCategoryAPI.getRecordCategory(recordCategoryChild.getParentId()).getId()); + + // Check the created folder from the server + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + RecordFolder recordFolder = recordFolderAPI.getRecordFolder(recordCategoryChild.getId(), "include=" + PATH); + + // Check the API response code assertStatusCode(OK); - // Verify the returned properties for the file plan component - record folder - assertFalse(folder.getIsCategory()); - assertFalse(folder.getIsFile()); - assertTrue(folder.getIsRecordFolder()); - //Check the path return contains the RELATIVE_PATH - assertTrue(folder.getPath().getName().contains(relativePath)); + // Verify the returned details for the record folder + assertTrue(RECORD_FOLDER_TYPE.equals(recordFolder.getNodeType())); - //New Relative Path only a part of containers need to be created before the record folder - String NEW_RELATIVE_PATH = LocalDateTime.now().getYear() + "/" + LocalDateTime.now().getMonth() + "/" + (LocalDateTime.now().getDayOfMonth() + 1); - //The record folder to be created - FilePlanComponent recordFolder2 = FilePlanComponent.builder() - .name(FOLDER_NAME) - .nodeType(RECORD_FOLDER_TYPE) - .relativePath(NEW_RELATIVE_PATH) - .build(); + // Check the path return contains the relativePath + AssertJUnit.assertTrue(recordFolder.getPath().getName().contains(relativePath)); + + // New relative path only a part of containers need to be created before the record folder + String newRelativePath = now().getYear() + "/" + now().getMonth() + "/" + (now().getDayOfMonth() + 1); + + // The record folder to be created + RecordCategoryChild newRecordFolderModel = RecordCategoryChild.builder() + .name(RECORD_FOLDER_NAME) + .nodeType(RECORD_FOLDER_TYPE) + .relativePath(newRelativePath) + .build(); // Create the record folder - FilePlanComponent folder2 = filePlanComponentsAPI.createFilePlanComponent(recordFolder2, FILE_PLAN_ALIAS, "include=" + PATH); - //Check the API response code + RecordCategoryChild newRecordCategoryChild = recordCategoryAPI.createRecordCategoryChild(newRecordFolderModel, recordCategoryId, "include=" + PATH); + + // Check the API response code assertStatusCode(CREATED); // Verify the returned properties for the file plan component - record folder - assertFalse(folder2.getIsCategory()); - assertFalse(folder2.getIsFile()); - assertTrue(folder2.getIsRecordFolder()); - //Check the path return contains the NEW_RELATIVE_PATH - assertTrue(folder2.getPath().getName().contains(NEW_RELATIVE_PATH)); + assertFalse(newRecordCategoryChild.getIsRecordCategory()); + assertTrue(newRecordCategoryChild.getIsRecordFolder()); - //check the parent is a category - assertTrue(filePlanComponentsAPI.getFilePlanComponent(folder.getParentId()).getIsCategory()); + // Check the path return contains the newRelativePath + AssertJUnit.assertTrue(newRecordCategoryChild.getPath().getName().contains(newRelativePath)); + + // Check the parent is a category + assertNotNull(recordCategoryAPI.getRecordCategory(newRecordCategoryChild.getParentId()).getId()); // Check the folder created on the server - folder2 = filePlanComponentsAPI.getFilePlanComponent(folder2.getId(), "include=" + PATH); - //Check the API response code + RecordFolder newRecordFolder = recordFolderAPI.getRecordFolder(newRecordCategoryChild.getId(), "include=" + PATH); + + // Check the API response code assertStatusCode(OK); - // Verify the returned properties for the file plan component - record folder - assertFalse(folder2.getIsCategory()); - assertFalse(folder2.getIsFile()); - assertTrue(folder2.getIsRecordFolder()); - //Check the path return contains the NEW_RELATIVE_PATH - assertTrue(folder2.getPath().getName().contains(NEW_RELATIVE_PATH)); + // Verify the returned details for the record folder + assertTrue(RECORD_FOLDER_TYPE.equals(recordFolder.getNodeType())); + + // Check the path return contains the newRelativePath + AssertJUnit.assertTrue(newRecordFolder.getPath().getName().contains(newRelativePath)); } /** @@ -454,55 +474,57 @@ public class RecordFolderTests extends BaseRMRestTest public void openClosedRecordFolder() throws Exception { // Create a record folder - FilePlanComponent recordFolder = createCategoryFolderInFilePlan(); + RecordCategoryChild recordFolder = createCategoryFolderInFilePlan(); // Assert that the record folder is not closed assertFalse(recordFolder.getProperties().getIsClosed()); - // Get the file plan component API - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); + // Get the record folder API + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); // Create a record folder model to close it - FilePlanComponent recordFolderModel = FilePlanComponent.builder() - .properties(FilePlanComponentProperties.builder() - .isClosed(true) - .build()) - .build(); + RecordFolder recordFolderModel = RecordFolder.builder() + .properties(RecordFolderProperties.builder() + .isClosed(true) + .build()) + .build(); // Make a request to close the record folder - FilePlanComponent updatedRecordFolder = filePlanComponentsAPI.updateFilePlanComponent(recordFolderModel, recordFolder.getId()); + RecordFolder updatedRecordFolder = recordFolderAPI.updateRecordFolder(recordFolderModel, recordFolder.getId()); + + //FIXME - remove this workaround after RM-4921 is fixed. + updatedRecordFolder = recordFolderAPI.getRecordFolder(updatedRecordFolder.getId()); // Verify that the record folder is closed now assertTrue(updatedRecordFolder.getProperties().getIsClosed()); // Create a record folder model to reopen it - recordFolderModel = FilePlanComponent.builder() - .properties(FilePlanComponentProperties.builder() - .isClosed(false) - .build()) - .build(); + recordFolderModel = RecordFolder.builder() + .properties(RecordFolderProperties.builder() + .isClosed(false) + .build()) + .build(); // Make a request to reopen the record folder - updatedRecordFolder = filePlanComponentsAPI.updateFilePlanComponent(recordFolderModel, recordFolder.getId()); + updatedRecordFolder = recordFolderAPI.updateRecordFolder(recordFolderModel, recordFolder.getId()); + + //FIXME - remove this workaround after RM-4921 is fixed. + updatedRecordFolder = recordFolderAPI.getRecordFolder(updatedRecordFolder.getId()); // Verify that the record folder is open now assertFalse(updatedRecordFolder.getProperties().getIsClosed()); } + @AfterMethod @AfterClass (alwaysRun = true) public void tearDown() throws Exception { - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - filePlanComponentsAPI.listChildComponents(FILE_PLAN_ALIAS).getEntries().forEach(filePlanComponentEntry -> + FilePlanAPI filePlansAPI = getRestAPIFactory().getFilePlansAPI(); + RecordCategoryAPI recordCategoryAPI = getRestAPIFactory().getRecordCategoryAPI(); + + filePlansAPI.getRootRecordCategories(FILE_PLAN_ALIAS).getEntries().forEach(recordCategoryEntry -> { - try - { - filePlanComponentsAPI.deleteFilePlanComponent(filePlanComponentEntry.getFilePlanComponentModel().getId()); - } - catch (Exception e) - { - e.printStackTrace(); - } + recordCategoryAPI.deleteRecordCategory(recordCategoryEntry.getEntry().getId()); }); } } diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UnfiledContainerTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UnfiledContainerTests.java new file mode 100644 index 0000000000..b115446add --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UnfiledContainerTests.java @@ -0,0 +1,353 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.fileplancomponents; + +import static java.time.LocalDateTime.now; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.NON_ELECTRONIC_RECORD_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_CONTAINER_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; +import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.OK; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; + +import org.alfresco.rest.rm.community.base.BaseRMRestTest; +import org.alfresco.rest.rm.community.model.fileplan.FilePlan; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainer; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildCollection; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +/** + * Unfiled container related API tests + * + * @author Ana Bozianu + * @since 2.6 + */ +public class UnfiledContainerTests extends BaseRMRestTest +{ + /** Number of children (for children creation test) */ + private static final int NUMBER_OF_CHILDREN = 10; + + /** + *
+     * Given the RM site exists
+     * When I retrieve the unfiled record conteiner by placeholder
+     * Then the details of the unfiled record container is returned
+     * 
+ */ + @Test + ( + description = "Get the unfiled records container" + ) + public void getUnfiledRecordsContainer() throws Exception + { + // Get the unfiled records container + UnfiledContainer container = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(UNFILED_RECORDS_CONTAINER_ALIAS); + + // Check the response code + assertStatusCode(OK); + + // Check the response contains the right node type + assertEquals(container.getNodeType(), UNFILED_CONTAINER_TYPE); + } + + /** + *
+     * Given that an unfiled container exists
+     * When I ask the API to update the details of the unfiled container
+     * Then the details of the unfiled container are updated
+     * 
+ */ + @Test + ( + description = "Rename unfiled container" + ) + public void renameUnfiledContainer() throws Exception + { + String newContainerName = "RenamedUnfiledContainer (" + getRandomAlphanumeric() + ")"; + + // Build the properties which will be updated + UnfiledContainer unfiledContainerUpdate = UnfiledContainer.builder().name(newContainerName).build(); + + // Update the unfiled records container + UnfiledContainer renamedUnfiledContainer = getRestAPIFactory().getUnfiledContainersAPI().updateUnfiledContainer(unfiledContainerUpdate, UNFILED_RECORDS_CONTAINER_ALIAS); + + // Verify the status code + assertStatusCode(OK); + + // Verify the returned unfiled records container + assertEquals(renamedUnfiledContainer.getName(), newContainerName); + + // Get actual FILE_PLAN_ALIAS id + FilePlan filePlan = getRestAPIFactory().getFilePlansAPI().getFilePlan(FILE_PLAN_ALIAS); + + // verify renamed component still has this parent + assertEquals(renamedUnfiledContainer.getParentId(), filePlan.getId()); + } + + /** + *
+     * Given that an unfiled records container exists
+     * When I ask the API to create a child unfiled record folder
+     * Then it is created within the unfiled records container
+     * 
+ */ + @Test + ( + description = "Create unfiled record folder child in unfiled root container" + ) + public void createUnfiledRecordFolderChild() throws Exception + { + String unfiledRecordFolderName = "UnfiledRecordFolder-" + getRandomAlphanumeric(); + UnfiledContainerChild unfiledRecordFolder = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, unfiledRecordFolderName, UNFILED_RECORD_FOLDER_TYPE); + + assertNotNull(unfiledRecordFolder.getId()); + UnfiledContainer container = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(UNFILED_RECORDS_CONTAINER_ALIAS); + assertEquals(unfiledRecordFolder.getParentId(), container.getId()); + assertFalse(unfiledRecordFolder.getIsRecord()); + assertEquals(unfiledRecordFolder.getNodeType(), UNFILED_RECORD_FOLDER_TYPE); + } + + /** + *
+     * Given that an unfiled records container exists
+     * When I ask the API to create a child unfiled record folder with relative path
+     * Then it is not supported
+     * 
+ */ + @Test + ( + description = "Create unfiled record folder child in unfiled root container" + ) + public void createUnfiledRecordFolderChildWithRealtivePathNotSuported() throws Exception + { + UnfiledContainerAPI unfiledContainerAPI = getRestAPIFactory().getUnfiledContainersAPI(); + // relativePath specify the container structure to create relative to + // the record folder to be created + String relativePath = now().getYear() + "/" + now().getMonth() + "/" + now().getDayOfMonth(); + + String unfiledRecordFolderName = "UnfiledRecordFolder-" + getRandomAlphanumeric(); + + UnfiledContainerChild unfiledFolderModel = UnfiledContainerChild.builder() + .name(unfiledRecordFolderName) + .nodeType(UNFILED_RECORD_FOLDER_TYPE) + .relativePath(relativePath) + .build(); + unfiledContainerAPI.createUnfiledContainerChild(unfiledFolderModel, UNFILED_RECORDS_CONTAINER_ALIAS); + + // Check the API response code + assertStatusCode(BAD_REQUEST); + } + + /** + *
+     * Given that an unfiled records container exists
+     * When I ask the API to create a child record
+     * Then it is created within the unfiled records container
+     * 
+ */ + @Test + ( + description = "Create record child in unfiled root container" + ) + public void createNonElectronicRecordChild() throws Exception + { + String recordName = "NERecord-" + getRandomAlphanumeric(); + UnfiledContainerChild unfiledRecord = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, recordName, NON_ELECTRONIC_RECORD_TYPE); + + assertNotNull(unfiledRecord.getId()); + assertTrue(unfiledRecord.getIsRecord()); + assertEquals(unfiledRecord.getNodeType(), NON_ELECTRONIC_RECORD_TYPE); + // check it was created in the unfiled root container + UnfiledContainer container = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(UNFILED_RECORDS_CONTAINER_ALIAS); + assertEquals(unfiledRecord.getParentId(), container.getId()); + // check the name contains the identifier + String identifier = unfiledRecord.getProperties().getIdentifier(); + assertNotNull(identifier); + assertEquals(unfiledRecord.getName(), recordName + " (" + identifier + ")"); + } + + /** + *
+     * Given that an unfiled records container exists
+     * When I ask the API to create a child record
+     * Then it is created within the unfiled records container
+     * 
+ */ + @Test + ( + description = "Create electronic record child in unfiled root container" + ) + public void createElectronicRecordChild() throws Exception + { + String recordName = "ERecord-" + getRandomAlphanumeric(); + UnfiledContainerChild unfiledRecord = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, recordName, CONTENT_TYPE); + + assertNotNull(unfiledRecord.getId()); + assertTrue(unfiledRecord.getIsRecord()); + assertEquals(unfiledRecord.getNodeType(), CONTENT_TYPE); + // check it was created in the unfiled root container + UnfiledContainer container = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(UNFILED_RECORDS_CONTAINER_ALIAS); + assertEquals(unfiledRecord.getParentId(), container.getId()); + // check the name contains the identifier + String identifier = unfiledRecord.getProperties().getIdentifier(); + assertNotNull(identifier); + assertEquals(unfiledRecord.getName(), recordName + " (" + identifier + ")"); + } + + /** + *
+     * Given the RM site is created
+     * And contains a number of records and unfiled record folders
+     * When I ask the API to get me the children of the unfiled root container
+     * Then I am returned the contained record and unfiled record folders
+     * 
+ */ + @Test + ( + description = "Get children of the root unfiled root container" + ) + public void getUnfiledRootContainerChildren() throws Exception + { + // Add unfiled root container children + List createdChildren = new LinkedList<>(); + for (int i=0; i < NUMBER_OF_CHILDREN; i++) + { + String childType; + if (i % 3 == 0) + { + childType = CONTENT_TYPE; + } + else if (i % 3 == 1) + { + childType = NON_ELECTRONIC_RECORD_TYPE; + } + else + { + childType = UNFILED_RECORD_FOLDER_TYPE; + } + UnfiledContainerChild child = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, getRandomAlphanumeric(), childType); + assertNotNull(child.getId()); + createdChildren.add(child); + } + + // Get children from API + UnfiledContainerChildCollection listedChildren = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS); + + // Check status code + assertStatusCode(OK); + + // Check listed children contains created list + UnfiledContainer unfiledContainer = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(UNFILED_RECORDS_CONTAINER_ALIAS); + List verifiedChildren = new LinkedList<>(); + listedChildren.getEntries().forEach(c -> + { + UnfiledContainerChild containerChild = c.getEntry(); + String childId = containerChild.getId(); + + assertNotNull(childId); + logger.info("Checking child " + childId); + + try + { + // Get the element from the created children list + UnfiledContainerChild createdComponent = createdChildren.stream() + .filter(child -> child.getId().equals(childId)) + .findFirst() + .get(); + + // Created by + assertEquals(containerChild.getCreatedByUser().getId(), getAdminUser().getUsername()); + + // Is parent id set correctly? + assertEquals(containerChild.getParentId(), unfiledContainer.getId()); + + // Boolean properties related to node type + if (containerChild.getNodeType().equals(UNFILED_RECORD_FOLDER_TYPE)) + { + assertFalse(containerChild.getIsRecord()); + } + else + { + assertTrue(containerChild.getIsRecord()); + } + + // Does returned object have the same contents as the created one? + assertEquals(createdComponent.getName(), containerChild.getName()); + assertEquals(createdComponent.getNodeType(), containerChild.getNodeType()); + + // FIXME: Verify properties + assertNotNull(createdComponent.getProperties().getIdentifier()); + + // add the element to the matched children list + verifiedChildren.add(createdComponent); + } + catch (NoSuchElementException e) + { + // the element was not created in this test, continue + } + }); + + // check all the created elements have been returned + assertTrue(verifiedChildren.containsAll(createdChildren)); + assertTrue(createdChildren.containsAll(verifiedChildren)); + } + + @AfterMethod + @AfterClass (alwaysRun = true) + public void tearDown() throws Exception + { + UnfiledContainerChildCollection listedChildren = getRestAPIFactory().getUnfiledContainersAPI() + .getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS); + + listedChildren.getEntries().forEach(UnfiledContainerChildEntry -> + { + if (UnfiledContainerChildEntry.getEntry().getIsRecord()) + { + getRestAPIFactory().getRecordsAPI().deleteRecord(UnfiledContainerChildEntry.getEntry().getId()); + } + else + { + getRestAPIFactory().getUnfiledRecordFoldersAPI().deleteUnfiledRecordFolder(UnfiledContainerChildEntry.getEntry().getId()); + } + }); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UnfiledRecordsFolderTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UnfiledRecordsFolderTests.java index ee6bd0c69a..54b86f5aab 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UnfiledRecordsFolderTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UnfiledRecordsFolderTests.java @@ -26,15 +26,18 @@ */ package org.alfresco.rest.rm.community.fileplancomponents; +import static java.time.LocalDateTime.now; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PATH; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.FILE_PLAN_TYPE; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.HOLD_CONTAINER_TYPE; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.HOLD_TYPE; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.NON_ELECTRONIC_RECORD_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.TRANSFER_CONTAINER_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_CONTAINER_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createUnfiledContainerChildModel; import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.NOT_FOUND; @@ -43,16 +46,20 @@ import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import java.util.List; import java.util.stream.Collectors; import org.alfresco.rest.rm.community.base.BaseRMRestTest; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentsCollection; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildCollection; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildProperties; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledRecordFolder; +import org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -67,15 +74,13 @@ public class UnfiledRecordsFolderTests extends BaseRMRestTest /** invalid root level types, at unfiled records root level these shouldn't be possible to create */ @DataProvider(name = "invalidRootTypes") - public Object[][] createData1() + public String[][] createData() { - return new Object[][] + return new String[][] { { FILE_PLAN_TYPE }, { RECORD_CATEGORY_TYPE }, { RECORD_FOLDER_TYPE }, - { HOLD_TYPE }, - { HOLD_CONTAINER_TYPE }, { TRANSFER_CONTAINER_TYPE }, { UNFILED_CONTAINER_TYPE } }; @@ -91,70 +96,140 @@ public class UnfiledRecordsFolderTests extends BaseRMRestTest @Test(description = "Create root unfiled records folder") public void createRootUnfiledRecordsFolder() throws Exception { - String folderName = "Folder " + getRandomAlphanumeric(); - String folderTitle = folderName + " Title"; - String folderDescription = folderName + " Description"; + String unfiledRecordFolderName = "UnfiledRecordFolder-" + getRandomAlphanumeric(); + UnfiledContainerChild unfiledRecordFolderChild = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, unfiledRecordFolderName, UNFILED_RECORD_FOLDER_TYPE); - // Build unfiled records folder properties - FilePlanComponent unfiledFolder = FilePlanComponent.builder() - .name(folderName) - .nodeType(UNFILED_RECORD_FOLDER_TYPE) - .properties(FilePlanComponentProperties.builder() - .title(folderTitle) - .description(folderDescription) - .build()) - .build(); - - FilePlanComponent filePlanComponent = getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(unfiledFolder, UNFILED_RECORDS_CONTAINER_ALIAS); - - // Verify the status code - assertStatusCode(CREATED); + assertNotNull(unfiledRecordFolderChild.getId()); // Verify the returned file plan component - assertFalse(filePlanComponent.getIsCategory()); - assertFalse(filePlanComponent.getIsFile()); - assertFalse(filePlanComponent.getIsRecordFolder()); // it is not a _normal_ record folder! + assertFalse(unfiledRecordFolderChild.getIsRecord()); + assertTrue(unfiledRecordFolderChild.getIsUnfiledRecordFolder()); // it is not a _normal_ record folder! - assertEquals(filePlanComponent.getName(), folderName); - assertEquals(filePlanComponent.getNodeType(), UNFILED_RECORD_FOLDER_TYPE); + assertEquals(unfiledRecordFolderChild.getName(), unfiledRecordFolderName); + assertEquals(unfiledRecordFolderChild.getNodeType(), UNFILED_RECORD_FOLDER_TYPE); - assertEquals(filePlanComponent.getCreatedByUser().getId(), getAdminUser().getUsername()); + assertEquals(unfiledRecordFolderChild.getCreatedByUser().getId(), getAdminUser().getUsername()); + UnfiledRecordFolder unfiledRecordFolder = getRestAPIFactory().getUnfiledRecordFoldersAPI().getUnfiledRecordFolder(unfiledRecordFolderChild.getId()); // Verify the returned file plan component properties - FilePlanComponentProperties filePlanComponentProperties = filePlanComponent.getProperties(); - assertEquals(filePlanComponentProperties.getTitle(), folderTitle); - assertEquals(filePlanComponentProperties.getDescription(), folderDescription); + UnfiledContainerChildProperties unfiledRecordFolderChildProperties = unfiledRecordFolder.getProperties(); + assertEquals(unfiledRecordFolderChildProperties.getTitle(), FilePlanComponentsUtil.TITLE_PREFIX + unfiledRecordFolderName); + } + + /** + *
+     * Given that I want to create an unfiled record folder
+     * When I use the API with the relativePath
+     * Then the folders specified in the relativePath that don't exist are created
+     * 
+ */ + @Test + ( + description = "Create a folder based on the relativePath. " + + "Containers in the relativePath that do not exist are created before the node is created" + ) + public void createUnfiledRecordFolderWithRelativePath() throws Exception + { + String unfiledRecordFolderName1 = "UnfiledRecordFolder-" + getRandomAlphanumeric(); + UnfiledContainerChild unfiledRecordFolderChild1 = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, unfiledRecordFolderName1, UNFILED_RECORD_FOLDER_TYPE); + + String unfiledRecordFolderName2 = "UnfiledRecordFolder-" + getRandomAlphanumeric(); + + // relativePath specify the container structure to create relative to the record folder to be created + String relativePath = now().getYear() + "/" + now().getMonth() + "/" + now().getDayOfMonth(); + + // The record folder to be created + UnfiledContainerChild unfiledFolderModel =UnfiledContainerChild.builder() + .name(unfiledRecordFolderName2) + .nodeType(UNFILED_RECORD_FOLDER_TYPE) + .relativePath(relativePath) + .build(); + + UnfiledContainerChild unfiledRecordFolderChild = getRestAPIFactory().getUnfiledRecordFoldersAPI().createUnfiledRecordFolderChild(unfiledFolderModel, unfiledRecordFolderChild1.getId(), "include=" + PATH); + + // Check the API response code + assertStatusCode(CREATED); + + // Verify the returned details for the record folder + assertFalse(unfiledRecordFolderChild.getIsRecord()); + assertTrue(unfiledRecordFolderChild.getIsUnfiledRecordFolder()); + assertTrue(UNFILED_RECORD_FOLDER_TYPE.equals(unfiledRecordFolderChild.getNodeType())); + + // Check the path return contains the relativePath + assertTrue(unfiledRecordFolderChild.getPath().getName().contains(relativePath)); + + // Check the parent is a folder, not a record + assertTrue(getRestAPIFactory().getUnfiledRecordFoldersAPI().getUnfiledRecordFolder(unfiledRecordFolderChild.getParentId()).getNodeType().equals(UNFILED_RECORD_FOLDER_TYPE)); + + // Check the created folder from the server + UnfiledRecordFolder recordFolder = getRestAPIFactory().getUnfiledRecordFoldersAPI().getUnfiledRecordFolder(unfiledRecordFolderChild.getId(), "include=" + PATH); + + // Check the API response code + assertStatusCode(OK); + + // Check the path return contains the relativePath + assertTrue(recordFolder.getPath().getName().contains(relativePath)); + + // New relative path only a part of containers need to be created before the record folder + String newRelativePath = now().getYear() + "/" + now().getMonth() + "/" + (now().getDayOfMonth() + 1); + + String unfiledRecordFolderName3 = "UnfiledRecordFolder-" + getRandomAlphanumeric(); + // The record folder to be created + UnfiledContainerChild newUnfiledFolderModel =UnfiledContainerChild.builder() + .name(unfiledRecordFolderName3) + .nodeType(UNFILED_RECORD_FOLDER_TYPE) + .relativePath(newRelativePath) + .build(); + + UnfiledContainerChild newUnfiledRecordFolderChild = getRestAPIFactory().getUnfiledRecordFoldersAPI().createUnfiledRecordFolderChild(newUnfiledFolderModel, unfiledRecordFolderChild1.getId(), "include=" + PATH); + + // Check the API response code + assertStatusCode(CREATED); + // Check the API response code + assertStatusCode(CREATED); + + // Verify the returned properties for the unfiled record folder + assertTrue(newUnfiledRecordFolderChild.getIsUnfiledRecordFolder()); + assertFalse(newUnfiledRecordFolderChild.getIsRecord()); + + // Check the path return contains the newRelativePath + assertTrue(newUnfiledRecordFolderChild.getPath().getName().contains(newRelativePath)); + + // Check the parent is a folder, not a record + assertFalse(getRestAPIFactory().getUnfiledRecordFoldersAPI().getUnfiledRecordFolder(newUnfiledRecordFolderChild.getParentId()).equals(UNFILED_RECORD_FOLDER_TYPE)); + + // Check the folder created on the server + UnfiledRecordFolder newRecordFolder = getRestAPIFactory().getUnfiledRecordFoldersAPI().getUnfiledRecordFolder(newUnfiledRecordFolderChild.getId(), "include=" + PATH); + + // Check the API response code + assertStatusCode(OK); + + // Check the path return contains the newRelativePath + assertTrue(newRecordFolder.getPath().getName().contains(newRelativePath)); } + /** - * Negative test to verify only unfiled record folders can be created at root level + * Negative test to check that invalid types cannot be created at unfiled container root level + * Only unfiled record folders and records can be created into unfiled container */ @Test ( dataProvider = "invalidRootTypes", - description = "Only unfiled records folders can be created at unfiled records root level" + description = "Only unfiled records folders can be created at unfiled records root level" ) public void onlyRecordFoldersCanBeCreatedAtUnfiledRecordsRoot(String filePlanComponentType) { - String folderName = "Folder " + getRandomAlphanumeric(); - String folderTitle = folderName + " Title"; - String folderDescription = folderName + " Description"; + String unfiledRecordFolderName = "UnfiledRecordFolder-" + getRandomAlphanumeric(); logger.info("creating " + filePlanComponentType); // Build unfiled records folder properties - FilePlanComponent unfiledFolder = FilePlanComponent.builder() - .name(folderName) - .nodeType(filePlanComponentType) - .properties(FilePlanComponentProperties.builder() - .title(folderTitle) - .description(folderDescription) - .build()) - .build(); + UnfiledContainerChild unfiledFolderModel = createUnfiledContainerChildModel(unfiledRecordFolderName, filePlanComponentType); try { - getRestAPIFactory().getFilePlanComponentsAPI().createFilePlanComponent(unfiledFolder, UNFILED_RECORDS_CONTAINER_ALIAS); + getRestAPIFactory().getUnfiledContainersAPI().createUnfiledContainerChild(unfiledFolderModel, UNFILED_RECORDS_CONTAINER_ALIAS); } catch (Exception error) { @@ -174,60 +249,61 @@ public class UnfiledRecordsFolderTests extends BaseRMRestTest @Test(description = "Child unfiled records folder can be created in a parent unfiled records folder") public void childUnfiledRecordsFolderCanBeCreated() throws Exception { - String parentFolderName = "Parent Folder " + getRandomAlphanumeric(); - String childFolderName = "Child Folder " + getRandomAlphanumeric(); - String childFolderTitle = childFolderName + " Title"; - String childFolderDescription = childFolderName + " Description"; + String unfiledParentFolderName = "UnfiledParentFolder" + getRandomAlphanumeric(); + String unfiledChildFolderName = "UnfiledChildFolder " + getRandomAlphanumeric(); + String rootUnfiledFolderName = "RootUnfiledFolder" + getRandomAlphanumeric(); + //create root unfiled record folder + UnfiledContainerChild createUnfiledContainerChild = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, rootUnfiledFolderName, UNFILED_RECORD_FOLDER_TYPE); // No need for fine control, create it using utility function - FilePlanComponent parentFolder = createUnfiledRecordsFolder(UNFILED_RECORDS_CONTAINER_ALIAS, parentFolderName); - assertEquals(parentFolderName, parentFolder.getName()); + UnfiledContainerChild unfiledParentFolder = createUnfiledRecordsFolderChild(createUnfiledContainerChild.getId(), + unfiledParentFolderName, UNFILED_RECORD_FOLDER_TYPE); + assertEquals(unfiledParentFolderName, unfiledParentFolder.getName()); // Build the unfiled records folder properties - FilePlanComponent unfiledFolder = FilePlanComponent.builder() - .name(childFolderName) + UnfiledContainerChild unfiledChildFolderModel = UnfiledContainerChild.builder().name(unfiledChildFolderName) .nodeType(UNFILED_RECORD_FOLDER_TYPE) - .properties(FilePlanComponentProperties.builder() - .title(childFolderTitle) - .description(childFolderDescription) - .build()) + .properties(UnfiledContainerChildProperties.builder().title(FilePlanComponentsUtil.TITLE_PREFIX + unfiledChildFolderName) + .description(FilePlanComponentsUtil.DESCRIPTION_PREFIX + unfiledChildFolderName).build()) .build(); // Create it as a child of parentFolder - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - FilePlanComponent childFolder = filePlanComponentsAPI.createFilePlanComponent(unfiledFolder, parentFolder.getId()); + UnfiledContainerChild unfiledChildFolder = getRestAPIFactory().getUnfiledRecordFoldersAPI() + .createUnfiledRecordFolderChild(unfiledChildFolderModel, unfiledParentFolder.getId()); // Verify the status code assertStatusCode(CREATED); - // Verify the returned file plan component - assertFalse(childFolder.getIsCategory()); - assertFalse(childFolder.getIsFile()); - assertFalse(childFolder.getIsRecordFolder()); // it is not a _normal_ record folder! + // Verify the returned unfiled child folder + assertTrue(unfiledChildFolder.getIsUnfiledRecordFolder()); + assertFalse(unfiledChildFolder.getIsRecord()); - assertEquals(childFolder.getName(), childFolderName); - assertEquals(childFolder.getNodeType(), UNFILED_RECORD_FOLDER_TYPE); - assertEquals(childFolder.getCreatedByUser().getId(), getAdminUser().getUsername()); + assertEquals(unfiledChildFolder.getName(), unfiledChildFolderName); + assertEquals(unfiledChildFolder.getNodeType(), UNFILED_RECORD_FOLDER_TYPE); + assertEquals(unfiledChildFolder.getCreatedByUser().getId(), getAdminUser().getUsername()); // Verify the returned file plan component properties - FilePlanComponentProperties childProperties = childFolder.getProperties(); - assertEquals(childProperties.getTitle(), childFolderTitle); - assertEquals(childProperties.getDescription(), childFolderDescription); + UnfiledRecordFolder unfiledChildRecordFolder = getRestAPIFactory().getUnfiledRecordFoldersAPI() + .getUnfiledRecordFolder(unfiledChildFolder.getId()); + // Verify the returned file plan component properties + UnfiledContainerChildProperties unfiledChildFolderProperties = unfiledChildRecordFolder.getProperties(); + assertEquals(unfiledChildFolderProperties.getTitle(), FilePlanComponentsUtil.TITLE_PREFIX + unfiledChildFolderName); + assertEquals(unfiledChildFolderProperties.getDescription(), FilePlanComponentsUtil.DESCRIPTION_PREFIX + unfiledChildFolderName); // Does this child point to its parent? - assertEquals(childFolder.getParentId(), parentFolder.getId()); + assertEquals(unfiledChildFolder.getParentId(), unfiledParentFolder.getId()); // Does child's parent point to it? // Perform another call as our parentFolder had been executed before childFolder existed - FilePlanComponentsCollection parentsChildren = filePlanComponentsAPI.listChildComponents(parentFolder.getId()); + UnfiledContainerChildCollection parentsChildren = getRestAPIFactory().getUnfiledRecordFoldersAPI().getUnfiledRecordFolderChildren(unfiledParentFolder.getId()); assertStatusCode(OK); List childIds = parentsChildren.getEntries() .stream() - .map(c -> c.getFilePlanComponentModel().getId()) + .map(c -> c.getEntry().getId()) .collect(Collectors.toList()); // Child folder is listed in parent - assertTrue(childIds.contains(childFolder.getId())); + assertTrue(childIds.contains(unfiledChildFolder.getId())); // There should be only one child assertEquals(1, childIds.size()); @@ -244,61 +320,96 @@ public class UnfiledRecordsFolderTests extends BaseRMRestTest public void editUnfiledRecordsFolder() throws Exception { String modified = "Modified "; - String folderName = "Folder To Modify" + getRandomAlphanumeric(); + String unfiledFolderName = "UnfiledFolderToModify" + getRandomAlphanumeric(); + String rootUnfiledFolderName = "RootUnfiledFolder" + getRandomAlphanumeric(); + //create root unfiledRecordFolder + UnfiledContainerChild createUnfiledContainerChild = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS,rootUnfiledFolderName, UNFILED_RECORD_FOLDER_TYPE); // No need for fine control, create it using utility function - FilePlanComponent folderToModify = createUnfiledRecordsFolder(UNFILED_RECORDS_CONTAINER_ALIAS, folderName); - assertEquals(folderName, folderToModify.getName()); + UnfiledContainerChild unfiledFolderToModify = createUnfiledRecordsFolderChild(createUnfiledContainerChild.getId(), + unfiledFolderName, UNFILED_RECORD_FOLDER_TYPE); + assertEquals(unfiledFolderName, unfiledFolderToModify.getName()); // Build the properties which will be updated - FilePlanComponent folderToUpdate = FilePlanComponent.builder() - .name(modified + folderToModify.getName()) - .properties(FilePlanComponentProperties.builder(). - title(modified + folderToModify.getProperties().getTitle()). - description(modified + folderToModify.getProperties().getDescription()) - .build()) - .build(); + UnfiledRecordFolder unfiledChildFolderModel = UnfiledRecordFolder.builder().name(modified + unfiledFolderName) + .properties(UnfiledContainerChildProperties.builder().title(modified + unfiledFolderToModify.getProperties().getTitle()) + .description(modified + unfiledFolderToModify.getProperties().getDescription()).build()) + .build(); // Update the unfiled records folder - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - filePlanComponentsAPI.updateFilePlanComponent(folderToUpdate, folderToModify.getId()); + getRestAPIFactory().getUnfiledRecordFoldersAPI().updateUnfiledRecordFolder(unfiledChildFolderModel, unfiledFolderToModify.getId()); // Verify the status code assertStatusCode(OK); // This is to ensure the change was actually applied, rather than simply trusting the object returned by PUT - FilePlanComponent renamedFolder = filePlanComponentsAPI.getFilePlanComponent(folderToModify.getId()); + UnfiledRecordFolder renamedUnfiledFolder = getRestAPIFactory().getUnfiledRecordFoldersAPI().getUnfiledRecordFolder(unfiledFolderToModify.getId()); // Verify the returned file plan component - assertEquals(modified + folderToModify.getName(), renamedFolder.getName()); - assertEquals(modified + folderToModify.getProperties().getTitle(), renamedFolder.getProperties().getTitle()); - assertEquals(modified + folderToModify.getProperties().getDescription(), renamedFolder.getProperties().getDescription()); + assertEquals(modified + unfiledFolderToModify.getName(), renamedUnfiledFolder.getName()); + assertEquals(modified + unfiledFolderToModify.getProperties().getTitle(), renamedUnfiledFolder.getProperties().getTitle()); + assertEquals(modified + unfiledFolderToModify.getProperties().getDescription(), renamedUnfiledFolder.getProperties().getDescription()); } /** - * Given an unfiled record folder + * Given an unfiled record folder and some records inside * When I delete the unfiled record folder via the ReST API - * Then the unfiled record folder is deleted + * Then the unfiled record folder is deleted and its content too * * @throws Exception for failed actions */ @Test(description = "Delete unfiled record folder") public void deleteUnfiledRecordsFolder() throws Exception { - String folderName = "Folder To Delete" + getRandomAlphanumeric(); + String unfiledFolderName = "UnfiledFolderToDelete" + getRandomAlphanumeric(); + String nonElectronicRecordName = "NonElectronicRecord" + getRandomAlphanumeric(); + String electronicRecordName = "ElectronicRecord" + getRandomAlphanumeric(); + String rootUnfiledFolderName = "RootUnfiledFolder" + getRandomAlphanumeric(); - // Create folderToDelete - FilePlanComponent folderToDelete = createUnfiledRecordsFolder(UNFILED_RECORDS_CONTAINER_ALIAS, folderName); - assertEquals(folderName, folderToDelete.getName()); + //create root unfiled record folder + UnfiledContainerChild createUnfiledContainerChild = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, rootUnfiledFolderName, UNFILED_RECORD_FOLDER_TYPE); + // Create unfiledFolderToDelete + UnfiledContainerChild unfiledFolderToDelete = createUnfiledRecordsFolderChild(createUnfiledContainerChild.getId(), + unfiledFolderName, UNFILED_RECORD_FOLDER_TYPE); + assertEquals(unfiledFolderName, unfiledFolderToDelete.getName()); + + // Create a non electronic record under unfiledFolderToDelete + UnfiledContainerChild nonElectronicRecord = createUnfiledRecordsFolderChild(unfiledFolderToDelete.getId(), + nonElectronicRecordName, NON_ELECTRONIC_RECORD_TYPE); + assertTrue(nonElectronicRecord.getParentId().equals(unfiledFolderToDelete.getId())); + + // Create an electronic record under unfiledFolderToDelete + UnfiledContainerChild electronicRecord = createUnfiledRecordsFolderChild(unfiledFolderToDelete.getId(), + electronicRecordName, CONTENT_TYPE); + assertTrue(electronicRecord.getParentId().equals(unfiledFolderToDelete.getId())); // Delete folderToDelete - FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI(); - filePlanComponentsAPI.deleteFilePlanComponent(folderToDelete.getId()); + getRestAPIFactory().getUnfiledRecordFoldersAPI().deleteUnfiledRecordFolder(unfiledFolderToDelete.getId()); // Verify the status code assertStatusCode(NO_CONTENT); // Deleted component should no longer be retrievable - filePlanComponentsAPI.getFilePlanComponent(folderToDelete.getId()); + getRestAPIFactory().getUnfiledRecordFoldersAPI().getUnfiledRecordFolder(unfiledFolderToDelete.getId()); assertStatusCode(NOT_FOUND); } + + @AfterMethod + @AfterClass (alwaysRun = true) + public void tearDown() throws Exception + { + UnfiledContainerChildCollection listedChildren = getRestAPIFactory().getUnfiledContainersAPI() + .getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS); + + listedChildren.getEntries().forEach(UnfiledContainerChildEntry -> + { + if (UnfiledContainerChildEntry.getEntry().getIsRecord()) + { + getRestAPIFactory().getRecordsAPI().deleteRecord(UnfiledContainerChildEntry.getEntry().getId()); + } + else + { + getRestAPIFactory().getUnfiledRecordFoldersAPI().deleteUnfiledRecordFolder(UnfiledContainerChildEntry.getEntry().getId()); + } + }); + } } diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UpdateRecordsTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UpdateRecordsTests.java index 88d245eec4..c0b7ce55f2 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UpdateRecordsTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplancomponents/UpdateRecordsTests.java @@ -26,9 +26,16 @@ */ package org.alfresco.rest.rm.community.fileplancomponents; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.IMAGE_FILE; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicRecordModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicUnfiledContainerChildModel; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createNonElectronicRecordModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createNonElectronicUnfiledContainerChildModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createRecordModel; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.getFile; +import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.OK; @@ -37,16 +44,22 @@ import static org.testng.Assert.assertEquals; import java.util.Arrays; import org.alfresco.rest.rm.community.base.BaseRMRestTest; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent; -import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; +import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; import org.alfresco.rest.rm.community.model.user.UserPermissions; import org.alfresco.rest.rm.community.model.user.UserRoles; -import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI; -import org.alfresco.rest.rm.community.requests.igCoreAPI.RMUserAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RMUserAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI; +import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledRecordFolderAPI; import org.alfresco.test.AlfrescoTest; import org.alfresco.utility.constants.UserRole; import org.alfresco.utility.model.SiteModel; import org.alfresco.utility.model.UserModel; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** @@ -58,10 +71,100 @@ import org.testng.annotations.Test; * @since 2.6 */ public class UpdateRecordsTests extends BaseRMRestTest -{ +{ /* to be used to append to modifications */ private final String MODIFIED_PREFIX = "modified_"; + /** Incomplete electronic and non electronic records created in one record folder, unfiled records container and one unfiled record folder */ + @DataProvider(name = "incompleteRecords") + public String[][] getIncompleteRecords() throws Exception + { + //create electronic and nonElectronic record in record folder + String recordFolderId = createCategoryFolderInFilePlan().getId(); + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + + Record electronicRecord = recordFolderAPI.createRecord(createElectronicRecordModel(), recordFolderId, getFile(IMAGE_FILE)); + assertStatusCode(CREATED); + + Record nonElectronicRecord = recordFolderAPI.createRecord(createNonElectronicRecordModel(), recordFolderId); + assertStatusCode(CREATED); + + //create electronic record and nonElectronic record in unfiled records container + UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI(); + UnfiledContainerChild electronicRecord1 = unfiledContainersAPI.uploadRecord(createElectronicUnfiledContainerChildModel(), UNFILED_RECORDS_CONTAINER_ALIAS, getFile(IMAGE_FILE)); + assertStatusCode(CREATED); + + UnfiledContainerChild nonElectronicRecord1 = unfiledContainersAPI.createUnfiledContainerChild(createNonElectronicUnfiledContainerChildModel(), UNFILED_RECORDS_CONTAINER_ALIAS); + assertStatusCode(CREATED); + + //create electronic record and nonElectronic record in unfiled record folder + String unfiledRecordFolderId = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, "Unfiled Folder " + getRandomAlphanumeric(), UNFILED_RECORD_FOLDER_TYPE).getId(); + UnfiledRecordFolderAPI unfiledRecordFoldersAPI = getRestAPIFactory().getUnfiledRecordFoldersAPI(); + UnfiledContainerChild electronicRecord2 = unfiledRecordFoldersAPI.uploadRecord(createElectronicUnfiledContainerChildModel(), unfiledRecordFolderId, getFile(IMAGE_FILE)); + assertStatusCode(CREATED); + + UnfiledContainerChild nonElectronicRecord2 = unfiledRecordFoldersAPI.createUnfiledRecordFolderChild(createNonElectronicUnfiledContainerChildModel(), unfiledRecordFolderId); + assertStatusCode(CREATED); + + return new String[][] + { + // an arbitrary record folder + { electronicRecord.getId(), nonElectronicRecord.getId()}, + // unfiled records root + { electronicRecord1.getId(), nonElectronicRecord1.getId()}, + // an arbitrary unfiled records folder + { electronicRecord2.getId(), nonElectronicRecord2.getId()} + }; + } + + /** Complete electronic and non electronic records created in one record folder, unfiled records container and one unfiled record folder */ + @DataProvider(name = "completeRecords") + public String[][] getCompleteRecords() throws Exception + { + //create electronic and nonElectronic record in record folder + String recordFolderId = createCategoryFolderInFilePlan().getId(); + RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); + + Record electronicRecord = recordFolderAPI.createRecord(createElectronicRecordModel(), recordFolderId, getFile(IMAGE_FILE)); + assertStatusCode(CREATED); + completeRecord(electronicRecord.getId()); + + Record nonElectronicRecord = recordFolderAPI.createRecord(createNonElectronicRecordModel(), recordFolderId); + assertStatusCode(CREATED); + completeRecord(nonElectronicRecord.getId()); + + //create electronic record and nonElectronic record in unfiled records container + UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI(); + UnfiledContainerChild electronicRecord1 = unfiledContainersAPI.uploadRecord(createElectronicUnfiledContainerChildModel(), UNFILED_RECORDS_CONTAINER_ALIAS, getFile(IMAGE_FILE)); + assertStatusCode(CREATED); + completeRecord(electronicRecord1.getId()); + + UnfiledContainerChild nonElectronicRecord1 = unfiledContainersAPI.createUnfiledContainerChild(createNonElectronicUnfiledContainerChildModel(), UNFILED_RECORDS_CONTAINER_ALIAS); + assertStatusCode(CREATED); + completeRecord(nonElectronicRecord1.getId()); + + //create electronic record and nonElectronic record in unfiled record folder + String unfiledRecordFolderId = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, "Unfiled Folder " + getRandomAlphanumeric(), UNFILED_RECORD_FOLDER_TYPE).getId(); + UnfiledRecordFolderAPI unfiledRecordFoldersAPI = getRestAPIFactory().getUnfiledRecordFoldersAPI(); + UnfiledContainerChild electronicRecord2 = unfiledRecordFoldersAPI.uploadRecord(createElectronicUnfiledContainerChildModel(), unfiledRecordFolderId, getFile(IMAGE_FILE)); + assertStatusCode(CREATED); + completeRecord(electronicRecord2.getId()); + + UnfiledContainerChild nonElectronicRecord2 = unfiledRecordFoldersAPI.createUnfiledRecordFolderChild(createNonElectronicUnfiledContainerChildModel(), unfiledRecordFolderId); + assertStatusCode(CREATED); + completeRecord(nonElectronicRecord2.getId()); + + return new String[][] + { + // an arbitrary record folder + { electronicRecord.getId(), nonElectronicRecord.getId()}, + // unfiled records root + { electronicRecord1.getId(), nonElectronicRecord1.getId()}, + // an arbitrary unfiled records folder + { electronicRecord2.getId(), nonElectronicRecord2.getId()} + }; + } + /** *
      * Given an incomplete record
@@ -72,40 +175,30 @@ public class UpdateRecordsTests extends BaseRMRestTest
      */
     @Test
     (
-        dataProvider = "validRootContainers",
+        dataProvider = "incompleteRecords",
         description = "Incomplete records can be updated"
     )
     @AlfrescoTest(jira="RM-4362")
-    public void incompleteRecordsCanBeUpdated(FilePlanComponent recordFolder) throws Exception
+    public void incompleteRecordsCanBeUpdated(String electronicRecordId, String nonElectronicRecordId) throws Exception
     {
-        FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI();
+        // Get the recordsAPI
+        RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
+        Record electronicRecord = recordsAPI.getRecord(electronicRecordId);
+        Record nonElectronicRecord = recordsAPI.getRecord(nonElectronicRecordId);
 
-        // create electronic and non-electronic records in a folder
-        FilePlanComponent electronicRecord = filePlanComponentsAPI.createElectronicRecord(createElectronicRecordModel(), IMAGE_FILE, recordFolder.getId());
-        assertStatusCode(CREATED);
-        FilePlanComponent nonElectronicRecord = filePlanComponentsAPI.createFilePlanComponent(createNonElectronicRecordModel(), recordFolder.getId());
-        assertStatusCode(CREATED);
-
-        for (FilePlanComponent record: Arrays.asList(electronicRecord, nonElectronicRecord)) {
-            // generate update metadata
+        for (Record record: Arrays.asList(electronicRecord, nonElectronicRecord)) {
+            // Generate update metadata
             String newName = getModifiedPropertyValue(record.getName());
             String newTitle = getModifiedPropertyValue(record.getProperties().getTitle());
             String newDescription = getModifiedPropertyValue(record.getProperties().getDescription());
+            Record recordModel = createRecordModel(newName, newDescription, newTitle);
 
-            FilePlanComponent updateRecord = FilePlanComponent.builder()
-                .name(newName)
-                .properties(FilePlanComponentProperties.builder()
-                    .description(newDescription)
-                    .title(newTitle)
-                    .build())
-                .build();
-
-            // update record
-            filePlanComponentsAPI.updateFilePlanComponent(updateRecord, record.getId());
+            // Update record
+            recordsAPI.updateRecord(recordModel, record.getId());
             assertStatusCode(OK);
 
-            // verify the update got applied
-            FilePlanComponent updatedRecord = filePlanComponentsAPI.getFilePlanComponent(record.getId());
+            // Verify the original record meta data has been retained
+            Record updatedRecord = recordsAPI.getRecord(record.getId());
             assertEquals(updatedRecord.getName(), newName);
             assertEquals(updatedRecord.getProperties().getTitle(), newTitle);
             assertEquals(updatedRecord.getProperties().getDescription(), newDescription);
@@ -127,9 +220,9 @@ public class UpdateRecordsTests extends BaseRMRestTest
     )
     @AlfrescoTest(jira="RM-4362")
     public void userWithEditMetadataCapsCanUpdateMetadata() throws Exception
-    {   
+    {
         RMUserAPI rmUserAPI = getRestAPIFactory().getRMUserAPI();
-        // create test user and add it with collab. privileges
+        // Create test user and add it with collab. privileges
         UserModel updateUser = getDataUser().createRandomTestUser("updateuser");
         updateUser.setUserRole(UserRole.SiteCollaborator);
         getDataUser().addUserToSite(updateUser, new SiteModel(getRestAPIFactory().getRMSiteAPI().getSite().getId()), UserRole.SiteCollaborator);
@@ -138,50 +231,44 @@ public class UpdateRecordsTests extends BaseRMRestTest
         rmUserAPI.assignRoleToUser(updateUser.getUsername(), UserRoles.ROLE_RM_SECURITY_OFFICER);
         assertStatusCode(OK);
 
-        // create random folder
-        FilePlanComponent randomFolder = createCategoryFolderInFilePlan();
-        logger.info("random folder:" + randomFolder.getName());
+        // Create random folder
+        RecordCategoryChild recordFolder = createCategoryFolderInFilePlan();
+        logger.info("random folder:" + recordFolder.getName());
 
-        // grant updateUser Filing privileges on randomFolder category, this will be
-        // inherited to randomFolder
-        FilePlanComponentAPI filePlanComponentsAPIAsAdmin = getRestAPIFactory().getFilePlanComponentsAPI();
-        rmUserAPI.addUserPermission(filePlanComponentsAPIAsAdmin.getFilePlanComponent(randomFolder.getParentId()),
+        // Grant updateUser Filing privileges on randomFolder category, this will be
+        // Inherited to randomFolder
+        RecordCategoryAPI recordCategoryAPI = getRestAPIFactory().getRecordCategoryAPI();
+        rmUserAPI.addUserPermission(recordCategoryAPI.getRecordCategory(recordFolder.getParentId()).getId(),
             updateUser, UserPermissions.PERMISSION_FILING);
         assertStatusCode(OK);
-        
-        // create electronic and non-electronic records in a folder
-        FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI();
-        FilePlanComponent electronicRecord = filePlanComponentsAPI.createElectronicRecord(createElectronicRecordModel(), IMAGE_FILE, randomFolder.getId());
+
+        // Create electronic and non-electronic records in a folder
+        RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI();
+        Record electronicRecord = recordFolderAPI.createRecord(createElectronicRecordModel(), recordFolder.getId(), getFile(IMAGE_FILE));
         assertStatusCode(CREATED);
-        FilePlanComponent nonElectronicRecord = filePlanComponentsAPI.createFilePlanComponent(createNonElectronicRecordModel(), randomFolder.getId());
+        Record nonElectronicRecord = recordFolderAPI.createRecord(createNonElectronicRecordModel(), recordFolder.getId());
         assertStatusCode(CREATED);
 
-        // get FilePlanComponentAPI instance initialised to updateUser
-        FilePlanComponentAPI filePlanComponentsAPIAsUser = getRestAPIFactory().getFilePlanComponentsAPI(updateUser);
+        // Get recordsAPI instance initialised to updateUser
+        RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI(updateUser);
 
-        for (FilePlanComponent record: Arrays.asList(electronicRecord, nonElectronicRecord)) {
-            filePlanComponentsAPIAsUser.getFilePlanComponent(record.getId());
+        for (Record record: Arrays.asList(electronicRecord, nonElectronicRecord))
+        {
+            recordsAPI.getRecord(record.getId());
             assertStatusCode(OK);
 
-            // generate update metadata
+            // Generate update metadata
             String newName = getModifiedPropertyValue(record.getName());
             String newTitle = getModifiedPropertyValue(record.getProperties().getTitle());
             String newDescription = getModifiedPropertyValue(record.getProperties().getDescription());
+            Record recordModel = createRecordModel(newName, newDescription, newTitle);
 
-            FilePlanComponent updateRecord = FilePlanComponent.builder()
-                .name(newName)
-                .properties(FilePlanComponentProperties.builder()
-                    .description(newDescription)
-                    .title(newTitle)
-                    .build())
-                .build();
-
-            // update record
-            filePlanComponentsAPIAsUser.updateFilePlanComponent(updateRecord, record.getId());
+            // Update record
+            recordsAPI.updateRecord(recordModel, record.getId());
             assertStatusCode(OK);
 
-            // verify the update got applied
-            FilePlanComponent updatedRecord = filePlanComponentsAPIAsUser.getFilePlanComponent(record.getId());
+            // Verify the update got applied
+            Record updatedRecord = recordsAPI.getRecord(record.getId());
             assertEquals(updatedRecord.getName(), newName);
             assertEquals(updatedRecord.getProperties().getTitle(), newTitle);
             assertEquals(updatedRecord.getProperties().getDescription(), newDescription);
@@ -200,46 +287,33 @@ public class UpdateRecordsTests extends BaseRMRestTest
      */
     @Test
     (
-        dataProvider = "validRootContainers",
+        dataProvider = "completeRecords",
         description = "Complete records can't be updated"
     )
     @AlfrescoTest(jira="RM-4362")
-    public void completeRecordsCantBeUpdated(FilePlanComponent recordFolder) throws Exception
+    public void completeRecordsCantBeUpdated(String electronicRecordId, String nonElectronicRecordId) throws Exception
     {
-        FilePlanComponentAPI filePlanComponentsAPI = getRestAPIFactory().getFilePlanComponentsAPI();
+        // Get the recordsAPI
+        RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
+        Record electronicRecord = recordsAPI.getRecord(electronicRecordId);
+        Record nonElectronicRecord = recordsAPI.getRecord(nonElectronicRecordId);
 
-        // create electronic and non-electronic records in a folder
-        FilePlanComponent electronicRecord = filePlanComponentsAPI.createElectronicRecord(createElectronicRecordModel(), IMAGE_FILE, recordFolder.getId());
-        assertStatusCode(CREATED);
-        closeRecord(electronicRecord);
-
-        FilePlanComponent nonElectronicRecord = filePlanComponentsAPI.createFilePlanComponent(createNonElectronicRecordModel(), recordFolder.getId());
-        assertStatusCode(CREATED);
-        closeRecord(nonElectronicRecord);
-
-        for (FilePlanComponent record: Arrays.asList(electronicRecord, nonElectronicRecord)) {
-            // generate update metadata
+        for (Record record: Arrays.asList(electronicRecord, nonElectronicRecord)) {
+            // Generate update metadata
             String newName = getModifiedPropertyValue(record.getName());
             String newTitle = getModifiedPropertyValue(record.getProperties().getTitle());
             String newDescription = getModifiedPropertyValue(record.getProperties().getDescription());
+            Record recordModel = createRecordModel(newName, newDescription, newTitle);
 
-            FilePlanComponent updateRecord = FilePlanComponent.builder()
-                .name(newName)
-                .properties(FilePlanComponentProperties.builder()
-                    .description(newDescription)
-                    .title(newTitle)
-                    .build())
-                .build();
-
-            // attempt to update record
-            filePlanComponentsAPI.updateFilePlanComponent(updateRecord, record.getId());
+            // Update record
+            recordsAPI.updateRecord(recordModel, record.getId());
             assertStatusCode(FORBIDDEN);
 
-            // verify the original record metatada has been retained
-            FilePlanComponent updatedRecord = filePlanComponentsAPI.getFilePlanComponent(record.getId());
+            // Verify the original record meta data has been retained
+            Record updatedRecord = recordsAPI.getRecord(record.getId());
             assertEquals(updatedRecord.getName(), record.getName());
             assertEquals(updatedRecord.getProperties().getTitle(), record.getProperties().getTitle());
-            assertEquals(updatedRecord.getProperties().getDescription(), record.getProperties().getTitle());
+            assertEquals(updatedRecord.getProperties().getDescription(), record.getProperties().getDescription());
         }
     }
 
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/files/DeclareDocumentAsRecordTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/files/DeclareDocumentAsRecordTests.java
index 6e50c8ed17..fa9881e07b 100644
--- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/files/DeclareDocumentAsRecordTests.java
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/files/DeclareDocumentAsRecordTests.java
@@ -41,13 +41,12 @@ import java.util.stream.Collectors;
 import org.alfresco.dataprep.CMISUtil;
 import org.alfresco.rest.model.RestNodeModel;
 import org.alfresco.rest.rm.community.base.BaseRMRestTest;
-import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent;
-import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentEntry;
-import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties;
-import org.alfresco.rest.rm.community.requests.igCoreAPI.FilePlanComponentAPI;
+import org.alfresco.rest.rm.community.model.record.Record;
+import org.alfresco.rest.rm.community.model.record.RecordProperties;
+import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildEntry;
+import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI;
 import org.alfresco.test.AlfrescoTest;
 import org.alfresco.utility.constants.UserRole;
-import org.alfresco.utility.data.DataUser;
 import org.alfresco.utility.model.FileModel;
 import org.alfresco.utility.model.FolderModel;
 import org.alfresco.utility.model.SiteModel;
@@ -55,7 +54,6 @@ import org.alfresco.utility.model.UserModel;
 import org.apache.chemistry.opencmis.client.api.Document;
 import org.apache.chemistry.opencmis.client.api.SecondaryType;
 import org.apache.commons.codec.digest.DigestUtils;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -67,9 +65,6 @@ import org.testng.annotations.Test;
  */
 public class DeclareDocumentAsRecordTests extends BaseRMRestTest
 {
-    @Autowired
-    private DataUser dataUser;
-
     private UserModel testUser, testUserReadOnly;
     private SiteModel testSite;
     private FolderModel testFolder;
@@ -78,13 +73,13 @@ public class DeclareDocumentAsRecordTests extends BaseRMRestTest
     public void declareDocumentAsRecordSetup() throws Exception
     {
         // create test user and test collaboration site to store documents in
-        testUser = dataUser.createRandomTestUser();
-        testUserReadOnly = dataUser.createRandomTestUser();
+        testUser = getDataUser().createRandomTestUser();
+        testUserReadOnly = getDataUser().createRandomTestUser();
 
         testSite = dataSite.usingAdmin().createPublicRandomSite();
 
-        dataUser.addUserToSite(testUser, testSite, UserRole.SiteContributor);
-        dataUser.addUserToSite(testUserReadOnly, testSite, UserRole.SiteConsumer);
+        getDataUser().addUserToSite(testUser, testSite, UserRole.SiteContributor);
+        getDataUser().addUserToSite(testUserReadOnly, testSite, UserRole.SiteConsumer);
 
         testFolder = dataContent.usingSite(testSite).usingUser(testUser).createFolder();
     }
@@ -112,15 +107,15 @@ public class DeclareDocumentAsRecordTests extends BaseRMRestTest
             .createContent(CMISUtil.DocumentType.TEXT_PLAIN);
 
         // declare document as record
-        FilePlanComponent record = getRestAPIFactory().getFilesAPI(testUser).declareAsRecord(document.getNodeRefWithoutVersion());
+        Record record = getRestAPIFactory().getFilesAPI(testUser).declareAsRecord(document.getNodeRefWithoutVersion());
         assertStatusCode(CREATED);
 
         // verify the declared record is in Unfiled Records folder
-        FilePlanComponentAPI filePlanComponentAPI = getRestAPIFactory().getFilePlanComponentsAPI();
-        List matchingRecords = filePlanComponentAPI.listChildComponents(UNFILED_RECORDS_CONTAINER_ALIAS)
+        UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI();
+        List matchingRecords = unfiledContainersAPI.getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS)
             .getEntries()
             .stream()
-            .filter(e -> e.getFilePlanComponentModel().getId().equals(document.getNodeRefWithoutVersion()))
+            .filter(e -> e.getEntry().getId().equals(document.getNodeRefWithoutVersion()))
             .collect(Collectors.toList());
         // there should be only one matching record corresponding this document
         assertEquals(matchingRecords.size(), 1, "More than one record matching document name");
@@ -136,7 +131,7 @@ public class DeclareDocumentAsRecordTests extends BaseRMRestTest
 
         // verify the new name has the form of " ()."
         String recordName = filesAfterRename.get(0).onModel().getName();
-        assertEquals(recordName, document.getName().replace(".", String.format(" (%s).", record.getProperties().getRmIdentifier())));
+        assertEquals(recordName, document.getName().replace(".", String.format(" (%s).", record.getProperties().getIdentifier())));
 
         // verify the document in collaboration site is now a record, note the file is now renamed hence folder + doc. name concatenation
         // this also verifies the document is still in the initial folder
@@ -208,16 +203,16 @@ public class DeclareDocumentAsRecordTests extends BaseRMRestTest
     public void recordCantBeDeclaredARecord() throws Exception
     {
         // create a non-electronic record in a random folder
-        FilePlanComponent nonelectronicRecord = FilePlanComponent.builder()
-            .properties(FilePlanComponentProperties.builder()
+        Record nonelectronicRecord = Record.builder()
+            .properties(RecordProperties.builder()
                 .description("Description")
                 .title("Title")
                 .build())
             .name("Non-Electronic Record")
             .nodeType(NON_ELECTRONIC_RECORD_TYPE)
             .build();
-        FilePlanComponent record = getRestAPIFactory().getFilePlanComponentsAPI()
-            .createFilePlanComponent(nonelectronicRecord, createCategoryFolderInFilePlan().getId());
+        Record record = getRestAPIFactory().getRecordFolderAPI()
+            .createRecord(nonelectronicRecord, createCategoryFolderInFilePlan().getId());
         assertStatusCode(CREATED);
 
         // try to declare it as a record
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/site/RMSiteTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/site/RMSiteTests.java
index 5cc5dffb32..709881d2e4 100644
--- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/site/RMSiteTests.java
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/site/RMSiteTests.java
@@ -51,7 +51,7 @@ import static org.testng.Assert.assertNotNull;
 import org.alfresco.rest.rm.community.base.BaseRMRestTest;
 import org.alfresco.rest.rm.community.base.TestData;
 import org.alfresco.rest.rm.community.model.site.RMSite;
-import org.alfresco.rest.rm.community.requests.igCoreAPI.RMSiteAPI;
+import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI;
 import org.alfresco.utility.data.RandomData;
 import org.alfresco.utility.model.UserModel;
 import org.alfresco.utility.report.Bug;
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java
index 98ec28e790..876523f7c1 100644
--- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java
@@ -26,17 +26,32 @@
  */
 package org.alfresco.rest.rm.community.utils;
 
+import static java.nio.charset.Charset.forName;
+
+import static com.google.common.io.Resources.getResource;
+
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.NON_ELECTRONIC_RECORD_TYPE;
+import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE;
+import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE;
 import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.OutputStreamWriter;
-import java.nio.charset.Charset;
 
-import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent;
-import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties;
+import org.alfresco.rest.rm.community.model.record.Record;
+import org.alfresco.rest.rm.community.model.record.RecordProperties;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChildProperties;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryProperties;
+import org.alfresco.rest.rm.community.model.recordfolder.RecordFolder;
+import org.alfresco.rest.rm.community.model.recordfolder.RecordFolderProperties;
+import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild;
+import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildProperties;
 
 /**
  * Utility class for file plan component models
@@ -51,18 +66,35 @@ public class FilePlanComponentsUtil
         // Intentionally blank
     }
 
-    /** image resource file to be used for records body */
+    /** Name of the image resource file to be used for records body */
     public static final String IMAGE_FILE = "money.JPG";
 
+    /** Title prefix for record category children */
+    public static final String TITLE_PREFIX = "Title for ";
+
+    /** Description prefix for record category children */
+    public static final String DESCRIPTION_PREFIX = "This is the description for";
+
+
+    /**
+     * Helper method to get a file by its name
+     *
+     * @return The file
+     */
+    public static File getFile(String fileName)
+    {
+        return new File(getResource(fileName).getFile());
+    }
+
     /**
      *  Creates a record model with the given type and a random name (with "Record " prefix)
      *
      * @param nodeType The node type
-     * @return The {@link FilePlanComponent} with for the given node type
+     * @return The {@link Record} with for the given node type
      */
-    private static FilePlanComponent createRecordModel(String nodeType)
+    private static Record createRecordModel(String nodeType)
     {
-        return FilePlanComponent.builder()
+        return Record.builder()
                 .name("Record " + getRandomAlphanumeric())
                 .nodeType(nodeType)
                 .build();
@@ -71,67 +103,254 @@ public class FilePlanComponentsUtil
     /**
      * Creates an electronic record model with a random name (with "Record " prefix)
      *
-     * @return The electronic record as {@link FilePlanComponent}
+     * @return The electronic record as {@link Record}
      */
-    public static FilePlanComponent createElectronicRecordModel()
+    public static Record createElectronicRecordModel()
     {
         return createRecordModel(CONTENT_TYPE);
     }
 
+    /**
+     * Creates a non-electronic unfiled container child model with a random name (with "Record " prefix)
+     *
+     * @return The electronic record as {@link UnfiledContainerChild}
+     */
+    public static UnfiledContainerChild createElectronicUnfiledContainerChildModel()
+    {
+        return createUnfiledContainerChildRecordModel("Record " + getRandomAlphanumeric(), CONTENT_TYPE);
+    }
+
+    /**
+     * Creates an electronic unfiled container child model with a random name (with "Record " prefix)
+     *
+     * @return The electronic record as {@link UnfiledContainerChild}
+     */
+    public static UnfiledContainerChild createNonElectronicUnfiledContainerChildModel()
+    {
+        return createUnfiledContainerChildRecordModel("Record " + getRandomAlphanumeric(), NON_ELECTRONIC_RECORD_TYPE);
+    }
+
+    /**
+     * Creates an unfiled records container child record model with the given name and type
+     *
+     * @param name The name of the unfiled records container child
+     * @param nodeType The type of the record category child
+     * @return The {@link UnfiledContainerChild} with the given details
+     */
+    public static UnfiledContainerChild createUnfiledContainerChildRecordModel(String name, String nodeType)
+    {
+        return UnfiledContainerChild.builder()
+                .name(name)
+                .nodeType(nodeType)
+                .build();
+    }
+
+    /**
+     * Creates a nonElectronic container child record model with all available properties for the non electronic records
+     *
+     * @param name The name of the unfiled records container child
+     * @param nodeType The type of the record category child
+     * @return The {@link UnfiledContainerChild} with the given details
+     */
+    public static UnfiledContainerChild createFullNonElectronicUnfiledContainerChildRecordModel(String name, String title, String description, String box, String file,
+                                                                                                String shelf, String storageLocation, Integer numberOfCopies, Integer physicalSize)
+    {
+        return UnfiledContainerChild.builder()
+                    .name(name)
+                    .nodeType(NON_ELECTRONIC_RECORD_TYPE)
+                    .properties(UnfiledContainerChildProperties.builder()
+                            .title(title)
+                            .description(description)
+                            .box(box)
+                            .file(file)
+                            .shelf(shelf)
+                            .storageLocation(storageLocation)
+                            .numberOfCopies(numberOfCopies)
+                            .physicalSize(physicalSize)
+                            .build())
+                    .build();
+    }
+
     /**
      * Creates a non-electronic record model with a random name (with "Record " prefix)
      *
-     * @return The non-electronic record as {@link FilePlanComponent}
+     * @return The non-electronic record as {@link Record}
      */
-    public static FilePlanComponent createNonElectronicRecordModel()
+    public static Record createNonElectronicRecordModel()
     {
         return createRecordModel(NON_ELECTRONIC_RECORD_TYPE);
     }
 
     /**
-     * Creates a file plan component with the given name, type and title
+     * Creates a non-electronic record model with with all available properties for the non electronic records
      *
-     * @param name The name of the file plan component
-     * @param type The type of the file plan component
-     * @param title The title of the file plan component
-     * @return The {@link FilePlanComponent} with the given details
+     * @return The non-electronic record as {@link Record}
      */
-    public static FilePlanComponent createFilePlanComponentModel(String name, String type, String title)
+    public static Record createFullNonElectronicRecordModel(String name, String title, String description, String box, String file,
+                                                            String shelf, String storageLocation, Integer numberOfCopies, Integer physicalSize)
     {
-        return FilePlanComponent.builder()
+        return Record.builder()
+                    .name(name)
+                    .nodeType(NON_ELECTRONIC_RECORD_TYPE)
+                    .properties(RecordProperties.builder()
+                            .title(title)
+                            .description(description)
+                            .box(box)
+                            .file(file)
+                            .shelf(shelf)
+                            .storageLocation(storageLocation)
+                            .numberOfCopies(numberOfCopies)
+                            .physicalSize(physicalSize)
+                            .build())
+                    .build();
+    }
+
+    /**
+     * Creates a record model with the given name, description and title
+     *
+     * @param name The name of the record
+     * @param description The description of the record
+     * @param title The title of the record
+     * @return The {@link Record} with the given details
+     */
+    public static Record createRecordModel(String name, String description, String title)
+    {
+        return Record.builder()
                 .name(name)
-                .nodeType(type)
-                .properties(FilePlanComponentProperties.builder()
-                                .title(title)
-                                .build())
+                .properties(RecordProperties.builder()
+                        .description(description)
+                        .title(title)
+                        .build())
+                .build();
+    }
+
+    /**
+     * Creates a record category child model with the given name and type
+     *
+     * @param name The name of the record category child
+     * @param nodeType The type of the record category child
+     * @return The {@link RecordCategoryChild} with the given details
+     */
+    public static RecordCategoryChild createRecordCategoryChildModel(String name, String nodeType)
+    {
+        return RecordCategoryChild.builder()
+                .name(name)
+                .nodeType(nodeType)
+                .properties(RecordCategoryChildProperties.builder()
+                        .title(TITLE_PREFIX + name)
+                        .build())
+                .build();
+    }
+
+    /**
+     * Creates a record category model with the given name and title
+     *
+     * @param name The name of the record category
+     * @param title The title of the record category
+     * @return The {@link RecordCategory} with the given details
+     */
+    public static RecordCategory createRecordCategoryModel(String name, String title)
+    {
+        return RecordCategory.builder()
+                .name(name)
+                .nodeType(RECORD_CATEGORY_TYPE)
+                .properties(RecordCategoryProperties.builder()
+                        .title(title)
+                        .build())
+                .build();
+    }
+
+    /**
+     * Creates a record folder model with the given name and title
+     *
+     * @param name The name of the record folder
+     * @param title The title of the record folder
+     * @return The {@link RecordFolder} with the given details
+     */
+    public static RecordFolder createRecordFolderModel(String name, String title)
+    {
+        return RecordFolder.builder()
+                .name(name)
+                .nodeType(RECORD_FOLDER_TYPE)
+                .properties(RecordFolderProperties.builder()
+                        .title(title)
+                        .build())
+                .build();
+    }
+
+    /**
+     * Creates an unfiled records container child model with the given name and type
+     *
+     * @param name The name of the unfiled records container child
+     * @param nodeType The type of the record category child
+     * @return The {@link UnfiledContainerChild} with the given details
+     */
+    public static UnfiledContainerChild createUnfiledContainerChildModel(String name, String nodeType)
+    {
+        return UnfiledContainerChild.builder()
+                .name(name)
+                .nodeType(nodeType)
+                .properties(UnfiledContainerChildProperties.builder()
+                        .title(TITLE_PREFIX + name)
+                        .build())
                 .build();
     }
 
     /**
      * Create temp file with content
      *
-     * @param name file name
-     * @return {@link File} file
+     * @param name The file name
+     * @return {@link File} The created file
      */
     public static File createTempFile(final String name, String content)
     {
         try
         {
-            // create file
+            // Create file
             final File file = File.createTempFile(name, ".txt");
 
-            // create writer
-            try (FileOutputStream fos = new FileOutputStream(file);
-                 OutputStreamWriter writer = new OutputStreamWriter(fos, Charset.forName("UTF-8").newEncoder()))
+            // Create writer
+            try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), forName("UTF-8").newEncoder()))
             {
                 // place content in file
                 writer.write(content);
             }
 
             return file;
-        } catch (Exception exception)
+        }
+        catch (Exception exception)
         {
             throw new RuntimeException("Unable to create test file.", exception);
         }
     }
+
+    /**
+     * Helper method to verify all properties of a nonElectronic record
+     *
+     * @param nonElectronicRecord
+     * @param name
+     * @param title
+     * @param description
+     * @param box
+     * @param file
+     * @param shelf
+     * @param storageLocation
+     * @param numberOfCopies
+     * @param physicalSize
+     */
+    public static void verifyFullNonElectronicRecord(Record nonElectronicRecord, String name, String title, String description, String box, String file,
+                                                     String shelf, String storageLocation, Integer numberOfCopies, Integer physicalSize)
+    {
+        RecordProperties properties = nonElectronicRecord.getProperties();
+        assertEquals(title, properties.getTitle());
+        assertEquals(description, properties.getDescription());
+        assertEquals(box, properties.getBox());
+        assertEquals(file, properties.getFile());
+        assertEquals(shelf, properties.getShelf());
+        assertEquals(storageLocation, properties.getStorageLocation());
+        assertEquals(numberOfCopies, properties.getNumberOfCopies());
+        assertEquals(physicalSize, properties.getPhysicalSize());
+        assertTrue(nonElectronicRecord.getName().contains(properties.getIdentifier()));
+        assertTrue(nonElectronicRecord.getName().contains(name));
+    }
 }
diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-public-rest-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-public-rest-context.xml
index 34da746fb3..a268d0264b 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-public-rest-context.xml
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-public-rest-context.xml
@@ -6,7 +6,7 @@
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
      
-        
+        
     
 
     
@@ -23,43 +23,145 @@
         
     
 
-    
-        
-        
-        
-        
+    
+        
+        
+        
+        
+        
+        
+        
+    
+
+    
+        
+        
+    
+
+    
+        
+        
         
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
     
 
-    
-       
-          org.alfresco.rm.rest.api.RMNodes
-       
-       
-          
-       
-       
-          
-             
-          
-       
+    
+       
+       
+       
     
 
-    
-       
+    
+       
+       
+       
+       
     
 
-    
-       
+    
+       
+       
+       
+    
+
+    
+       
+       
+       
+       
+       
+    
+
+    
+       
+       
+       
+    
+
+    
+       
+       
+       
+       
+       
+    
+
+    
+       
+       
+       
+    
+
+    
+       
+       
+       
+       
+    
+
+    
+       
+       
+       
+    
+
+    
+       
+       
+       
+       
+       
     
 
     
-       
-       
+       
+       
+       
+       
+       
     
 
     
-       
+       
+       
+       
+       
+       
+    
+
+    
+       
+       
+       
+    
+
+    
+       
+       
+       
+       
+    
+
+    
+       
+       
+       
+    
+
+    
+       
+       
+       
+       
     
 
    
@@ -89,37 +191,13 @@
        
    
 
-
-    
-        
-        
-        
-        
-        
-        
-        
-    
-
-    
-       
-          org.alfresco.rm.rest.api.Records
-       
-       
-          
-       
-       
-          
-             
-          
-       
-    
-
    
    
        
             
                 
                 
+                
             
         
    
diff --git a/rm-community/rm-community-repo/pom.xml b/rm-community/rm-community-repo/pom.xml
index f61f1bf752..4a59bbbdb7 100644
--- a/rm-community/rm-community-repo/pom.xml
+++ b/rm-community/rm-community-repo/pom.xml
@@ -577,7 +577,7 @@
                                  org.alfresco
                                  alfresco-rm-community-rest-api-explorer
                                  ${project.version}
-                                 /ig-api-explorer
+                                 /gs-api-explorer
                                  war
                                  true
                               
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/RMNodes.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/RMNodes.java
index dc9a345a6d..f5e506edea 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/RMNodes.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/RMNodes.java
@@ -28,8 +28,11 @@
 package org.alfresco.rm.rest.api;
 
 import org.alfresco.rest.api.Nodes;
+import org.alfresco.rest.api.model.Node;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
 import org.alfresco.service.cmr.repository.NodeRef;
 import org.alfresco.service.namespace.QName;
+import org.springframework.extensions.webscripts.servlet.FormData;
 
 /**
  * RM Nodes API
@@ -37,6 +40,7 @@ import org.alfresco.service.namespace.QName;
  * @author Ana Bozianu
  * @since 2.6
  */
+@Deprecated
 public interface RMNodes extends Nodes
 {
     public static String PATH_FILE_PLAN = "-filePlan-";
@@ -47,10 +51,13 @@ public interface RMNodes extends Nodes
     public static String PARAM_INCLUDE_HAS_RETENTION_SCHEDULE = "hasRetentionSchedule";
     public static String PARAM_INCLUDE_IS_CLOSED = "isClosed";
     public static String PARAM_INCLUDE_IS_COMPLETED = "isCompleted";
+    public static String PARAM_INCLUDE_TRANSFER_PDF_INDICATOR = "transferPDFIndicator";
+    public static String PARAM_INCLUDE_TRANSFER_LOCATION = "transferLocation";
+    public static String PARAM_INCLUDE_TRANSFER_ACCESSION_INDICATOR = "transferAccessionIndicator";
 
     /**
      * Gets or creates the relative path starting from the provided parent folder.
-     * The method decides the type of the created elements considering the 
+     * The method decides the type of the created elements considering the
      * parent container's type and the type of the node to be created.
      * @param parentFolderNodeId the parent folder to start from
      * @param relativePath the relative path
@@ -58,4 +65,46 @@ public interface RMNodes extends Nodes
      * @return reference to the last element of the created path
      */
     public NodeRef getOrCreatePath(String parentFolderNodeId, String relativePath, QName nodeTypeQName);
+
+    /**
+     * Validates if the component with provided id and provided parameters belongs to the current endpoint.
+     *
+     * @param filePlanComponentId - file plan component id to check
+     * @param parameters - provided parameters
+     * @param requestedTypeQName - the requested type that identifies the endpoint we are in
+     */
+    public void validateNodeType(String filePlanComponentId, Parameters parameters, QName requestedTypeQName);
+
+    /**
+     * Validates if the component with provided id and provided parameters from formData belongs to the current endpoint.
+     *
+     * @param filePlanComponentId - file plan component id to check
+     * @param formData - provided formData
+     * @param requestedTypeQName - the requested type that identifies the endpoint we are in
+     */
+    public void validateNodeType(String filePlanComponentId, FormData formData, QName requestedTypeQName);
+
+    /**
+     * Validates if the component with provided id, relativePath from new created filePlanComponent and provided parameters belongs to the current endpoint.
+     *
+     * @param filePlanComponentId - file plan component id to check
+     * @param filePlanComponentInfo - new created file plan component info
+     * @param parameters - provided parameters
+     * @param requestedTypeQName - the requested type that identifies the endpoint we are in
+     */
+    public void validateNodeType(String filePlanComponentId, Node filePlanComponentInfo, Parameters parameters, QName requestedTypeQName);
+
+    /**
+     * Validates if the filePlanComponent with provided id is a record.
+     *
+     * @param filePlanComponentId
+     */
+    public void validateRecord(String filePlanComponentId);
+
+    /**
+     * Helper method to obtain file plan type or null if the rm site does not exist.
+     *
+     * @return file plan type or null
+     */
+    public QName getFilePlanType();
 }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/RMSites.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/RMSites.java
index 68776bd228..81e21760de 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/RMSites.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/RMSites.java
@@ -28,10 +28,9 @@
 package org.alfresco.rm.rest.api;
 
 import org.alfresco.rest.api.Sites;
-import org.alfresco.rest.api.model.Site;
+import org.alfresco.rest.api.model.SiteUpdate;
 import org.alfresco.rest.framework.resource.parameters.Parameters;
 import org.alfresco.rm.rest.api.model.RMSite;
-import org.alfresco.rm.rest.api.model.SiteUpdate;
 
 /**
  * RM Sites API
@@ -73,14 +72,4 @@ public interface RMSites extends Sites
      * @param parameters
      */
     void deleteRMSite(String siteId, Parameters parameters);
-
-    /**
-     * TODO Copied from Sites interface because was not available in 5.2.a-EA. To be removed after upgrading.
-     *
-     * @param siteId
-     * @param site
-     * @param parameters
-     * @return
-     */
-    Site updateSite(String siteId, SiteUpdate site, Parameters parameters);
 }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/Records.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/Records.java
deleted file mode 100644
index c8b2333c80..0000000000
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/Records.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api;
-
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.framework.resource.parameters.Parameters;
-import org.alfresco.rm.rest.api.model.TargetContainer;
-
-/**
- * Records API
- * 
- * @author Ana Bozianu
- * @since 2.6
- */
-public interface Records
-{
-    public static final String PARAM_HIDE_RECORD = "hideRecord";
-
-    /**
-     * Creates a record from a file
-     * 
-     * @param fileId the id of a non record file
-     * @param parameters  the {@link Parameters} object to get the parameters passed into the request
-     * @return information about the created record
-     */
-    public Node declareFileAsRecord(String fileId, Parameters parameters);
-
-    /**
-     * Files a record into th fileplan.
-     * If the record is already filed it links the record to the target folder
-     * 
-     * @param recordId the id of the record do file/link
-     * @param target the target parent folder
-     * @param parameters the {@link Parameters} object to get the parameters passed into the request
-     * @return information about the new state of the record
-     */
-    public Node fileOrLinkRecord(String recordId, TargetContainer target, Parameters parameters);
-}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentChildrenRelation.java
deleted file mode 100644
index d5e6e98502..0000000000
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentChildrenRelation.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api.fileplancomponents;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.framework.WebApiDescription;
-import org.alfresco.rest.framework.WebApiParam;
-import org.alfresco.rest.framework.core.exceptions.ApiException;
-import org.alfresco.rest.framework.resource.RelationshipResource;
-import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction;
-import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
-import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
-import org.alfresco.rest.framework.resource.parameters.Parameters;
-import org.alfresco.rest.framework.webscripts.WithResponse;
-import org.alfresco.rm.rest.api.RMNodes;
-import org.springframework.extensions.webscripts.servlet.FormData;
-
-/**
- * An implementation of an Entity Resource for a fileplan component
- *
- * @author Ana Bozianu
- * @since 2.6
- */
-@RelationshipResource(name="children", entityResource = FileplanComponentsEntityResource.class, title = "Children of fileplan component")
-public class FileplanComponentChildrenRelation implements RelationshipResourceAction.Read,
-                                                 RelationshipResourceAction.Create,
-                                                 MultiPartRelationshipResourceAction.Create
-{
-    private RMNodes nodes;
-
-    public void setNodes(RMNodes nodes)
-    {
-        this.nodes = nodes;
-    }
-
-    @Override
-    @WebApiDescription(title = "Return a paged list of fileplan components for the container identified by parentFolderNodeId")
-    public CollectionWithPagingInfo readAll(String parentFolderNodeId, Parameters parameters)
-    {
-        return nodes.listChildren(parentFolderNodeId, parameters);
-    }
-
-    @Override
-    @WebApiDescription(title="Create one (or more) nodes as children of container identified by parentFolderNodeId")
-    public List create(String parentFolderNodeId, List nodeInfos, Parameters parameters)
-    {
-        List result = new ArrayList<>(nodeInfos.size());
-
-        for (Node nodeInfo : nodeInfos)
-        {
-            result.add(nodes.createNode(parentFolderNodeId, nodeInfo, parameters));
-        }
-
-        return result;
-    }
-
-    @Override
-    @WebApiDescription(title = "Upload file content and meta-data into the repository.")
-    @WebApiParam(name = "formData", title = "A single form data", description = "A single form data which holds FormFields.")
-    public Node create(String parentFolderNodeId, FormData formData, Parameters parameters, WithResponse withResponse)
-    {
-        try
-        {
-            return nodes.upload(parentFolderNodeId, formData, parameters);
-        }
-        catch (ApiException apiException)
-        {
-            /*
-             * The upload method encapsulates most exceptions that can occur on node creation in an ApiException. 
-             * To allow the API framework to correctly map the exception to the API error code we throw the original exception.
-             */
-            Throwable originalException = apiException.getCause();
-            if (originalException != null && originalException instanceof RuntimeException)
-            {
-                throw (RuntimeException) originalException;
-            }
-            else
-            {
-                throw apiException;
-            }
-        }
-    }
-}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentsEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentsEntityResource.java
deleted file mode 100644
index 0859280670..0000000000
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentsEntityResource.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api.fileplancomponents;
-
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.framework.WebApiDescription;
-import org.alfresco.rest.framework.WebApiParam;
-import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
-import org.alfresco.rest.framework.resource.EntityResource;
-import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
-import org.alfresco.rest.framework.resource.parameters.Parameters;
-import org.alfresco.rm.rest.api.RMNodes;
-import org.alfresco.util.ParameterCheck;
-import org.springframework.beans.factory.InitializingBean;
-
-/**
- * Fileplan component children
- *
- * @author Ana Bozianu
- * @since 2.6
- */
-@EntityResource(name="fileplan-components", title = "Fileplan Components")
-public class FileplanComponentsEntityResource implements
-        EntityResourceAction.ReadById,
-        EntityResourceAction.Delete,
-        EntityResourceAction.Update,
-        InitializingBean
-{
-    private RMNodes nodes;
-    private String PARAM_PERMANENT = "permanent";
-
-    public void setNodes(RMNodes nodes)
-    {
-        this.nodes = nodes;
-    }
-
-    @WebApiDescription(title = "Get Node Information", description = "Get information for the fileplan component with id 'nodeId'")
-    @WebApiParam(name = "nodeId", title = "The node id")
-    public Node readById(String nodeId, Parameters parameters)
-    {
-        return nodes.getFolderOrDocument(nodeId, parameters);
-    }
-
-    @Override
-    @WebApiDescription(title="Updates a node (file or folder) with id 'nodeId'")
-    public Node update(String nodeId, Node nodeInfo, Parameters parameters)
-    {
-        return nodes.updateNode(nodeId, nodeInfo, parameters);
-    }
-
-    @Override
-    @WebApiDescription(title = "Delete Node", description="Delete the file or folder with id 'nodeId'. Folder will cascade delete")
-    public void delete(String nodeId, Parameters parameters)
-    {
-        String permanentParameter = parameters.getParameter(PARAM_PERMANENT);
-        if(permanentParameter != null)
-        {
-            throw new InvalidArgumentException("DELETE does not support parameter: permanent");
-        }
-        nodes.deleteNode(nodeId, parameters);
-    }
-
-    @Override
-    public void afterPropertiesSet() throws Exception
-    {
-        ParameterCheck.mandatory("nodes", this.nodes);
-    }
-}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanChildrenRelation.java
new file mode 100644
index 0000000000..2ed5d321ac
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanChildrenRelation.java
@@ -0,0 +1,193 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.fileplans;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.query.PagingResults;
+import org.alfresco.repo.node.getchildren.FilterProp;
+import org.alfresco.rest.api.impl.Util;
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
+import org.alfresco.rest.framework.resource.RelationshipResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
+import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.impl.SearchTypesFactory;
+import org.alfresco.rm.rest.api.model.FilePlan;
+import org.alfresco.rm.rest.api.model.RecordCategory;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.ParameterCheck;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * File plan children relation
+ *
+ * @author Ramona Popa
+ * @since 2.6
+ */
+@RelationshipResource(name="categories", entityResource = FilePlanEntityResource.class, title = "Category children of file plan")
+public class FilePlanChildrenRelation implements RelationshipResourceAction.Read,
+                                                 RelationshipResourceAction.Create, InitializingBean
+{
+    /** Record category type */
+    public static final String RECORD_CATEGORY_TYPE = "rma:recordCategory";
+
+    private FilePlanComponentsApiUtils apiUtils;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+    private SearchTypesFactory searchTypesFactory;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    public void setSearchTypesFactory(SearchTypesFactory searchTypesFactory)
+    {
+        this.searchTypesFactory = searchTypesFactory;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception
+    {
+        ParameterCheck.mandatory("apiUtils", this.apiUtils);
+        ParameterCheck.mandatory("fileFolderService", this.fileFolderService);
+        ParameterCheck.mandatory("nodesModelFactory", this.nodesModelFactory);
+        ParameterCheck.mandatory("searchTypesFactory", this.searchTypesFactory);
+    }
+
+    @Override
+    @WebApiDescription(title = "Return a paged list of file plan children (record categories) for the container identified by 'filePlanId'")
+    public CollectionWithPagingInfo readAll(String filePlanId, Parameters parameters)
+    {
+        // validate parameters
+        checkNotBlank("filePlanId", filePlanId);
+        mandatory("parameters", parameters);
+
+        QName filePlanType = apiUtils.getFilePlanType();
+        if(filePlanType == null)// rm site not created
+        {
+            throw new EntityNotFoundException(filePlanId);
+        }
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(filePlanId, filePlanType);
+
+        // list record categories
+        Set searchTypeQNames = searchTypesFactory.buildSearchTypesForFilePlanEndpoint();
+
+        //FIXME this param null
+        List filterProps = apiUtils.getListChildrenFilterProps(parameters, null);
+
+        final PagingResults pagingResults = fileFolderService.list(parentNodeRef,
+                null,
+                searchTypeQNames,
+                null,
+                apiUtils.getSortProperties(parameters),
+                filterProps,
+                Util.getPagingRequest(parameters.getPaging()));
+
+        final List page = pagingResults.getPage();
+        Map mapUserInfo = new HashMap<>();
+        List nodes = new AbstractList()
+        {
+            @Override
+            public RecordCategory get(int index)
+            {
+                FileInfo info = page.get(index);
+                return nodesModelFactory.createRecordCategory(info, parameters, mapUserInfo, true);
+            }
+
+            @Override
+            public int size()
+            {
+                return page.size();
+            }
+        };
+
+        FilePlan sourceEntity = null;
+        if (parameters.includeSource())
+        {
+            FileInfo info = fileFolderService.getFileInfo(parentNodeRef);
+            sourceEntity = nodesModelFactory.createFilePlan(info, parameters, mapUserInfo, true);
+        }
+
+        return CollectionWithPagingInfo.asPaged(parameters.getPaging(), nodes, pagingResults.hasMoreItems(),
+                pagingResults.getTotalResultCount().getFirst(), sourceEntity);
+    }
+
+    @Override
+    @WebApiDescription(title="Create one (or more) record categories as children of container identified by 'filePlanId'")
+    public List create(String filePlanId, List nodeInfos, Parameters parameters)
+    {
+        checkNotBlank("filePlanId", filePlanId);
+        mandatory("nodeInfos", nodeInfos);
+        mandatory("parameters", parameters);
+
+        QName filePlanType = apiUtils.getFilePlanType();
+        if(filePlanType == null)// rm site not created
+        {
+            throw new EntityNotFoundException(filePlanId);
+        }
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(filePlanId, filePlanType);
+
+        List result = new ArrayList<>(nodeInfos.size());
+        Map mapUserInfo = new HashMap<>();
+        for (RecordCategory nodeInfo : nodeInfos)
+        {
+            // Create the node
+            NodeRef newNode = apiUtils.createRMNode(parentNodeRef, nodeInfo.getName(), RECORD_CATEGORY_TYPE, nodeInfo.getProperties(), nodeInfo.getAspectNames());
+            FileInfo info = fileFolderService.getFileInfo(newNode);
+            result.add(nodesModelFactory.createRecordCategory(info, parameters, mapUserInfo, false));
+        }
+        return result;
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanEntityResource.java
new file mode 100644
index 0000000000..2e8a3097b5
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanEntityResource.java
@@ -0,0 +1,125 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.fileplans;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
+import org.alfresco.rest.framework.resource.EntityResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.model.FilePlan;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.ParameterCheck;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * File plan entity resource
+ *
+ * @author Ramona Popa
+ * @since 2.6
+ */
+@EntityResource(name = "file-plans", title = "File plans")
+public class FilePlanEntityResource
+        implements EntityResourceAction.ReadById, EntityResourceAction.Update, InitializingBean
+{
+
+    private FilePlanComponentsApiUtils apiUtils;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception
+    {
+        ParameterCheck.mandatory("apiUtils", this.apiUtils);
+        ParameterCheck.mandatory("fileFolderService", this.fileFolderService);
+        ParameterCheck.mandatory("nodesModelFactory", this.nodesModelFactory);
+    }
+
+    @WebApiDescription(title = "Get file plan information", description = "Get information for a file plan with id 'filePlanId'")
+    @WebApiParam(name = "filePlanId", title = "The file plan id")
+    public FilePlan readById(String filePlanId, Parameters parameters)
+    {
+        checkNotBlank("filePlanId", filePlanId);
+        mandatory("parameters", parameters);
+
+        QName filePlanType = apiUtils.getFilePlanType();
+        if(filePlanType == null)// rm site not created
+        {
+            throw new EntityNotFoundException(filePlanId);
+        }
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(filePlanId, filePlanType);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+
+        return nodesModelFactory.createFilePlan(info, parameters, null, false);
+    }
+
+    @Override
+    @WebApiDescription(title = "Update file plan", description = "Updates a filePlan with id 'filePlanId'")
+    public FilePlan update(String filePlanId, FilePlan filePlanInfo, Parameters parameters)
+    {
+        checkNotBlank("filePlanId", filePlanId);
+        mandatory("filePlanInfo", filePlanInfo);
+        mandatory("parameters", parameters);
+
+        QName filePlanType = apiUtils.getFilePlanType();
+        if(filePlanType == null)// rm site not created
+        {
+            throw new EntityNotFoundException(filePlanId);
+        }
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(filePlanId, filePlanType);
+        apiUtils.updateNode(nodeRef, filePlanInfo, parameters);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+        return nodesModelFactory.createFilePlan(info, parameters, null, false);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/package-info.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/package-info.java
new file mode 100644
index 0000000000..782f3226a5
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/package-info.java
@@ -0,0 +1,37 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+/**
+ * Package info that defines the Information Governance File Plans REST API
+ *
+ * @author Ramona Popa
+ * @since 2.6
+ */
+@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
+package org.alfresco.rm.rest.api.fileplans;
+import org.alfresco.rest.framework.Api;
+import org.alfresco.rest.framework.WebApi;
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/files/FilesEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/files/FilesEntityResource.java
index a6b8995687..05981ff7d9 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/files/FilesEntityResource.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/files/FilesEntityResource.java
@@ -27,14 +27,21 @@
 
 package org.alfresco.rm.rest.api.files;
 
-import org.alfresco.rest.api.model.Node;
+import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
+import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
+import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
 import org.alfresco.rest.framework.Operation;
 import org.alfresco.rest.framework.WebApiDescription;
 import org.alfresco.rest.framework.resource.EntityResource;
 import org.alfresco.rest.framework.resource.parameters.Parameters;
 import org.alfresco.rest.framework.webscripts.WithResponse;
-import org.alfresco.rm.rest.api.Records;
-import org.alfresco.rm.rest.api.model.TargetContainer;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.model.Record;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.StoreRef;
 import org.alfresco.util.ParameterCheck;
 import org.springframework.beans.factory.InitializingBean;
 
@@ -47,23 +54,70 @@ import org.springframework.beans.factory.InitializingBean;
 @EntityResource(name="files", title = "Files")
 public class FilesEntityResource implements InitializingBean
 {
-    private Records records;
+    private ApiNodesModelFactory nodesModelFactory;
+    private AuthenticationUtil authenticationUtil;
+    private FilePlanService filePlanService;
+    private FileFolderService fileFolderService;
+    private RecordService recordService;
 
-    public void setRecords(Records records)
+    public void setAuthenticationUtil(AuthenticationUtil authenticationUtil)
     {
-        this.records = records;
+        this.authenticationUtil = authenticationUtil;
+    }
+
+    public void setFilePlanService(FilePlanService filePlanService)
+    {
+        this.filePlanService = filePlanService;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setRecordService(RecordService recordService)
+    {
+        this.recordService = recordService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
     }
 
     @Operation("declare")
     @WebApiDescription(title = "Declare as record", description="Declare a file as record.")
-    public Node declareAsRecord(String fileId, Void body, Parameters parameters, WithResponse withResponse)
+    public Record declareAsRecord(String fileId, Void body, Parameters parameters, WithResponse withResponse)
     {
-        return records.declareFileAsRecord(fileId, parameters);
+        // Get fileplan
+        NodeRef filePlan = authenticationUtil.runAsSystem(new RunAsWork()
+        {
+            @Override
+            public NodeRef doWork()
+            {
+                return filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
+            }
+        });
+
+        // default false (if not provided)
+        boolean hideRecord = Boolean.valueOf(parameters.getParameter(Record.PARAM_HIDE_RECORD));
+
+        // Create the record
+        NodeRef file = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, fileId);
+        recordService.createRecord(filePlan, file, !hideRecord);
+
+        // Return record state
+        FileInfo info = fileFolderService.getFileInfo(file);
+        return nodesModelFactory.createRecord(info, parameters, null, false);
     }
 
     @Override
     public void afterPropertiesSet() throws Exception
     {
-        ParameterCheck.mandatory("records", this.records);
+        ParameterCheck.mandatory("nodesModelFactory", nodesModelFactory);
+        ParameterCheck.mandatory("authenticationUtil", authenticationUtil);
+        ParameterCheck.mandatory("filePlanService", filePlanService);
+        ParameterCheck.mandatory("fileFolderService", fileFolderService);
+        ParameterCheck.mandatory("recordService", recordService);
     }
 }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/files/package-info.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/files/package-info.java
index 8ba4b1f926..8f4528e6d4 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/files/package-info.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/files/package-info.java
@@ -27,11 +27,11 @@
 
 /**
  * Package info that defines the Information Governance Files REST API
- * 
+ *
  * @author Ana Bozianu
  * @since 2.6
  */
-@WebApi(name="ig", scope=Api.SCOPE.PUBLIC, version=1)
+@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
 package org.alfresco.rm.rest.api.files;
 import org.alfresco.rest.framework.Api;
 import org.alfresco.rest.framework.WebApi;
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/ApiNodesModelFactory.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/ApiNodesModelFactory.java
new file mode 100644
index 0000000000..9605a79f32
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/ApiNodesModelFactory.java
@@ -0,0 +1,839 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.impl;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
+import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.rest.api.Nodes;
+import org.alfresco.rest.api.model.ContentInfo;
+import org.alfresco.rest.api.model.Node;
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.model.FilePlan;
+import org.alfresco.rm.rest.api.model.RMNode;
+import org.alfresco.rm.rest.api.model.Record;
+import org.alfresco.rm.rest.api.model.RecordCategory;
+import org.alfresco.rm.rest.api.model.RecordCategoryChild;
+import org.alfresco.rm.rest.api.model.RecordFolder;
+import org.alfresco.rm.rest.api.model.Transfer;
+import org.alfresco.rm.rest.api.model.TransferChild;
+import org.alfresco.rm.rest.api.model.TransferContainer;
+import org.alfresco.rm.rest.api.model.UnfiledChild;
+import org.alfresco.rm.rest.api.model.UnfiledContainer;
+import org.alfresco.rm.rest.api.model.UnfiledContainerChild;
+import org.alfresco.rm.rest.api.model.UnfiledRecordFolder;
+import org.alfresco.rm.rest.api.model.UnfiledRecordFolderChild;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.ContentData;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.security.PersonService;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * Utility class containing Alfresco and RM java services required by the API
+ * endpoints
+ *
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+public class ApiNodesModelFactory
+{
+
+    // excluded namespaces (aspects, properties, assoc types)
+    public static final List EXCLUDED_NS = Arrays.asList(NamespaceService.SYSTEM_MODEL_1_0_URI);
+
+    // excluded aspects
+    public static final List EXCLUDED_ASPECTS = Arrays.asList();
+
+    // excluded properties
+    public static final List EXCLUDED_PROPS = Arrays.asList(
+            // top-level minimal info
+            ContentModel.PROP_NAME, ContentModel.PROP_MODIFIER, ContentModel.PROP_MODIFIED, ContentModel.PROP_CREATOR,
+            ContentModel.PROP_CREATED, ContentModel.PROP_CONTENT,
+            // other - TBC
+            ContentModel.PROP_INITIAL_VERSION, ContentModel.PROP_AUTO_VERSION_PROPS, ContentModel.PROP_AUTO_VERSION);
+
+    private NodeService nodeService;
+    private NamespaceService namespaceService;
+    private Nodes nodes;
+    private FilePlanComponentsApiUtils apiUtils;
+    private PersonService personService;
+    private DispositionService dispositionService;
+    private ServiceRegistry serviceRegistry;
+
+    public NodeService getNodeService()
+    {
+        return nodeService;
+    }
+
+    public void setNodeService(NodeService nodeService)
+    {
+        this.nodeService = nodeService;
+    }
+
+    public NamespaceService getNamespaceService()
+    {
+        return namespaceService;
+    }
+
+    public void setNamespaceService(NamespaceService namespaceService)
+    {
+        this.namespaceService = namespaceService;
+    }
+
+    public void setNodes(Nodes nodes)
+    {
+        this.nodes = nodes;
+    }
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setPersonService(PersonService personService)
+    {
+        this.personService = personService;
+    }
+
+    public DispositionService getDispositionService()
+    {
+        return dispositionService;
+    }
+
+    public void setDispositionService(DispositionService dispositionService)
+    {
+        this.dispositionService = dispositionService;
+    }
+
+    public void setServiceRegistry(ServiceRegistry serviceRegistry)
+    {
+        this.serviceRegistry = serviceRegistry;
+    }
+
+    /**
+     * Helper method that sets the basic information for most of the node types.
+     *
+     * @param rmNode
+     * @param info
+     * @param propertyFilter
+     * @param isMinimalInfo
+     */
+    private void mapBasicInfo(RMNode rmNode, FileInfo info, BeanPropertiesFilter propertyFilter, Map mapUserInfo,
+            boolean isMinimalInfo)
+    {
+        if (propertyFilter.isAllowed(RMNode.PARAM_ID))
+        {
+            rmNode.setNodeRef(info.getNodeRef());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_PARENT_ID))
+        {
+            rmNode.setParentId(nodeService.getPrimaryParent(info.getNodeRef()).getParentRef());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_NAME))
+        {
+            rmNode.setName(info.getName());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_NODE_TYPE))
+        {
+            rmNode.setNodeType(info.getType().toPrefixString(namespaceService));
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_MODIFIED_AT))
+        {
+            rmNode.setModifiedAt(info.getModifiedDate());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_MODIFIED_BY_USER))
+        {
+            if (mapUserInfo == null)
+            {
+                mapUserInfo = new HashMap<>(2);
+            }
+            UserInfo modifer = Node.lookupUserInfo((String) info.getProperties().get(ContentModel.PROP_MODIFIER), mapUserInfo,
+                    personService);
+            rmNode.setModifiedByUser(modifer);
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_CREATED_AT))
+        {
+            rmNode.setCreatedAt(info.getCreatedDate());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_CREATED_BY_USER))
+        {
+            if (mapUserInfo == null)
+            {
+                mapUserInfo = new HashMap<>(2);
+            }
+            UserInfo creator = Node.lookupUserInfo((String) info.getProperties().get(ContentModel.PROP_CREATOR), mapUserInfo,
+                    personService);
+            rmNode.setCreatedByUser(creator);
+        }
+        if (!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_ASPECT_NAMES))
+        {
+            rmNode.setAspectNames(mapFromNodeAspects(nodeService.getAspects(info.getNodeRef())));
+        }
+        if (!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_PROPERTIES))
+        {
+            rmNode.setProperties(mapFromNodeProperties(info.getProperties()));
+        }
+    }
+
+    /**
+     * Helper method that sets the optional information for most of the node types.
+     *
+     * @param rmNode
+     * @param info
+     * @param includeParam
+     * @param isMinimalInfo
+     */
+    private void mapOptionalInfo(RMNode rmNode, FileInfo info, List includeParam, boolean isMinimalInfo)
+    {
+        if (includeParam == null || includeParam.isEmpty())
+        {
+            return;
+        }
+        if (includeParam.contains(RMNode.PARAM_ALLOWABLE_OPERATIONS))
+        {
+            rmNode.setAllowableOperations(apiUtils.getAllowableOperations(info.getNodeRef(), info.getType()));
+        }
+        if (includeParam.contains(RMNode.PARAM_PATH))
+        {
+            rmNode.setPath(apiUtils.lookupPathInfo(info.getNodeRef()));
+        }
+        if (isMinimalInfo && includeParam.contains(RMNode.PARAM_ASPECT_NAMES))
+        {
+            rmNode.setAspectNames(mapFromNodeAspects(nodeService.getAspects(info.getNodeRef())));
+        }
+        if (isMinimalInfo && includeParam.contains(RMNode.PARAM_PROPERTIES))
+        {
+            rmNode.setProperties(mapFromNodeProperties(info.getProperties()));
+        }
+    }
+
+    /**
+     * Helper method that sets the information for unfiled child type.
+     *
+     * @param unfiledChild
+     * @param info
+     * @param propertyFilter
+     */
+    private void mapUnfiledChildInfo(UnfiledChild unfiledChild, FileInfo info, BeanPropertiesFilter propertyFilter)
+    {
+        if (RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER.equals(info.getType()))
+        {
+            if (propertyFilter.isAllowed(UnfiledChild.PARAM_IS_UNFILED_RECORD_FOLDER))
+            {
+                unfiledChild.setIsUnfiledRecordFolder(true);
+            }
+            if (propertyFilter.isAllowed(UnfiledChild.PARAM_IS_RECORD))
+            {
+                unfiledChild.setIsRecord(false);
+            }
+        }
+        else
+        {
+            if (propertyFilter.isAllowed(UnfiledChild.PARAM_IS_UNFILED_RECORD_FOLDER))
+            {
+                unfiledChild.setIsUnfiledRecordFolder(false);
+            }
+            if (propertyFilter.isAllowed(UnfiledChild.PARAM_IS_RECORD))
+            {
+                unfiledChild.setIsRecord(true);
+            }
+        }
+    }
+
+    /**
+     * Helper method that sets the information for transfer container type.
+     *
+     * @param transferContainer
+     * @param info
+     * @param propertyFilter
+     */
+    private void mapTransferContainerInfo(TransferContainer transferContainer, FileInfo info, Map mapUserInfo, BeanPropertiesFilter propertyFilter, List includeParam, boolean isMinimalInfo)
+    {
+        if (propertyFilter.isAllowed(RMNode.PARAM_ID))
+        {
+            transferContainer.setNodeRef(info.getNodeRef());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_PARENT_ID))
+        {
+            transferContainer.setParentId(nodeService.getPrimaryParent(info.getNodeRef()).getParentRef());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_NAME))
+        {
+            transferContainer.setName(info.getName());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_NODE_TYPE))
+        {
+            transferContainer.setNodeType(info.getType().toPrefixString(namespaceService));
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_MODIFIED_AT))
+        {
+            transferContainer.setModifiedAt(info.getModifiedDate());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_MODIFIED_BY_USER))
+        {
+            if (mapUserInfo == null)
+            {
+                mapUserInfo = new HashMap<>(2);
+            }
+            UserInfo modifer = Node.lookupUserInfo((String) info.getProperties().get(ContentModel.PROP_MODIFIER), mapUserInfo,
+                    personService);
+            transferContainer.setModifiedByUser(modifer);
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_CREATED_AT))
+        {
+            transferContainer.setCreatedAt(info.getCreatedDate());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_CREATED_BY_USER))
+        {
+            if (mapUserInfo == null)
+            {
+                mapUserInfo = new HashMap<>(2);
+            }
+            UserInfo creator = Node.lookupUserInfo((String) info.getProperties().get(ContentModel.PROP_CREATOR), mapUserInfo,
+                    personService);
+            transferContainer.setCreatedByUser(creator);
+        }
+        if (!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_ASPECT_NAMES))
+        {
+            transferContainer.setAspectNames(mapFromNodeAspects(nodeService.getAspects(info.getNodeRef())));
+        }
+        if (!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_PROPERTIES))
+        {
+            transferContainer.setProperties(mapFromNodeProperties(info.getProperties()));
+        }
+
+        //optional parameters
+        if (includeParam == null || includeParam.isEmpty())
+        {
+            return;
+        }
+        if (includeParam.contains(RMNode.PARAM_ALLOWABLE_OPERATIONS))
+        {
+            transferContainer.setAllowableOperations(apiUtils.getAllowableOperations(info.getNodeRef(), info.getType()));
+        }
+        if (isMinimalInfo && includeParam.contains(RMNode.PARAM_ASPECT_NAMES))
+        {
+            transferContainer.setAspectNames(mapFromNodeAspects(nodeService.getAspects(info.getNodeRef())));
+        }
+        if (isMinimalInfo && includeParam.contains(RMNode.PARAM_PROPERTIES))
+        {
+            transferContainer.setProperties(mapFromNodeProperties(info.getProperties()));
+        }
+    }
+
+    /**
+     * Helper method that sets the information for transfer type.
+     *
+     * @param transfer
+     * @param info
+     * @param propertyFilter
+     */
+    private void mapTransferInfo(Transfer transfer, FileInfo info, Map mapUserInfo, BeanPropertiesFilter propertyFilter, List includeParam, boolean isMinimalInfo)
+    {
+        if (propertyFilter.isAllowed(RMNode.PARAM_ID))
+        {
+            transfer.setNodeRef(info.getNodeRef());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_PARENT_ID))
+        {
+            transfer.setParentId(nodeService.getPrimaryParent(info.getNodeRef()).getParentRef());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_NAME))
+        {
+            transfer.setName(info.getName());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_NODE_TYPE))
+        {
+            transfer.setNodeType(info.getType().toPrefixString(namespaceService));
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_CREATED_AT))
+        {
+            transfer.setCreatedAt(info.getCreatedDate());
+        }
+        if (propertyFilter.isAllowed(RMNode.PARAM_CREATED_BY_USER))
+        {
+            if (mapUserInfo == null)
+            {
+                mapUserInfo = new HashMap<>(2);
+            }
+            UserInfo creator = Node.lookupUserInfo((String) info.getProperties().get(ContentModel.PROP_CREATOR), mapUserInfo,
+                        personService);
+            transfer.setCreatedByUser(creator);
+        }
+        if (!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_ASPECT_NAMES))
+        {
+            transfer.setAspectNames(mapFromNodeAspects(nodeService.getAspects(info.getNodeRef())));
+        }
+        if (!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_PROPERTIES))
+        {
+            transfer.setProperties(mapFromNodeProperties(info.getProperties()));
+        }
+
+        //optional parameters
+        if (isMinimalInfo && includeParam == null || includeParam.isEmpty())
+        {
+            return;
+        }
+        if (includeParam.contains(RMNode.PARAM_ALLOWABLE_OPERATIONS))
+        {
+            transfer.setAllowableOperations(apiUtils.getAllowableOperations(info.getNodeRef(), info.getType()));
+        }
+        if (isMinimalInfo && includeParam.contains(RMNode.PARAM_ASPECT_NAMES))
+        {
+            transfer.setAspectNames(mapFromNodeAspects(nodeService.getAspects(info.getNodeRef())));
+        }
+        if (isMinimalInfo && includeParam.contains(RMNode.PARAM_PROPERTIES))
+        {
+            transfer.setProperties(mapFromNodeProperties(info.getProperties()));
+        }
+        if ((!isMinimalInfo && propertyFilter.isAllowed(Transfer.PARAM_TRANSFER_ACCESSION_INDICATOR)) || (isMinimalInfo && includeParam.contains(Transfer.PARAM_TRANSFER_ACCESSION_INDICATOR)))
+        {
+            transfer.setTransferAccessionIndicator((Boolean) nodeService.getProperty(info.getNodeRef(), RecordsManagementModel.PROP_TRANSFER_ACCESSION_INDICATOR));
+        }
+        if ((!isMinimalInfo && propertyFilter.isAllowed(Transfer.PARAM_TRANSFER_LOCATION)) || (isMinimalInfo && includeParam.contains(Transfer.PARAM_TRANSFER_LOCATION)))
+        {
+            transfer.setTransferLocation((String) nodeService.getProperty(info.getNodeRef(), RecordsManagementModel.PROP_TRANSFER_LOCATION));
+        }
+        if ((!isMinimalInfo && propertyFilter.isAllowed(Transfer.PARAM_TRANSFER_PDF_INDICATOR)) || (isMinimalInfo && includeParam.contains(Transfer.PARAM_TRANSFER_PDF_INDICATOR)))
+        {
+            transfer.setTransferPDFIndicator((Boolean) nodeService.getProperty(info.getNodeRef(), RecordsManagementModel.PROP_TRANSFER_PDF_INDICATOR));
+        }
+    }
+
+    /**
+     * Helper method that sets the information for transfer child type.
+     *
+     * @param transferChild
+     * @param info
+     * @param propertyFilter
+     */
+    private void mapTransferChildInfo(TransferChild transferChild, FileInfo info, List includeParam, boolean isMinimalInfo)
+    {
+        if (includeParam == null || includeParam.isEmpty())
+        {
+            return;
+        }
+        if (RecordsManagementModel.TYPE_RECORD_FOLDER.equals(info.getType()))
+        {
+            if (isMinimalInfo && includeParam.contains(TransferChild.PARAM_IS_RECORD_FOLDER))
+            {
+                transferChild.setIsRecordFolder(true);
+            }
+            if (isMinimalInfo && includeParam.contains(TransferChild.PARAM_IS_RECORD))
+            {
+                transferChild.setIsRecord(false);
+            }
+            if(isMinimalInfo && includeParam.contains(RMNode.PARAM_IS_CLOSED))
+            {
+                transferChild.setIsClosed((Boolean) nodeService.getProperty(info.getNodeRef(), RecordsManagementModel.PROP_IS_CLOSED));
+            }
+            if(isMinimalInfo && includeParam.contains(TransferChild.PARAM_IS_COMPLETED))
+            {
+                transferChild.setIsCompleted(null);
+            }
+        }
+        else
+        {
+            if (isMinimalInfo && includeParam.contains(TransferChild.PARAM_IS_RECORD_FOLDER))
+            {
+                transferChild.setIsRecordFolder(false);
+            }
+            if (isMinimalInfo && includeParam.contains(TransferChild.PARAM_IS_RECORD))
+            {
+                transferChild.setIsRecord(true);
+            }
+            if(isMinimalInfo && includeParam.contains(RMNode.PARAM_IS_CLOSED))
+            {
+                transferChild.setIsClosed(null);
+            }
+            if(isMinimalInfo && includeParam.contains(TransferChild.PARAM_IS_COMPLETED))
+            {
+                transferChild.setIsCompleted(nodeService.hasAspect(info.getNodeRef(), RecordsManagementModel.ASPECT_DECLARED_RECORD));
+            }
+        }
+    }
+
+    /**
+     * Helper method that sets the information for record category child type.
+     *
+     * @param recordCategoryChild the record category child to set the fields to
+     * @param info info of the record category child
+     * @param includeParam the requested include parameters
+     * @param propertyFilter
+     */
+    private void mapRecordCategoryChildInfo(RecordCategoryChild recordCategoryChild, FileInfo info, List includeParam, BeanPropertiesFilter propertyFilter, boolean isMinimalInfo)
+    {
+        if (isMinimalInfo && (includeParam == null || includeParam.isEmpty()))
+        {
+            return;
+        }
+        if(RecordsManagementModel.TYPE_RECORD_FOLDER.equals(info.getType()))
+        {
+            if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)))
+            {
+                recordCategoryChild.setIsRecordFolder(true);
+            }
+            if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)))
+            {
+                recordCategoryChild.setIsRecordCategory(false);
+            }
+            if((!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_IS_CLOSED)) || (isMinimalInfo && includeParam.contains(RMNode.PARAM_IS_CLOSED)))
+            {
+                recordCategoryChild.setIsClosed((Boolean) nodeService.getProperty(info.getNodeRef(), RecordsManagementModel.PROP_IS_CLOSED));
+            }
+            if((!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_HAS_RETENTION_SCHEDULE)) || (isMinimalInfo && includeParam.contains(RMNode.PARAM_HAS_RETENTION_SCHEDULE)))
+            {
+                recordCategoryChild.setHasRetentionSchedule(null);
+            }
+        }
+        else
+        {
+            if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)))
+            {
+                recordCategoryChild.setIsRecordFolder(false);
+            }
+            if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)))
+            {
+                recordCategoryChild.setIsRecordCategory(true);
+            }
+            if((!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_HAS_RETENTION_SCHEDULE)) || (isMinimalInfo && includeParam.contains(RMNode.PARAM_HAS_RETENTION_SCHEDULE)))
+            {
+                DispositionSchedule ds = dispositionService.getDispositionSchedule(info.getNodeRef());
+                recordCategoryChild.setHasRetentionSchedule(ds !=null ? true : false);
+            }
+            if((!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_IS_CLOSED)) || (isMinimalInfo && includeParam.contains(RMNode.PARAM_IS_CLOSED)))
+            {
+                recordCategoryChild.setIsClosed(null);
+            }
+        }
+    }
+
+
+    /**
+     * Utility method that maps record specific fields
+     *
+     * @param record the record to set the fields to
+     * @param info info of the record
+     * @param includeParam the requested include parameters
+     */
+    private void mapRecordInfo(Record record, FileInfo info, List includeParam)
+    {
+        if (includeParam == null || includeParam.isEmpty())
+        {
+            return;
+        }
+        if (includeParam.contains(Record.PARAM_IS_COMPLETED))
+        {
+            record.setIsCompleted(nodeService.hasAspect(info.getNodeRef(), RecordsManagementModel.ASPECT_DECLARED_RECORD));
+        }
+        if(includeParam.contains(Record.PARAM_CONTENT))
+        {
+            Serializable val = info.getProperties().get(ContentModel.PROP_CONTENT);
+
+            if ((val != null) && (val instanceof ContentData)) {
+                ContentData cd = (ContentData)val;
+                String mimeType = cd.getMimetype();
+                String mimeTypeName = serviceRegistry.getMimetypeService().getDisplaysByMimetype().get(mimeType);
+                ContentInfo contentInfo = new ContentInfo(mimeType, mimeTypeName, cd.getSize(), cd.getEncoding());
+                record.setContent(contentInfo);
+            }
+        }
+    }
+
+    /**
+     * Helper method that converts a set of QName aspects into a list of String aspects
+     *
+     * @param properties
+     */
+    private List mapFromNodeAspects(Set nodeAspects)
+    {
+        return nodes.mapFromNodeAspects(nodeAspects, EXCLUDED_NS, EXCLUDED_ASPECTS);
+    }
+
+    /**
+     * Helper method that converts a map of QName properties into a map of String properties
+     *
+     * @param properties
+     * @return a map of String properties
+     */
+    private Map mapFromNodeProperties(Map properties)
+    {
+        return nodes.mapFromNodeProperties(properties, new ArrayList<>(), new HashMap<>(), EXCLUDED_NS, EXCLUDED_PROPS);
+    }
+
+    /**
+     * Creates an object of type FilePlan
+     *
+     * @param info info of the file plan
+     * @param propertyFilter
+     * @param includeParam
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return FilePlan object
+     */
+    public FilePlan createFilePlan(FileInfo info, Parameters parameters, Map mapUserInfo, boolean isMinimalInfo)
+    {
+        FilePlan filePlan = new FilePlan();
+        mapBasicInfo(filePlan, info, parameters.getFilter(), mapUserInfo, isMinimalInfo);
+        mapOptionalInfo(filePlan, info, parameters.getInclude(), isMinimalInfo);
+        return filePlan;
+    }
+
+    /**
+     * Creates an object of type RecordCategory
+     *
+     * @param info info of the record category
+     * @param propertyFilter
+     * @param includeParam
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return RecordCategory object
+     */
+    public RecordCategory createRecordCategory(FileInfo info, Parameters parameters, Map mapUserInfo,
+            boolean isMinimalInfo)
+    {
+        RecordCategory recordCategory = new RecordCategory();
+        mapBasicInfo(recordCategory, info, parameters.getFilter(), mapUserInfo, isMinimalInfo);
+        mapOptionalInfo(recordCategory, info, parameters.getInclude(), isMinimalInfo);
+
+        if (parameters.getInclude().contains(RMNode.PARAM_HAS_RETENTION_SCHEDULE))
+        {
+            DispositionSchedule ds = dispositionService.getDispositionSchedule(info.getNodeRef());
+            recordCategory.setHasRetentionSchedule(ds !=null ? true : false);
+        }
+
+        return recordCategory;
+    }
+
+    /**
+     * Creates an object of type RecordCategory
+     *
+     * @param info
+     * @param propertyFilter
+     * @param includeParam
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return RecordCategory object
+     */
+    public RecordFolder createRecordFolder(FileInfo info, Parameters parameters, Map mapUserInfo,
+            boolean isMinimalInfo)
+    {
+        RecordFolder recordFolder = new RecordFolder();
+        mapBasicInfo(recordFolder, info, parameters.getFilter(), mapUserInfo, isMinimalInfo);
+        mapOptionalInfo(recordFolder, info, parameters.getInclude(), isMinimalInfo);
+
+        if (parameters.getInclude().contains(RMNode.PARAM_IS_CLOSED))
+        {
+            recordFolder.setIsClosed((Boolean) nodeService.getProperty(info.getNodeRef(), RecordsManagementModel.PROP_IS_CLOSED));
+        }
+
+        return recordFolder;
+    }
+
+    /**
+     * Creates an object of type UnfiledContainer
+     *
+     * @param info
+     * @param propertyFilter
+     * @param includeParam
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return UnfiledContainer object
+     */
+    public UnfiledContainer createUnfiledContainer(FileInfo info, Parameters parameters, Map mapUserInfo,
+            boolean isMinimalInfo)
+    {
+        UnfiledContainer unfiledContainer = new UnfiledContainer();
+        mapBasicInfo(unfiledContainer, info, parameters.getFilter(), mapUserInfo, isMinimalInfo);
+        mapOptionalInfo(unfiledContainer, info, parameters.getInclude(), isMinimalInfo);
+        return unfiledContainer;
+    }
+
+    /**
+     * Creates an object of type TransferContainer
+     *
+     * @param info
+     * @param propertyFilter
+     * @param includeParam
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return UnfiledContainer object
+     */
+    public TransferContainer createTransferContainer(FileInfo info, Parameters parameters, Map mapUserInfo,
+            boolean isMinimalInfo)
+    {
+        TransferContainer transferContainer = new TransferContainer();
+        mapTransferContainerInfo(transferContainer, info, mapUserInfo, parameters.getFilter(), parameters.getInclude(), isMinimalInfo);
+        return transferContainer;
+    }
+
+    /**
+     * Creates an object of type Transfer
+     *
+     * @param info
+     * @param propertyFilter
+     * @param includeParam
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return UnfiledContainer object
+     */
+    public Transfer createTransfer(FileInfo info, Parameters parameters, Map mapUserInfo,
+                boolean isMinimalInfo)
+    {
+        Transfer transfer = new Transfer();
+        mapTransferInfo(transfer, info, mapUserInfo, parameters.getFilter(), parameters.getInclude(), isMinimalInfo);
+        return transfer;
+    }
+
+    /**
+     * Creates an object of type TransferChild
+     *
+     * @param info
+     * @param propertyFilter
+     * @param includeParam
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return UnfiledContainer object
+     */
+    public TransferChild createTransferChild(FileInfo info, Parameters parameters, Map mapUserInfo,
+                boolean isMinimalInfo)
+    {
+        TransferChild transferChild = new TransferChild();
+        mapBasicInfo(transferChild, info, parameters.getFilter(), mapUserInfo, isMinimalInfo);
+        mapOptionalInfo(transferChild, info, parameters.getInclude(), isMinimalInfo);
+        mapTransferChildInfo(transferChild, info, parameters.getInclude(), isMinimalInfo);
+        return transferChild;
+    }
+
+    /**
+     * Creates an object of type UnfiledContainerChild
+     *
+     * @param info
+     * @param parameters
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return UnfiledContainerChild object
+     */
+    public UnfiledContainerChild createUnfiledContainerChild(FileInfo info, Parameters parameters, Map mapUserInfo,
+            boolean isMinimalInfo)
+    {
+        UnfiledContainerChild unfiledContainerChild = new UnfiledContainerChild();
+        mapBasicInfo(unfiledContainerChild, info, parameters.getFilter(), mapUserInfo, isMinimalInfo);
+        mapOptionalInfo(unfiledContainerChild, info, parameters.getInclude(), isMinimalInfo);
+        mapUnfiledChildInfo(unfiledContainerChild, info, parameters.getFilter());
+        return unfiledContainerChild;
+    }
+
+    /**
+     * Creates an object of type UnfiledRecordFolder
+     *
+     * @param info
+     * @param parameters
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return UnfiledRecordFolder object
+     */
+    public UnfiledRecordFolder createUnfiledRecordFolder(FileInfo info, Parameters parameters, Map mapUserInfo,
+            boolean isMinimalInfo)
+    {
+        UnfiledRecordFolder unfiledChild = new UnfiledRecordFolder();
+        mapBasicInfo(unfiledChild, info, parameters.getFilter(), mapUserInfo, isMinimalInfo);
+        mapOptionalInfo(unfiledChild, info, parameters.getInclude(), isMinimalInfo);
+        return unfiledChild;
+    }
+
+    /**
+     * Creates an object of type UnfiledRecordFolderChild
+     *
+     * @param info
+     * @param parameters
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return UnfiledRecordFolderChild object
+     */
+    public UnfiledRecordFolderChild createUnfiledRecordFolderChild(FileInfo info, Parameters parameters, Map mapUserInfo,
+            boolean isMinimalInfo)
+    {
+        UnfiledRecordFolderChild unfiledRecordFolderChild = new UnfiledRecordFolderChild();
+        mapBasicInfo(unfiledRecordFolderChild, info, parameters.getFilter(), mapUserInfo, isMinimalInfo);
+        mapOptionalInfo(unfiledRecordFolderChild, info, parameters.getInclude(), isMinimalInfo);
+        mapUnfiledChildInfo(unfiledRecordFolderChild, info, parameters.getFilter());
+        return unfiledRecordFolderChild;
+    }
+
+    /**
+     * Creates an object of type RecordCategoryChild
+     *
+     * @param info
+     * @param parameters
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return
+     */
+    public RecordCategoryChild createRecordCategoryChild(FileInfo info, Parameters parameters,  Map mapUserInfo,
+                boolean isMinimalInfo)
+    {
+        RecordCategoryChild recordCategoryChild = new RecordCategoryChild();
+        mapBasicInfo(recordCategoryChild, info, parameters.getFilter(), mapUserInfo, isMinimalInfo);
+        mapOptionalInfo(recordCategoryChild, info, parameters.getInclude(), isMinimalInfo);
+        mapRecordCategoryChildInfo(recordCategoryChild, info, parameters.getInclude(), parameters.getFilter(), isMinimalInfo);
+        return recordCategoryChild;
+    }
+
+    /**
+     * Create an object of type Record
+     *
+     * @param info
+     * @param parameters
+     * @param mapUserInfo
+     * @param isMinimalInfo
+     * @return
+     */
+    public Record createRecord(FileInfo info, Parameters parameters, Map mapUserInfo, boolean isMinimalInfo)
+    {
+        Record record = new Record();
+        mapBasicInfo(record, info, parameters.getFilter(), mapUserInfo, isMinimalInfo);
+        mapOptionalInfo(record, info, parameters.getInclude(), isMinimalInfo);
+        mapRecordInfo(record, info, parameters.getInclude());
+        return record;
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/FilePlanComponentsApiUtils.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/FilePlanComponentsApiUtils.java
new file mode 100644
index 0000000000..7ba7add131
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/FilePlanComponentsApiUtils.java
@@ -0,0 +1,961 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.impl;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
+import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
+import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
+import org.alfresco.repo.content.ContentLimitViolationException;
+import org.alfresco.repo.content.MimetypeMap;
+import org.alfresco.repo.model.filefolder.FileFolderServiceImpl.InvalidTypeException;
+import org.alfresco.repo.node.getchildren.FilterProp;
+import org.alfresco.repo.node.getchildren.FilterPropBoolean;
+import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.repo.tenant.TenantUtil;
+import org.alfresco.rest.antlr.WhereClauseParser;
+import org.alfresco.rest.api.Activities;
+import org.alfresco.rest.api.Nodes;
+import org.alfresco.rest.api.model.PathInfo;
+import org.alfresco.rest.api.model.PathInfo.ElementInfo;
+import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
+import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
+import org.alfresco.rest.framework.core.exceptions.InsufficientStorageException;
+import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
+import org.alfresco.rest.framework.core.exceptions.NotFoundException;
+import org.alfresco.rest.framework.core.exceptions.RequestEntityTooLargeException;
+import org.alfresco.rest.framework.resource.content.BinaryResource;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rest.framework.resource.parameters.where.Query;
+import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
+import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
+import org.alfresco.rm.rest.api.RMSites;
+import org.alfresco.rm.rest.api.model.RMNode;
+import org.alfresco.rm.rest.api.model.RMSite;
+import org.alfresco.rm.rest.api.model.TransferContainer;
+import org.alfresco.service.cmr.activities.ActivityInfo;
+import org.alfresco.service.cmr.activities.ActivityPoster;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.lock.NodeLockedException;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.ContentIOException;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.ContentWriter;
+import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
+import org.alfresco.service.cmr.repository.MimetypeService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.Path;
+import org.alfresco.service.cmr.repository.Path.Element;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.security.AccessStatus;
+import org.alfresco.service.cmr.security.PermissionService;
+import org.alfresco.service.cmr.usage.ContentQuotaException;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.Pair;
+import org.alfresco.util.ParameterCheck;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.social.InternalServerErrorException;
+
+import net.sf.acegisecurity.vote.AccessDecisionVoter;
+
+/**
+ * Utility class that handles common api endpoint tasks
+ *
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+public class FilePlanComponentsApiUtils
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(SearchTypesFactory.class);
+
+    public static final String FILE_PLAN_ALIAS = "-filePlan-";
+    public static final String TRANSFERS_ALIAS = "-transfers-";
+    public static final String UNFILED_ALIAS = "-unfiled-";
+    public static final String HOLDS_ALIAS = "-holds-";
+    public static final String RM_SITE_ID = "rm";
+    //public static String PARAM_RELATIVE_PATH = "relativePath";
+
+    // excluded properties
+    public static final List TYPES_CAN_CREATE = Arrays.asList(
+            RecordsManagementModel.TYPE_FILE_PLAN,
+            RecordsManagementModel.TYPE_RECORD_CATEGORY,
+            RecordsManagementModel.TYPE_RECORD_FOLDER,
+            RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER,
+            RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER,
+            RecordsManagementModel.TYPE_HOLD_CONTAINER);
+
+    /** RM Nodes API */
+    private Nodes nodes;
+    private FileFolderService fileFolderService;
+    private FilePlanService filePlanService;
+    private NodeService nodeService;
+    private ContentService contentService;
+    private MimetypeService mimetypeService;
+    private DictionaryService dictionaryService;
+    private CapabilityService capabilityService;
+    private PermissionService permissionService;
+    private RecordService recordService;
+    private AuthenticationUtil authenticationUtil;
+    private ActivityPoster activityPoster;
+    private RMSites sites;
+
+    public void setNodes(Nodes nodes)
+    {
+        this.nodes = nodes;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setFilePlanService(FilePlanService filePlanService)
+    {
+        this.filePlanService = filePlanService;
+    }
+
+    public void setNodeService(NodeService nodeService)
+    {
+        this.nodeService = nodeService;
+    }
+
+    public void setContentService(ContentService contentService)
+    {
+        this.contentService = contentService;
+    }
+
+    public void setMimetypeService(MimetypeService mimetypeService)
+    {
+        this.mimetypeService = mimetypeService;
+    }
+
+    public void setDictionaryService(DictionaryService dictionaryService)
+    {
+        this.dictionaryService = dictionaryService;
+    }
+
+    public void setCapabilityService(CapabilityService capabilityService)
+    {
+        this.capabilityService = capabilityService;
+    }
+
+    public void setPermissionService(PermissionService permissionService)
+    {
+        this.permissionService = permissionService;
+    }
+
+    public void setRecordService(RecordService recordService)
+    {
+        this.recordService = recordService;
+    }
+
+    public void setAuthenticationUtil(AuthenticationUtil authenticationUtil)
+    {
+        this.authenticationUtil = authenticationUtil;
+    }
+    
+    public void setActivityPoster(ActivityPoster poster)
+    {
+        this.activityPoster = poster;
+    }
+
+    public void setSites(RMSites sites)
+    {
+        this.sites = sites;
+    }
+
+    /**
+     * lookup node and validate type
+     *
+     * @param nodeId
+     * @param expectedNodeType
+     * @return
+     * @throws EntityNotFoundException
+     */
+    public NodeRef lookupAndValidateNodeType(String nodeId, QName expectedNodeType) throws EntityNotFoundException
+    {
+        return lookupAndValidateNodeType(nodeId, expectedNodeType, null);
+    }
+
+    /**
+     * lookup node by id and relative path and validate type
+     *
+     * @param nodeId
+     * @param expectedNodeType
+     * @param relativePath
+     * @return
+     * @throws EntityNotFoundException
+     */
+    public NodeRef lookupAndValidateNodeType(String nodeId, QName expectedNodeType, String relativePath) throws EntityNotFoundException
+    {
+        return lookupAndValidateNodeType(nodeId, expectedNodeType, relativePath, false);
+    }
+
+    /**
+     * lookup node by id and relative path and validate type
+     *
+     * @param nodeId
+     * @param expectedNodeType
+     * @param relativePath
+     * @return
+     * @throws EntityNotFoundException
+     */
+    public NodeRef lookupAndValidateNodeType(String nodeId, QName expectedNodeType, String relativePath, boolean readOnlyRelativePath) throws EntityNotFoundException
+    {
+        ParameterCheck.mandatoryString("nodeId", nodeId);
+        ParameterCheck.mandatory("expectedNodeType", expectedNodeType);
+
+        /*
+         * Lookup by placeholder
+         */
+        NodeRef nodeRef;
+        if (nodeId.equals(FILE_PLAN_ALIAS))
+        {
+            NodeRef filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
+            if (filePlan != null)
+            {
+                nodeRef = filePlan;
+            }
+            else
+            {
+                throw new EntityNotFoundException(nodeId);
+            }
+        }
+        else if (nodeId.equals(TRANSFERS_ALIAS))
+        {
+            NodeRef filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
+            if (filePlan != null)
+            {
+                nodeRef = filePlanService.getTransferContainer(filePlan);
+            }
+            else
+            {
+                throw new EntityNotFoundException(nodeId);
+            }
+        }
+        else if (nodeId.equals(UNFILED_ALIAS))
+        {
+            NodeRef filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
+            if (filePlan != null)
+            {
+                nodeRef = filePlanService.getUnfiledContainer(filePlan);
+            }
+            else
+            {
+                throw new EntityNotFoundException(nodeId);
+            }
+        }
+        else if (nodeId.equals(HOLDS_ALIAS))
+        {
+            NodeRef filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
+            if (filePlan != null)
+            {
+                nodeRef = filePlanService.getHoldContainer(filePlan);
+            }
+            else
+            {
+                throw new EntityNotFoundException(nodeId);
+            }
+        }
+        else
+        {
+            nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
+        }
+
+        QName nodeType = nodeService.getType(nodeRef);
+        if (!nodeType.equals(expectedNodeType))
+        {
+            throw new InvalidArgumentException("The given id:'" + nodeId + "' (nodeType:" + nodeType.toString()
+            + ") is not valid for this endpoint. Expected nodeType is:" + expectedNodeType.toString());
+        }
+
+        if(StringUtils.isNotBlank(relativePath))
+        {
+            nodeRef = lookupAndValidateRelativePath(nodeRef, relativePath, readOnlyRelativePath, expectedNodeType);
+        }
+        return nodeRef;
+    }
+
+    /**
+     * TODO
+     * @param parameters
+     * @return
+     */
+    public List> getSortProperties(Parameters parameters)
+    {
+        List> sortProps = new ArrayList<>();
+        sortProps.add(new Pair<>(GetChildrenCannedQuery.SORT_QNAME_NODE_TYPE, true));
+        sortProps.add(new Pair<>(ContentModel.PROP_NAME, true));
+        return sortProps;
+    }
+
+    /**
+     * Write content to file
+     *
+     * @param nodeRef  the node to write the content to
+     * @param fileName  the name of the file (used for guessing the file's mimetype)
+     * @param stream  the input stream to write
+     * @param guessEncoding  whether to guess stream encoding
+     */
+    public void writeContent(NodeRef nodeRef, String fileName, InputStream stream, boolean guessEncoding)
+    {
+        try
+        {
+            ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
+
+            String mimeType = mimetypeService.guessMimetype(fileName);
+            if ((mimeType != null) && (!mimeType.equals(MimetypeMap.MIMETYPE_BINARY)))
+            {
+                // quick/weak guess based on file extension
+                writer.setMimetype(mimeType);
+            } else
+            {
+                // stronger guess based on file stream
+                writer.guessMimetype(fileName);
+            }
+
+            InputStream is = null;
+
+            if (guessEncoding)
+            {
+                is = new BufferedInputStream(stream);
+                is.mark(1024);
+                writer.setEncoding(guessEncoding(is, mimeType, false));
+                try
+                {
+                    is.reset();
+                } catch (IOException ioe)
+                {
+                    if (LOGGER.isWarnEnabled())
+                    {
+                        LOGGER.warn("Failed to reset stream after trying to guess encoding: " + ioe.getMessage());
+                    }
+                }
+            } else
+            {
+                is = stream;
+            }
+
+            writer.putContent(is);
+        }
+        catch (ContentQuotaException cqe)
+        {
+            throw new InsufficientStorageException();
+        }
+        catch (ContentLimitViolationException clv)
+        {
+            throw new RequestEntityTooLargeException(clv.getMessage());
+        }
+        catch (ContentIOException cioe)
+        {
+            if (cioe.getCause() instanceof NodeLockedException)
+            {
+                throw (NodeLockedException)cioe.getCause();
+            }
+            throw cioe;
+        }
+    }
+
+    /**
+     * Helper method that guesses the encoding of a stream of data
+     * @param in  the stream to guess the encoding for
+     * @param mimeType  the mimetype of the file
+     * @param close  if true the stream will be closed at the end
+     * @return the stream encoding
+     */
+    private String guessEncoding(InputStream in, String mimeType, boolean close)
+    {
+        String encoding = "UTF-8";
+        try
+        {
+            if (in != null)
+            {
+                Charset charset = mimetypeService.getContentCharsetFinder().getCharset(in, mimeType);
+                encoding = charset.name();
+            }
+        }
+        finally
+        {
+            try
+            {
+                if (close && (in != null))
+                {
+                    in.close();
+                }
+            }
+            catch (IOException ioe)
+            {
+                if (LOGGER.isWarnEnabled())
+                {
+                    LOGGER.warn("Failed to close stream after trying to guess encoding: " + ioe.getMessage());
+                }
+            }
+        }
+        return encoding;
+    }
+
+    /**
+     * Helper method that creates a relative path if it doesn't already exist
+     * The relative path will be build with nodes of the type specified in nodesType
+     * If the relative path already exists the method validates if the last element is of type nodesType
+     * The method does not validate the type of parentNodeRef
+     *
+     * @param parentNodeRef  the first node of the path
+     * @param relativePath  a string representing the relative path in the format "Folder1/Folder2/Folder3"
+     * @param nodesType  the type of all the containers in the path
+     * @return  the last element of the relative path
+     */
+    public NodeRef lookupAndValidateRelativePath(final NodeRef parentNodeRef, String relativePath, QName nodesType)
+    {
+        return lookupAndValidateRelativePath(parentNodeRef, relativePath, false, nodesType);
+    }
+
+    /**
+     * Helper method that creates a relative path if it doesn't already exist and if relative path is not read only.
+     * If relative path is read only an exception will be thrown if the provided relative path does not exist.
+     * The relative path will be build with nodes of the type specified in nodesType
+     * If the relative path already exists the method validates if the last element is of type nodesType
+     * The method does not validate the type of parentNodeRef
+     *
+     * @param parentNodeRef  the first node of the path
+     * @param relativePath  a string representing the relative path in the format "Folder1/Folder2/Folder3"
+     * @param readOnlyRelativePath the flag that indicates if the relativePath should be created if doesn't exist or not
+     * @param nodesType  the type of all the containers in the path
+     * @return  the last element of the relative path
+     */
+    public NodeRef lookupAndValidateRelativePath(final NodeRef parentNodeRef, String relativePath, boolean readOnlyRelativePath, QName nodesType)
+    {
+        mandatory("parentNodeRef", parentNodeRef);
+        mandatory("nodesType", nodesType);
+        if (StringUtils.isBlank(relativePath))
+        {
+            return parentNodeRef;
+        }
+        List pathElements = getPathElements(relativePath);
+        if (pathElements.isEmpty())
+        {
+            return parentNodeRef;
+        }
+
+        /*
+         * Get the latest existing path element
+         */
+        NodeRef lastNodeRef = parentNodeRef;
+        int i = 0;
+        for (; i < pathElements.size(); i++)
+        {
+            final String pathElement = pathElements.get(i);
+            final NodeRef contextParentNodeRef = lastNodeRef;
+            // Navigation should not check permissions
+            NodeRef child = authenticationUtil.runAsSystem(new RunAsWork()
+            {
+                @Override
+                public NodeRef doWork() throws Exception
+                {
+                    return nodeService.getChildByName(contextParentNodeRef, ContentModel.ASSOC_CONTAINS, pathElement);
+                }
+            });
+
+            if(child == null)
+            {
+                break;
+            }
+            lastNodeRef = child;
+        }
+        if(i == pathElements.size())
+        {
+            QName nodeType = nodeService.getType(lastNodeRef);
+            if(!nodeType.equals(nodesType))
+            {
+                throw new InvalidArgumentException("The given id:'"+ parentNodeRef.getId() +"' and the relative path '"+ relativePath + "' reach a node type invalid for this endpoint."
+                            + " Expected nodeType is:" + nodesType.toString() + ". Actual nodeType is:" + nodeType);
+            }
+            return lastNodeRef;
+        }
+        else
+        {
+            if(!readOnlyRelativePath)
+            {
+                pathElements = pathElements.subList(i, pathElements.size());
+            }
+            else
+            {
+                throw new NotFoundException("The entity with relativePath: " + relativePath + " was not found.");
+            }
+        }
+
+        /*
+         * Starting from the latest existing element create the rest of the elements
+         */
+        if(nodesType.equals(RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER))
+        {
+            for (String pathElement : pathElements)
+            {
+                lastNodeRef = fileFolderService.create(lastNodeRef, pathElement, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER).getNodeRef();
+            }
+        }
+        else if(nodesType.equals(RecordsManagementModel.TYPE_RECORD_CATEGORY))
+        {
+            for (String pathElement : pathElements)
+            {
+                lastNodeRef = filePlanService.createRecordCategory(lastNodeRef, pathElement);
+            }
+        }
+        else
+        {
+            // Throw internal error as this method should not be called for other types
+            throw new InternalServerErrorException("Creating relative path of type '" + nodesType + "' not suported for this endpoint");
+        }
+
+        return lastNodeRef;
+    }
+
+    /**
+     * Helper method that parses a string representing a file path and returns a list of element names
+     * @param path the file path represented as a string
+     * @return a list of file path element names
+     */
+    private List getPathElements(String path)
+    {
+        final List pathElements = new ArrayList<>();
+        if (path != null && path.trim().length() > 0)
+        {
+            // There is no need to check for leading and trailing "/"
+            final StringTokenizer tokenizer = new StringTokenizer(path, "/");
+            while (tokenizer.hasMoreTokens())
+            {
+                pathElements.add(tokenizer.nextToken().trim());
+            }
+        }
+        return pathElements;
+    }
+    /**
+     * Helper method that converts a map of String properties into a map of QName properties
+     * @param props
+     * @return a map of properties
+     */
+    public Map mapToNodeProperties(Map properties)
+    {
+        Map response = null;
+        if(properties != null)
+        {
+            response = nodes.mapToNodeProperties(properties);
+        }
+        return response;
+    }
+
+    /**
+     * Create an RM node
+     *
+     * @param parentNodeRef  the parent of the node
+     * @param name  the name of the new node
+     * @param type  the type of the node
+     * @param properties properties to set on the new node
+     * @param aspects aspects to set on the new node
+     * @return the new node
+     */
+    public NodeRef createRMNode(NodeRef parentNodeRef, String name, String type, Map properties, List aspects)
+    {
+        mandatory("parentNodeRef", parentNodeRef);
+        checkNotBlank(RMNode.PARAM_NAME, name);
+        checkNotBlank(RMNode.PARAM_NODE_TYPE, type);
+
+        // Create the node
+        NodeRef newNodeRef = null;
+        try
+        {
+            QName typeQName = nodes.createQName(type);
+            newNodeRef = fileFolderService.create(parentNodeRef, name, typeQName).getNodeRef();
+
+            // Set the provided properties if any
+            Map qnameProperties = mapToNodeProperties(properties);
+            if (qnameProperties != null)
+            {
+                nodeService.addProperties(newNodeRef, qnameProperties);
+            }
+
+            // If electronic record create empty content
+            if (!typeQName.equals(RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT)
+                    && dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT))
+            {
+                writeContent(newNodeRef, name, new ByteArrayInputStream("".getBytes()), false);
+            }
+
+            // Add the provided aspects if any
+            if (aspects != null)
+            {
+                nodes.addCustomAspects(newNodeRef, aspects, ApiNodesModelFactory.EXCLUDED_ASPECTS);
+            }
+        }
+        catch (InvalidTypeException ex)
+        {
+            throw new InvalidArgumentException("The given type:'" + type + "' is invalid '");
+        }
+
+        return newNodeRef;
+    }
+
+    /**
+     * Upload a record
+     *
+     * @param parentNodeRef the parent of the record
+     * @param name the name of the record
+     * @param type the type of the record (if null the record's type will be cm:content)
+     * @param properties properties to set (can be null)
+     * @param stream the stream to write
+     * @return the new record
+     */
+    public NodeRef uploadRecord(NodeRef parentNodeRef, String name, String type, Map properties, InputStream stream)
+    {
+        checkNotBlank(RMNode.PARAM_NAME, name);
+        mandatory("stream", stream);
+
+        // Create the node
+        QName typeQName = StringUtils.isBlank(type) ? ContentModel.TYPE_CONTENT : nodes.createQName(type);
+        if(!dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT))
+        {
+            throw new InvalidArgumentException("Can only upload type of cm:content: " + typeQName);
+        }
+        NodeRef newNodeRef = fileFolderService.create(parentNodeRef, name, typeQName).getNodeRef();
+
+        // Write content
+        writeContent(newNodeRef, name, stream, true);
+
+        // Set the provided properties if any
+        Map qnameProperties = mapToNodeProperties(properties);
+        if(qnameProperties != null)
+        {
+            nodeService.addProperties(newNodeRef, qnameProperties);
+        }
+
+        return newNodeRef;
+    }
+
+    /**
+     * Returns a List of filter properties specified by request parameters.
+     * @param parameters The {@link Parameters} object to get the parameters passed into the request
+     *        including:
+     *        - filter, sort & paging params (where, orderBy, skipCount, maxItems)
+     * @return The list of {@link FilterProp}. Can be null.
+     */
+    public List getListChildrenFilterProps(Parameters parameters, Set listFolderChildrenEqualsQueryProperties)
+    {
+
+        List filterProps = null;
+        Query q = parameters.getQuery();
+        if (q != null)
+        {
+            MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(listFolderChildrenEqualsQueryProperties, null);
+            QueryHelper.walk(q, propertyWalker);
+
+            Boolean isPrimary = propertyWalker.getProperty(RMNode.PARAM_ISPRIMARY, WhereClauseParser.EQUALS, Boolean.class);
+
+            if (isPrimary != null)
+            {
+                filterProps = new ArrayList<>(1);
+                filterProps.add(new FilterPropBoolean(GetChildrenCannedQuery.FILTER_QNAME_NODE_IS_PRIMARY, isPrimary));
+            }
+            Boolean isClosed = propertyWalker.getProperty(RMNode.PARAM_IS_CLOSED, WhereClauseParser.EQUALS, Boolean.class);
+            if (isClosed != null)
+            {
+                filterProps = new ArrayList<>(1);
+                filterProps.add(new FilterPropBoolean(RecordsManagementModel.PROP_IS_CLOSED, isClosed));
+            }
+            //TODO see how we can filter for categories that have retention schedule
+//            Boolean hasRetentionSchedule = propertyWalker.getProperty(RMNode.PARAM_HAS_RETENTION_SCHEDULE, WhereClauseParser.EQUALS, Boolean.class);
+//            if (hasRetentionSchedule != null)
+//            {
+//                filterProps = new ArrayList<>(1);
+//            }
+        }
+        return filterProps;
+    }
+
+    /**
+     * Utility method that updates a node's name and properties
+     * @param nodeRef  the node to update
+     * @param updateInfo  information to update the record with
+     * @param parameters  request parameters
+     */
+    public void updateNode(NodeRef nodeRef, RMNode updateInfo, Parameters parameters)
+    {
+        Map props = new HashMap<>(0);
+
+        if (updateInfo.getProperties() != null)
+        {
+            props = mapToNodeProperties(updateInfo.getProperties());
+        }
+
+        String name = updateInfo.getName();
+        if ((name != null) && (!name.isEmpty()))
+        {
+            // update node name if needed
+            props.put(ContentModel.PROP_NAME, name);
+        }
+
+        try
+        {
+            // update node properties - note: null will unset the specified property
+            nodeService.addProperties(nodeRef, props);
+        }
+        catch (DuplicateChildNodeNameException dcne)
+        {
+            throw new ConstraintViolatedException(dcne.getMessage());
+        }
+
+        // update aspects
+        List aspectNames = updateInfo.getAspectNames();
+        nodes.updateCustomAspects(nodeRef, aspectNames, ApiNodesModelFactory.EXCLUDED_ASPECTS);
+    }
+
+    /**
+     * Validates a record
+     *
+     * @param recordId  the id of the record to validate
+     * @return
+     */
+    public NodeRef validateRecord(String recordId) throws InvalidArgumentException
+    {
+        NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, recordId);
+        if(!recordService.isRecord(nodeRef))
+        {
+            throw new IllegalArgumentException("The given id:'"+ recordId +"' is not valid for this endpoint. This endpoint only supports records.");
+        }
+        return nodeRef;
+    }
+
+    public BinaryResource getContent(NodeRef nodeRef, Parameters parameters, boolean recordActivity)
+    {
+        return nodes.getContent(nodeRef, parameters, recordActivity);
+    }
+
+    /**
+     * Utility method that updates a transfer container's name and properties
+     *
+     * @param nodeRef  the node to update
+     * @param transferContainerInfo  information to update the transfer container with
+     * @param parameters  request parameters
+     */
+    public void updateTransferContainer(NodeRef nodeRef, TransferContainer transferContainerInfo, Parameters parameters)
+    {
+        Map props = new HashMap<>(0);
+
+        if (transferContainerInfo.getProperties() != null)
+        {
+            props = mapToNodeProperties(transferContainerInfo.getProperties());
+        }
+
+        String name = transferContainerInfo.getName();
+        if ((name != null) && (!name.isEmpty()))
+        {
+            // update node name if needed
+            props.put(ContentModel.PROP_NAME, name);
+        }
+
+        try
+        {
+            // update node properties - note: null will unset the specified property
+            nodeService.addProperties(nodeRef, props);
+        }
+        catch (DuplicateChildNodeNameException dcne)
+        {
+            throw new ConstraintViolatedException(dcne.getMessage());
+        }
+    }
+
+    /**
+     * Helper method that generates allowable operation for the provided node
+     * @param nodeRef the node to get the allowable operations for
+     * @param type the type of the provided nodeRef
+     * @return a sublist of [{@link Nodes.OP_DELETE}, {@link Nodes.OP_CREATE}, {@link Nodes.OP_UPDATE}] representing the allowable operations for the provided node
+     */
+    protected List getAllowableOperations(NodeRef nodeRef, QName typeQName)
+    {
+        List allowableOperations = new ArrayList<>();
+
+        boolean isFilePlan =  typeQName.equals(RecordsManagementModel.TYPE_FILE_PLAN);
+        boolean isTransferContainer = typeQName.equals(RecordsManagementModel.TYPE_TRANSFER_CONTAINER);
+        boolean isUnfiledContainer = typeQName.equals(RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER);
+        boolean isHoldsContainer = typeQName.equals(RecordsManagementModel.TYPE_HOLD_CONTAINER);
+        boolean isSpecialContainer = isFilePlan || isTransferContainer || isUnfiledContainer || isHoldsContainer;
+
+        // DELETE
+        if(!isSpecialContainer &&
+                capabilityService.getCapability("Delete").evaluate(nodeRef) == AccessDecisionVoter.ACCESS_GRANTED)
+        {
+            allowableOperations.add(Nodes.OP_DELETE);
+        }
+
+        // CREATE
+        if(TYPES_CAN_CREATE.contains(typeQName) &&
+                capabilityService.getCapability("FillingPermissionOnly").evaluate(nodeRef) == AccessDecisionVoter.ACCESS_GRANTED)
+        {
+            allowableOperations.add(Nodes.OP_CREATE);
+        }
+
+        // UPDATE
+        if (capabilityService.getCapability("Update").evaluate(nodeRef) == AccessDecisionVoter.ACCESS_GRANTED)
+        {
+            allowableOperations.add(Nodes.OP_UPDATE);
+        }
+
+        return allowableOperations;
+    }
+
+    protected PathInfo lookupPathInfo(NodeRef nodeRefIn)
+    {
+        List pathElements = new ArrayList<>();
+        Boolean isComplete = Boolean.TRUE;
+        final Path nodePath = nodeService.getPath(nodeRefIn);;
+        final int pathIndex = 2;
+
+        for (int i = nodePath.size() - pathIndex; i >= 0; i--)
+        {
+            Element element = nodePath.get(i);
+            if (element instanceof Path.ChildAssocElement)
+            {
+                ChildAssociationRef elementRef = ((Path.ChildAssocElement) element).getRef();
+                if (elementRef.getParentRef() != null)
+                {
+                    NodeRef childNodeRef = elementRef.getChildRef();
+                    if (permissionService.hasPermission(childNodeRef, PermissionService.READ) == AccessStatus.ALLOWED)
+                    {
+                        Serializable nameProp = nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME);
+                        pathElements.add(0, new ElementInfo(childNodeRef.getId(), nameProp.toString()));
+                    }
+                    else
+                    {
+                        // Just return the pathInfo up to the location where the user has access
+                        isComplete = Boolean.FALSE;
+                        break;
+                    }
+                }
+            }
+        }
+
+        String pathStr = null;
+        if (pathElements.size() > 0)
+        {
+            StringBuilder sb = new StringBuilder(120);
+            for (PathInfo.ElementInfo e : pathElements)
+            {
+                sb.append("/").append(e.getName());
+            }
+            pathStr = sb.toString();
+        }
+        else
+        {
+            // There is no path element, so set it to null in order to be
+            // ignored by Jackson during serialisation
+            isComplete = null;
+        }
+        return new PathInfo(pathStr, isComplete, pathElements);
+    }
+
+    /**
+     * Helper method to obtain file plan type or null if the rm site does not exist.
+     *
+     * @return file plan type or null
+     */
+    public QName getFilePlanType()
+    {
+        NodeRef filePlanNodeRef = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
+        if(filePlanNodeRef != null)
+        {
+            return nodeService.getType(filePlanNodeRef);
+        }
+        return null;
+    }
+    /**
+     * Posts activities for given fileInfo
+     * 
+     * @param fileInfo
+     * @param parentNodeRef
+     * @param activityType
+     */
+    public void postActivity(FileInfo fileInfo, NodeRef parentNodeRef, String activityType)
+    {
+        ActivityInfo activityInfo = null;
+        RMSite rmSite = sites.getRMSite(RM_SITE_ID);
+        if (rmSite != null && !rmSite.getId().equals(""))
+        {
+            if (fileInfo != null)
+            {
+                boolean isContent = dictionaryService.isSubClass(fileInfo.getType(), ContentModel.TYPE_CONTENT);
+
+                if (isContent)
+                {
+                    activityInfo = new ActivityInfo(null, parentNodeRef, RM_SITE_ID, fileInfo);
+                }
+            }
+        }
+        else
+        {
+            if (LOGGER.isDebugEnabled())
+            {
+                LOGGER.debug("Non-site activity, so ignored " + fileInfo.getNodeRef());
+            }
+        }
+
+        if (activityInfo == null)
+            return; // Nothing to do.
+
+        if (activityType != null)
+        {
+            activityPoster.postFileFolderActivity(activityType, null, TenantUtil.getCurrentDomain(), activityInfo.getSiteId(),
+                    activityInfo.getParentNodeRef(), activityInfo.getNodeRef(), activityInfo.getFileName(), Activities.APP_TOOL,
+                    Activities.RESTAPI_CLIENT, activityInfo.getFileInfo());
+
+        }
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RMNodesImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RMNodesImpl.java
deleted file mode 100644
index e9b7f71de9..0000000000
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RMNodesImpl.java
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api.impl;
-
-import java.security.InvalidParameterException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-
-import org.alfresco.model.ContentModel;
-import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry;
-import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
-import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
-import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
-import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
-import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
-import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
-import org.alfresco.rest.api.Nodes;
-import org.alfresco.rest.api.impl.NodesImpl;
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.api.model.UserInfo;
-import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
-import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
-import org.alfresco.rest.framework.resource.parameters.Parameters;
-import org.alfresco.rm.rest.api.RMNodes;
-import org.alfresco.rm.rest.api.model.FileplanComponentNode;
-import org.alfresco.rm.rest.api.model.RecordCategoryNode;
-import org.alfresco.rm.rest.api.model.RecordFolderNode;
-import org.alfresco.rm.rest.api.model.RecordNode;
-import org.alfresco.service.cmr.dictionary.DictionaryService;
-import org.alfresco.service.cmr.model.FileFolderService;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.repository.NodeService;
-import org.alfresco.service.namespace.QName;
-import org.alfresco.util.Pair;
-import org.alfresco.util.ParameterCheck;
-import org.apache.commons.lang.StringUtils;
-import org.springframework.extensions.webscripts.servlet.FormData;
-
-import net.sf.acegisecurity.vote.AccessDecisionVoter;
-
-/**
- * Centralizes access to the repository.
- *
- * @author Ana Bozianu
- * @since 2.6
- */
-public class RMNodesImpl extends NodesImpl implements RMNodes
-{
-    private enum RMNodeType
-    {
-        // Note: ordered
-        CATEGORY, RECORD_FOLDER, FILE
-    }
-
-    private FilePlanService filePlanService;
-    private NodeService nodeService;
-    private RecordsManagementServiceRegistry serviceRegistry;
-    private DictionaryService dictionaryService;
-    private DispositionService dispositionService;
-    private CapabilityService capabilityService;
-    private FileFolderService fileFolderService;
-
-    public void init()
-    {
-        super.init();
-        this.nodeService = serviceRegistry.getNodeService();
-        this.dictionaryService = serviceRegistry.getDictionaryService();
-        this.dispositionService = serviceRegistry.getDispositionService();
-    }
-
-    public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry serviceRegistry)
-    {
-        this.serviceRegistry = serviceRegistry;
-    }
-
-    public void setFilePlanService(FilePlanService filePlanService)
-    {
-        this.filePlanService = filePlanService;
-    }
-
-    public void setCapabilityService(CapabilityService capabilityService)
-    {
-        this.capabilityService = capabilityService;
-    }
-
-    public void setFileFolderService(FileFolderService fileFolderService)
-    {
-        this.fileFolderService = fileFolderService;
-    }
-
-    @Override
-    public Node getFolderOrDocument(final NodeRef nodeRef, NodeRef parentNodeRef, QName nodeTypeQName, List includeParam, Map mapUserInfo)
-    {
-        Node originalNode = super.getFolderOrDocument(nodeRef, parentNodeRef, nodeTypeQName, includeParam, mapUserInfo);
-
-        if(nodeTypeQName == null)
-        {
-            nodeTypeQName = nodeService.getType(nodeRef);
-        }
-
-        RMNodeType type = getType(nodeTypeQName, nodeRef);
-        FileplanComponentNode node = null;
-        if (mapUserInfo == null)
-        {
-            mapUserInfo = new HashMap<>(2);
-        }
-
-        if (type == null)
-        {
-            if (filePlanService.isFilePlanComponent(nodeRef))
-            {
-                node = new FileplanComponentNode(originalNode);
-            }
-            else
-            {
-                throw new InvalidParameterException("The provided node is not a fileplan component");
-            }
-        }
-        else
-        {
-            switch(type)
-            {
-                case CATEGORY:
-                    RecordCategoryNode categoryNode = new RecordCategoryNode(originalNode);
-                    if (includeParam.contains(PARAM_INCLUDE_HAS_RETENTION_SCHEDULE))
-                    {
-                        DispositionSchedule ds = dispositionService.getDispositionSchedule(nodeRef);
-                        categoryNode.setHasRetentionSchedule(ds!=null?true:false);
-                    }
-                    node = categoryNode;
-                    break;
-                case RECORD_FOLDER:
-                    RecordFolderNode rfNode = new RecordFolderNode(originalNode);
-                    if (includeParam.contains(PARAM_INCLUDE_IS_CLOSED))
-                    {
-                        rfNode.setIsClosed((Boolean) nodeService.getProperty(nodeRef, RecordsManagementModel.PROP_IS_CLOSED));
-                    }
-                    node = rfNode;
-                    break;
-                case FILE:
-                    RecordNode rNode = new RecordNode(originalNode);
-                    if (includeParam.contains(PARAM_INCLUDE_IS_COMPLETED))
-                    {
-                        rNode.setIsCompleted(nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_DECLARED_RECORD));
-                    }
-                    node = rNode;
-                    break;
-            }
-        }
-
-        if (includeParam.contains(PARAM_INCLUDE_ALLOWABLEOPERATIONS))
-        {
-            // If the user does not have any of the mapped permissions then "allowableOperations" is not returned (rather than an empty array)
-            List allowableOperations = getAllowableOperations(nodeRef, type);
-            node.setAllowableOperations((allowableOperations.size() > 0 )? allowableOperations : null);
-        }
-
-        return node;
-    }
-
-    /**
-     * Helper method that generates allowable operation for the provided node
-     * @param nodeRef the node to get the allowable operations for
-     * @param type the type of the provided nodeRef
-     * @return a sublist of [{@link Nodes.OP_DELETE}, {@link Nodes.OP_CREATE}, {@link Nodes.OP_UPDATE}] representing the allowable operations for the provided node
-     */
-    private List getAllowableOperations(NodeRef nodeRef, RMNodeType type)
-    {
-        List allowableOperations = new ArrayList<>();
-
-        NodeRef filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
-        boolean isFilePlan = nodeRef.equals(filePlan);
-        boolean isTransferContainer = nodeRef.equals(filePlanService.getTransferContainer(filePlan));
-        boolean isUnfiledContainer = nodeRef.equals(filePlanService.getUnfiledContainer(filePlan));
-        boolean isHoldsContainer = nodeRef.equals(filePlanService.getHoldContainer(filePlan)) ;
-        boolean isSpecialContainer = isFilePlan || isTransferContainer || isUnfiledContainer || isHoldsContainer;
-
-        // DELETE
-        if(!isSpecialContainer &&
-                capabilityService.getCapability("Delete").evaluate(nodeRef) == AccessDecisionVoter.ACCESS_GRANTED)
-        {
-            allowableOperations.add(OP_DELETE);
-        }
-
-        // CREATE
-        if(type != RMNodeType.FILE &&
-                !isTransferContainer &&
-                capabilityService.getCapability("FillingPermissionOnly").evaluate(nodeRef) == AccessDecisionVoter.ACCESS_GRANTED)
-        {
-            allowableOperations.add(OP_CREATE);
-        }
-
-        // UPDATE
-        if (capabilityService.getCapability("Update").evaluate(nodeRef) == AccessDecisionVoter.ACCESS_GRANTED)
-        {
-            allowableOperations.add(OP_UPDATE);
-        }
-
-        return allowableOperations;
-    }
-
-    @Override
-    public NodeRef validateNode(String nodeId)
-    {
-        ParameterCheck.mandatoryString("nodeId", nodeId);
-
-        if (nodeId.equals(PATH_FILE_PLAN))
-        {
-            NodeRef filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
-            if (filePlan != null)
-            {
-                return filePlan;
-            }
-            else
-            {
-                throw new EntityNotFoundException(nodeId);
-            }
-        }
-        else if (nodeId.equals(PATH_TRANSFERS))
-        {
-            NodeRef filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
-            if (filePlan != null)
-            {
-                return filePlanService.getTransferContainer(filePlan);
-            }
-            else
-            {
-                throw new EntityNotFoundException(nodeId);
-            }
-        }
-        else if (nodeId.equals(PATH_UNFILED))
-        {
-            NodeRef filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
-            if (filePlan != null)
-            {
-                return filePlanService.getUnfiledContainer(filePlan);
-            }
-            else
-            {
-                throw new EntityNotFoundException(nodeId);
-            }
-        }
-        else if (nodeId.equals(PATH_HOLDS))
-        {
-            NodeRef filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
-            if (filePlan != null)
-            {
-                return filePlanService.getHoldContainer(filePlan);
-            }
-            else
-            {
-                throw new EntityNotFoundException(nodeId);
-            }
-        }
-
-        return super.validateNode(nodeId);
-    }
-
-    private RMNodeType getType(QName typeQName, NodeRef nodeRef)
-    {
-        // quick check for common types
-        if (typeQName.equals(RecordsManagementModel.TYPE_RECORD_FOLDER))
-        {
-            return RMNodeType.RECORD_FOLDER;
-        }
-        if (typeQName.equals(RecordsManagementModel.TYPE_RECORD_CATEGORY))
-        {
-            return RMNodeType.CATEGORY;
-        }
-        if (typeQName.equals(ContentModel.TYPE_CONTENT))
-        {
-            return RMNodeType.FILE;
-        }
-
-        // check subclasses
-        if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT))
-        {
-            return RMNodeType.FILE;
-        }
-        if (dictionaryService.isSubClass(typeQName, RecordsManagementModel.TYPE_RECORD_FOLDER))
-        {
-            return RMNodeType.RECORD_FOLDER;
-        }
-
-        return null;
-    }
-
-    @Override
-    protected Pair, Set> buildSearchTypesAndIgnoreAspects(QName nodeTypeQName, boolean includeSubTypes, Set ignoreQNameTypes, Boolean includeFiles, Boolean includeFolders)
-    {
-        Pair, Set> searchTypesAndIgnoreAspects = super.buildSearchTypesAndIgnoreAspects(nodeTypeQName, includeSubTypes, ignoreQNameTypes, includeFiles, includeFolders);
-        Set searchTypeQNames = searchTypesAndIgnoreAspects.getFirst();
-        Set ignoreAspectQNames = searchTypesAndIgnoreAspects.getSecond();
-
-        searchTypeQNames.remove(RecordsManagementModel.TYPE_HOLD_CONTAINER);
-        searchTypeQNames.remove(RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER);
-        searchTypeQNames.remove(RecordsManagementModel.TYPE_TRANSFER_CONTAINER);
-
-        searchTypeQNames.remove(RecordsManagementModel.TYPE_DISPOSITION_SCHEDULE);
-        searchTypeQNames.remove(RecordsManagementModel.TYPE_DISPOSITION_ACTION);
-        searchTypeQNames.remove(RecordsManagementModel.TYPE_DISPOSITION_ACTION_DEFINITION);
-
-        return new Pair<>(searchTypeQNames, ignoreAspectQNames);
-    }
-
-    @Override
-    public Node createNode(String parentFolderNodeId, Node nodeInfo, Parameters parameters)
-    {
-        // create RM path if needed and call the super method with the last element of the created path
-        String relativePath = nodeInfo.getRelativePath();
-
-        // Get the type of the node to be created
-        String nodeType = nodeInfo.getNodeType();
-        if ((nodeType == null) || nodeType.isEmpty())
-        {
-            throw new InvalidArgumentException("Node type is expected: "+parentFolderNodeId+","+nodeInfo.getName());
-        }
-        QName nodeTypeQName = createQName(nodeType);
-
-        // Get or create the path
-        NodeRef parentNodeRef = getOrCreatePath(parentFolderNodeId, relativePath, nodeTypeQName);
-
-        // Set relative path to null as we pass the last element from the path
-        nodeInfo.setRelativePath(null);
-
-        return super.createNode(parentNodeRef.getId(), nodeInfo, parameters);
-    }
-
-    @Override
-    public Node upload(String parentFolderNodeId, FormData formData, Parameters parameters)
-    {
-        if (formData == null || !formData.getIsMultiPart())
-        {
-            throw new InvalidArgumentException("The request content-type is not multipart: "+parentFolderNodeId);
-        }
-
-        for (FormData.FormField field : formData.getFields())
-        {
-            if(field.getName().equalsIgnoreCase("relativepath"))
-            {
-                // Create the path if it does not exist
-                getOrCreatePath(parentFolderNodeId, getStringOrNull(field.getValue()), ContentModel.TYPE_CONTENT);
-                break;
-            }
-        }
-
-        return super.upload(parentFolderNodeId, formData, parameters);
-    }
-
-    private String getStringOrNull(String value)
-    {
-        if (StringUtils.isNotEmpty(value))
-        {
-            return value.equalsIgnoreCase("null") ? null : value;
-        }
-        return null;
-    }
-
-    @Override
-    public NodeRef getOrCreatePath(String parentFolderNodeId, String relativePath, QName nodeTypeQName)
-    {
-        NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null);
-
-        if (relativePath == null)
-        {
-            return parentNodeRef;
-        }
-        List pathElements = getPathElements(relativePath);
-        if (pathElements.isEmpty())
-        {
-            return parentNodeRef;
-        }
-
-        /*
-         * Get the latest existing path element
-         */
-        int i = 0;
-        for (; i < pathElements.size(); i++)
-        {
-            final String pathElement = pathElements.get(i);
-            final NodeRef contextParentNodeRef = parentNodeRef;
-            // Navigation should not check permissions
-            NodeRef child = AuthenticationUtil.runAsSystem(new RunAsWork()
-            {
-                @Override
-                public NodeRef doWork() throws Exception
-                {
-                    return nodeService.getChildByName(contextParentNodeRef, ContentModel.ASSOC_CONTAINS, pathElement);
-                }
-            });
-
-            if(child == null)
-            {
-                break;
-            }
-            parentNodeRef = child;
-        }
-        if(i == pathElements.size())
-        {
-            return parentNodeRef;
-        }
-        else
-        {
-            pathElements = pathElements.subList(i, pathElements.size());
-        }
-
-        /*
-         * Starting from the latest existing element create the rest of the elements
-         */
-        QName parentNodeType = nodeService.getType(parentNodeRef);
-        if(dictionaryService.isSubClass(parentNodeType, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER) ||
-           dictionaryService.isSubClass(parentNodeType, RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER))
-        {
-            for (String pathElement : pathElements)
-            {
-                // Create unfiled record folder
-                parentNodeRef = fileFolderService.create(parentNodeRef, pathElement, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER).getNodeRef();
-            }
-        }
-        else
-        {
-            /* Outside the unfiled record container the path elements are record categories
-             * except the last element which is a record folder if the created node is of type content
-             */
-            Iterator iterator = pathElements.iterator();
-            while(iterator.hasNext())
-            {
-                String pathElement = iterator.next();
-
-                if(!iterator.hasNext() && dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_CONTENT))
-                {
-                    // last element, create record folder if the node to be created is content
-                    parentNodeRef = fileFolderService.create(parentNodeRef, pathElement, RecordsManagementModel.TYPE_RECORD_FOLDER).getNodeRef();
-                }
-                else
-                {
-                    // create record category
-                    parentNodeRef = filePlanService.createRecordCategory(parentNodeRef, pathElement);
-                }
-            }
-        }
-
-        return parentNodeRef;
-    }
-
-    /**
-     * Helper method that parses a string representing a file path and returns a list of element names
-     * @param path the file path represented as a string
-     * @return a list of file path element names
-     */
-    private List getPathElements(String path)
-    {
-        final List pathElements = new ArrayList<>();
-        if (path != null && path.trim().length() > 0)
-        {
-            // There is no need to check for leading and trailing "/"
-            final StringTokenizer tokenizer = new StringTokenizer(path, "/");
-            while (tokenizer.hasMoreTokens())
-            {
-                pathElements.add(tokenizer.nextToken().trim());
-            }
-        }
-        return pathElements;
-    }
-
-}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RMSitesImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RMSitesImpl.java
index 45a8b50cea..f8c64fa807 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RMSitesImpl.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RMSitesImpl.java
@@ -27,31 +27,19 @@
 
 package org.alfresco.rm.rest.api.impl;
 
-import org.alfresco.model.ContentModel;
 import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model;
 import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
-import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.site.SiteServiceException;
-import org.alfresco.rest.api.impl.SiteImportPackageHandler;
 import org.alfresco.rest.api.impl.SitesImpl;
 import org.alfresco.rest.api.model.Site;
-import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
-import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
+import org.alfresco.rest.api.model.SiteUpdate;
 import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
 import org.alfresco.rest.framework.resource.parameters.Parameters;
 import org.alfresco.rm.rest.api.RMSites;
 import org.alfresco.rm.rest.api.model.RMSite;
 import org.alfresco.rm.rest.api.model.RMSiteCompliance;
-import org.alfresco.rm.rest.api.model.SiteUpdate;
 import org.alfresco.service.cmr.repository.NodeRef;
 import org.alfresco.service.cmr.site.SiteInfo;
-import org.alfresco.service.cmr.site.SiteService;
 import org.alfresco.service.cmr.site.SiteVisibility;
-import org.alfresco.service.cmr.view.ImportPackageHandler;
-import org.alfresco.service.cmr.view.ImporterBinding;
-import org.alfresco.service.cmr.view.ImporterContentCache;
-import org.alfresco.service.cmr.view.ImporterProgress;
-import org.alfresco.service.cmr.view.Location;
 import org.alfresco.service.namespace.QName;
 
 /**
@@ -79,134 +67,19 @@ public class RMSitesImpl extends SitesImpl implements RMSites
         return new RMSite(site, compliance);
     }
 
-    /**
-     * TODO remove when upgrading to 5.2.N. We'll need to only extend the extended method createSite(Site) and use the siteService method that gets site type as parameter
-     */
     @Override
-    public Site createSite(Site site, Parameters parameters)
+    protected SiteInfo createSite(Site site)
     {
-        site = validateSite(site);
-
-        SiteInfo siteInfo = null;
-        try
-        {
-            siteInfo = siteService.createSite(RM_SITE_PRESET, RM_SITE_ID, site.getTitle(), site.getDescription(),
-                        SiteVisibility.PUBLIC, getRMSiteType((RMSite) site));
-        }
-        catch (SiteServiceException sse)
-        {
-            if (sse.getMsgId().equals("site_service.unable_to_create"))
-            {
-                throw new ConstraintViolatedException(sse.getMessage());
-            }
-            else
-            {
-                throw sse;
-            }
-        }
-
-        String siteId = siteInfo.getShortName();
-        NodeRef siteNodeRef = siteInfo.getNodeRef();
-
-        // import default/fixed preset Share surf config
-        importSite(siteId, siteNodeRef);
-
-        // pre-create doclib
-        siteService.createContainer(siteId, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null);
-
-        // default false (if not provided)
-        boolean skipAddToFavorites = Boolean.valueOf(parameters.getParameter(PARAM_SKIP_ADDTOFAVORITES));
-        if (skipAddToFavorites == false)
-        {
-            String personId = AuthenticationUtil.getFullyAuthenticatedUser();
-            favouritesService.addFavourite(personId, siteNodeRef); // ignore result
-        }
-
-        return getSite(siteInfo, true);
+        return siteService.createSite(RM_SITE_PRESET, RM_SITE_ID, site.getTitle(), site.getDescription(), SiteVisibility.PUBLIC, getRMSiteType((RMSite) site));
     }
 
     /**
-     * Copied from SitesImpl since we didn't had access to it.
-     *
-     * TODO to remove when upgrading to 5.2.N
-     *
-     * @param siteInfo
-     * @param includeRole
-     * @return
-     */
-    private Site getSite(SiteInfo siteInfo, boolean includeRole)
-    {
-        // set the site id to the short name (to deal with case sensitivity issues with using the siteId from the url)
-        String siteId = siteInfo.getShortName();
-        String role = null;
-        if (includeRole)
-        {
-            role = getSiteRole(siteId);
-        }
-        return new Site(siteInfo, role);
-    }
-
-    /**
-     * Copied from SitesImpl since we didn't had access to it.
-     *
-     * TODO to be removed when upgrading to 5.2.N
-     *
-     * @param siteId
-     * @param siteNodeRef
-     */
-    private void importSite(final String siteId, final NodeRef siteNodeRef)
-    {
-        ImportPackageHandler acpHandler = new SiteImportPackageHandler(siteSurfConfig, siteId);
-        Location location = new Location(siteNodeRef);
-        ImporterBinding binding = new ImporterBinding()
-        {
-            @Override
-            public String getValue(String key)
-            {
-                if (key.equals("siteId"))
-                {
-                    return siteId;
-                }
-                return null;
-            }
-
-            @Override
-            public UUID_BINDING getUUIDBinding()
-            {
-                return UUID_BINDING.CREATE_NEW;
-            }
-
-            @Override
-            public QName[] getExcludedClasses()
-            {
-                return null;
-            }
-
-            @Override
-            public boolean allowReferenceWithinTransaction()
-            {
-                return false;
-            }
-
-            @Override
-            public ImporterContentCache getImportConentCache()
-            {
-                return null;
-            }
-        };
-        importerService.importView(acpHandler, location, binding, (ImporterProgress)null);
-    }
-
-    /**
-     * This method is copied from SitesImpl since we could not access it since it is private.
-     *
-     * TODO change this to protected and override validate method from core when upgrading to 5.2.N
-     *
      * Even if the method it will be protected in core, we still need to override since we don't need to check if the visibility is set since for RM site it is always PUBLIC.
      * We also don't need to generate the id from title, or to check the id, since the id is always rm.
      * @param site
      * @return
      */
+    @Override
     protected Site validateSite(Site site)
     {
         // site title - mandatory
@@ -269,46 +142,6 @@ public class RMSitesImpl extends SitesImpl implements RMSites
         return compliance;
     }
 
-    /**
-     * TODO copied from core 5.2.N since we don't have it in 5.2.a-EA version. To be removed when upgrading.
-     * @param siteId
-     * @param update
-     * @param parameters
-     * @return
-     */
-    public Site updateSite(String siteId, SiteUpdate update, Parameters parameters)
-    {
-        // Get the site by ID (aka short name)
-        SiteInfo siteInfo = validateSite(siteId);
-        if (siteInfo == null)
-        {
-            // site does not exist
-            throw new EntityNotFoundException(siteId);
-        }
-
-        // Bind any provided values to the site info, allowing for "partial" updates.
-        if (update.getTitle() != null)
-        {
-            siteInfo.setTitle(update.getTitle());
-        }
-        if (update.getDescription() != null)
-        {
-            siteInfo.setDescription(update.getDescription());
-        }
-        if (update.getVisibility() != null)
-        {
-            siteInfo.setVisibility(update.getVisibility());
-        }
-
-        // Validate the new details
-        validateSite(new Site(siteInfo, null));
-
-        // Perform the actual update.
-        siteService.updateSite(siteInfo);
-
-        return getSite(siteId);
-    }
-
     /**
      * Gets RM site type based on compliance.
      *
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RecordsImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RecordsImpl.java
deleted file mode 100644
index 2938feee94..0000000000
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/RecordsImpl.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api.impl;
-
-import java.security.InvalidParameterException;
-
-import org.alfresco.model.ContentModel;
-import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
-import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
-import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
-import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
-import org.alfresco.repo.node.integrity.IntegrityException;
-import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
-import org.alfresco.rest.framework.resource.parameters.Parameters;
-import org.alfresco.rm.rest.api.RMNodes;
-import org.alfresco.rm.rest.api.Records;
-import org.alfresco.rm.rest.api.model.TargetContainer;
-import org.alfresco.service.cmr.dictionary.DictionaryService;
-import org.alfresco.service.cmr.model.FileExistsException;
-import org.alfresco.service.cmr.model.FileFolderService;
-import org.alfresco.service.cmr.model.FileNotFoundException;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.repository.NodeService;
-import org.apache.commons.lang.StringUtils;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.extensions.surf.util.ParameterCheck;
-
-/**
- * Centralizes access to record services
- * 
- * @author Ana Bozianu
- * @since 2.6
- */
-public class RecordsImpl implements Records, InitializingBean
-{
-    protected RecordService recordService;
-    protected FilePlanService filePlanService;
-    protected NodeService nodeService;
-    protected FileFolderService fileFolderService;
-    protected DictionaryService dictionaryService;
-    protected AuthenticationUtil authenticationUtil;
-    protected RMNodes nodes;
-
-    public void setRecordService(RecordService recordService)
-    {
-        this.recordService = recordService;
-    }
-
-    public void setFilePlanService(FilePlanService filePlanService)
-    {
-        this.filePlanService = filePlanService;
-    }
-
-    public void setNodeService(NodeService nodeService)
-    {
-        this.nodeService = nodeService;
-    }
-
-    public void setFileFolderService(FileFolderService fileFolderService)
-    {
-        this.fileFolderService = fileFolderService;
-    }
-
-    public void setDictionaryService(DictionaryService dictionaryService)
-    {
-        this.dictionaryService = dictionaryService;
-    }
-
-    public void setAuthenticationUtil(AuthenticationUtil authenticationUtil)
-    {
-        this.authenticationUtil = authenticationUtil;
-    }
-
-    public void setNodes(RMNodes nodes)
-    {
-        this.nodes = nodes;
-    }
-
-    @Override
-    public Node declareFileAsRecord(String fileId, Parameters parameters)
-    {
-        // Get file to be declared
-        NodeRef fileNodeRef = nodes.validateNode(fileId) ;
-
-        // Get fileplan
-        NodeRef filePlan = authenticationUtil.runAsSystem(new RunAsWork()
-        {
-            @Override
-            public NodeRef doWork()
-            {
-                return filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
-            }
-        });
-
-        // default false (if not provided)
-        boolean hideRecord = Boolean.valueOf(parameters.getParameter(PARAM_HIDE_RECORD));
-
-        // Create the record
-        recordService.createRecord(filePlan, fileNodeRef, !hideRecord);
-
-        // Get information about the new record
-        return nodes.getFolderOrDocument(fileId, parameters);
-    }
-
-    @Override
-    public Node fileOrLinkRecord(String recordId, TargetContainer target, Parameters parameters)
-    {
-        if(StringUtils.isBlank(target.getTargetParentId()) && StringUtils.isBlank(target.getRelativePath()))
-        {
-            throw new InvalidParameterException("No target folder information was provided");
-        }
-
-        // Get record
-        NodeRef record = nodes.validateNode(recordId);
-
-        // Get record folder to file/link the record to
-        String parentContainerId = target.getTargetParentId();
-        if(parentContainerId == null || parentContainerId.isEmpty())
-        {
-            // If target container not provided get fileplan
-            parentContainerId = authenticationUtil.runAsSystem(new RunAsWork()
-            {
-                @Override
-                public String doWork()
-                {
-                    return filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID).getId();
-                }
-            });
-        }
-        NodeRef parentRecordFolder = nodes.getOrCreatePath(parentContainerId, target.getRelativePath(), ContentModel.TYPE_CONTENT);
-
-        // Check if the target is a record folder
-        if(!dictionaryService.isSubClass(nodeService.getType(parentRecordFolder), RecordsManagementModel.TYPE_RECORD_FOLDER))
-        {
-            throw new InvalidArgumentException("The provided target parent is not a record folder");
-        }
-
-        // Get the current parent type to decide if we link or move the record
-        NodeRef primaryParent = nodeService.getPrimaryParent(record).getParentRef();
-        if(dictionaryService.isSubClass(nodeService.getType(primaryParent), RecordsManagementModel.TYPE_RECORD_FOLDER))
-        {    
-            recordService.link(record, parentRecordFolder);
-        }
-        else
-        {
-            try
-            {
-                fileFolderService.moveFrom(record, primaryParent, parentRecordFolder, null);
-            }
-            catch (FileExistsException e)
-            {
-                throw new IntegrityException(e.getMessage(), null);
-            }
-            catch (FileNotFoundException e)
-            {
-                throw new ConcurrencyFailureException("The record was deleted while filing it", e);
-            }
-        }
-
-        // Get the record info
-        return nodes.getFolderOrDocument(recordId, parameters);
-    }
-
-    @Override
-    public void afterPropertiesSet() throws Exception
-    {
-        ParameterCheck.mandatory("recordService", recordService);
-        ParameterCheck.mandatory("filePlanService", filePlanService);
-        ParameterCheck.mandatory("nodes", nodes);
-        ParameterCheck.mandatory("nodeService",  nodeService);
-        ParameterCheck.mandatory("fileFolderService", fileFolderService);
-        ParameterCheck.mandatory("dictionaryService", dictionaryService);
-    }
-}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java
new file mode 100644
index 0000000000..2e103a9874
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java
@@ -0,0 +1,292 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.impl;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.rest.antlr.WhereClauseParser;
+import org.alfresco.rest.api.Nodes;
+import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rest.framework.resource.parameters.where.Query;
+import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
+import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
+import org.alfresco.rm.rest.api.model.RMNode;
+import org.alfresco.rm.rest.api.model.RecordCategoryChild;
+import org.alfresco.rm.rest.api.model.UnfiledChild;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.Pair;
+
+/**
+ * Utility class that handles common api endpoint tasks
+ *
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+public class SearchTypesFactory
+{
+    private DictionaryService dictionaryService;
+    private Nodes nodes;
+
+    public void setDictionaryService(DictionaryService dictionaryService)
+    {
+        this.dictionaryService = dictionaryService;
+    }
+
+    public void setNodes(Nodes nodes)
+    {
+        this.nodes = nodes;
+    }
+
+    public Set buildSearchTypesForFilePlanEndpoint()
+    {
+        Set searchTypeQNames = new HashSet<>();
+        searchTypeQNames.add(RecordsManagementModel.TYPE_RECORD_CATEGORY);
+        return searchTypeQNames;
+    }
+
+    /**
+     * Helper method to build search types for unfiled container and unfiled record folders endpoints
+     * @param parameters
+     * @param listFolderChildrenEqualsQueryProperties
+     * @return
+     */
+    public Set buildSearchTypesForUnfiledEndpoint(Parameters parameters, Set listFolderChildrenEqualsQueryProperties)
+    {
+        Set searchTypeQNames = new HashSet<>();
+
+        Query q = parameters.getQuery();
+
+        boolean includeUnfiledRecordFolders = false;
+        boolean includeRecords = false;
+        boolean includeSubTypes = false;
+
+        if (q != null)
+        {
+            // filtering via "where" clause
+            MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(listFolderChildrenEqualsQueryProperties, null);
+            QueryHelper.walk(q, propertyWalker);
+
+            Boolean isUnfiledRecordFolder = propertyWalker.getProperty(UnfiledChild.PARAM_IS_UNFILED_RECORD_FOLDER,
+                    WhereClauseParser.EQUALS, Boolean.class);
+            Boolean isRecord = propertyWalker.getProperty(UnfiledChild.PARAM_IS_RECORD, WhereClauseParser.EQUALS, Boolean.class);
+            if ((isUnfiledRecordFolder != null && isUnfiledRecordFolder.booleanValue()) || (isRecord != null && !isRecord.booleanValue()))
+            {
+                includeUnfiledRecordFolders = true;
+            }
+            else if ((isUnfiledRecordFolder != null && !isUnfiledRecordFolder.booleanValue()) || (isRecord != null && isRecord.booleanValue()))
+            {
+                includeRecords = true;
+            }
+
+            String nodeTypeQNameStr = propertyWalker.getProperty(UnfiledChild.PARAM_NODE_TYPE, WhereClauseParser.EQUALS, String.class);
+            QName filterNodeTypeQName;
+            if (nodeTypeQNameStr != null)
+            {
+                if ((isUnfiledRecordFolder != null) || (isRecord != null))
+                {
+                    throw new InvalidArgumentException("Invalid filter - nodeType and isUnfiledRecordFolder/isRecord are mutually exclusive");
+                }
+
+                Pair pair = parseNodeTypeFilter(nodeTypeQNameStr);
+                filterNodeTypeQName = pair.getFirst();
+                includeSubTypes = pair.getSecond();
+
+                if (nodeTypeQNameStr.equals(RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER))
+                {
+                    includeUnfiledRecordFolders = true;
+                }
+                else if (filterNodeTypeQName.equals(ContentModel.TYPE_CONTENT))
+                {
+                    includeRecords = true;
+                }
+                else if (dictionaryService.isSubClass(filterNodeTypeQName, ContentModel.TYPE_CONTENT))
+                {
+                    searchTypeQNames.add(filterNodeTypeQName);
+                    if (includeSubTypes)
+                    {
+                        Collection qnames = dictionaryService.getSubTypes(filterNodeTypeQName, true);
+                        searchTypeQNames.addAll(qnames);
+                    }
+                }
+                else
+                {
+                    throw new InvalidParameterException("Filter nodeType: " + nodeTypeQNameStr + " is invalid for this endpoint");
+                }
+            }
+        }
+        else
+        {
+            includeRecords = true;
+            includeUnfiledRecordFolders = true;
+            includeSubTypes = true;
+        }
+
+        if (includeUnfiledRecordFolders)
+        {
+            searchTypeQNames.add(RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER);
+        }
+        if (includeRecords)
+        {
+
+            if (includeSubTypes)
+            {
+                Collection qnames = dictionaryService.getSubTypes(ContentModel.TYPE_CONTENT, true);
+                searchTypeQNames.addAll(qnames);
+            }
+            else
+            {
+                searchTypeQNames.add(ContentModel.TYPE_CONTENT);
+                searchTypeQNames.add(RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT);
+            }
+        }
+        return searchTypeQNames;
+    }
+
+    /**
+     * Helper method to build search types for categories endpoint
+     * @param parameters
+     * @param listRecordCategoryChildrenEqualsQueryProperties
+     * @return
+     */
+    public Set buildSearchTypesCategoriesEndpoint(Parameters parameters, Set listRecordCategoryChildrenEqualsQueryProperties)
+    {
+        Set searchTypeQNames = new HashSet<>();
+
+        Query q = parameters.getQuery();
+
+        boolean includeRecordFolders = false;
+        boolean includeRecordCategories = false;
+
+        if (q != null)
+        {
+            // filtering via "where" clause
+            MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(listRecordCategoryChildrenEqualsQueryProperties, null);
+            QueryHelper.walk(q, propertyWalker);
+
+            Boolean isRecordFolder = propertyWalker.getProperty(RecordCategoryChild.PARAM_IS_RECORD_FOLDER,
+                    WhereClauseParser.EQUALS, Boolean.class);
+            Boolean isRecordCategory = propertyWalker.getProperty(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY, WhereClauseParser.EQUALS, Boolean.class);
+            if ((isRecordFolder != null && isRecordFolder.booleanValue()) || (isRecordCategory != null && !isRecordCategory.booleanValue()))
+            {
+                includeRecordFolders = true;
+            }
+            else if ((isRecordFolder != null && !isRecordFolder.booleanValue()) || (isRecordCategory != null && isRecordCategory.booleanValue()))
+            {
+                includeRecordCategories = true;
+            }
+
+            String nodeTypeQNameStr = propertyWalker.getProperty(RecordCategoryChild.PARAM_NODE_TYPE, WhereClauseParser.EQUALS, String.class);
+            QName filterNodeTypeQName;
+            if (nodeTypeQNameStr != null)
+            {
+                if ((isRecordFolder != null) || (isRecordCategory != null))
+                {
+                    throw new InvalidArgumentException("Invalid filter - nodeType and isRecordFolder/isRecordCategory are mutually exclusive");
+                }
+
+                Pair pair = parseNodeTypeFilter(nodeTypeQNameStr);
+                filterNodeTypeQName = pair.getFirst();
+                if (nodeTypeQNameStr.equals(RecordsManagementModel.TYPE_RECORD_FOLDER))
+                {
+                    includeRecordFolders = true;
+                }
+                else if (filterNodeTypeQName.equals(RecordsManagementModel.TYPE_RECORD_CATEGORY))
+                {
+                    includeRecordCategories = true;
+                }
+                else
+                {
+                    throw new InvalidParameterException("Filter nodeType: " + nodeTypeQNameStr + " is invalid for this endpoint");
+                }
+            }
+        }
+        else
+        {
+            includeRecordCategories = true;
+            includeRecordFolders = true;
+        }
+
+        if (includeRecordFolders)
+        {
+            searchTypeQNames.add(RecordsManagementModel.TYPE_RECORD_FOLDER);
+        }
+        if (includeRecordCategories)
+        {
+            searchTypeQNames.add(RecordsManagementModel.TYPE_RECORD_CATEGORY);
+        }
+        return searchTypeQNames;
+    }
+
+    /**
+     * Helper method to build search types for transfer containers endpoint
+     * @return
+     */
+    public Set buildSearchTypesForTransferContainersEndpoint()
+    {
+        Set searchTypeQNames = new HashSet<>();
+        searchTypeQNames.add(RecordsManagementModel.TYPE_TRANSFER);
+        return searchTypeQNames;
+    }
+
+    /**
+     * Helper method to parse the nodeType filter
+     * default nodeType filtering is without subTypes (unless nodeType value is suffixed with ' INCLUDESUBTYPES')
+     * @param nodeTypeStr
+     * @return
+     */
+    private Pair parseNodeTypeFilter(String nodeTypeStr)
+    {
+        boolean filterIncludeSubTypes = false;
+
+        int idx = nodeTypeStr.lastIndexOf(' ');
+        if (idx > 0)
+        {
+            String suffix = nodeTypeStr.substring(idx);
+            if (suffix.equalsIgnoreCase(" " + RMNode.PARAM_INCLUDE_SUBTYPES))
+            {
+                filterIncludeSubTypes = true;
+                nodeTypeStr = nodeTypeStr.substring(0, idx);
+            }
+        }
+
+        QName filterNodeTypeQName = nodes.createQName(nodeTypeStr);
+        if (dictionaryService.getType(filterNodeTypeQName) == null)
+        {
+            throw new InvalidParameterException("Filter nodeType: " + nodeTypeStr + " is invalid");
+        }
+
+        return new Pair<>(filterNodeTypeQName, filterIncludeSubTypes);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/FilePlan.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/FilePlan.java
new file mode 100644
index 0000000000..c359585cb4
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/FilePlan.java
@@ -0,0 +1,43 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.model;
+
+/**
+ * Concrete class carrying general information for an unfiled container
+ * 
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+public class FilePlan extends RMNode
+{
+    public FilePlan()
+    {
+        super();
+    }
+
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/FileplanComponentNode.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/FileplanComponentNode.java
deleted file mode 100644
index 6fd654a622..0000000000
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/FileplanComponentNode.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api.model;
-
-import java.io.Serializable;
-import java.util.Map;
-
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.api.model.UserInfo;
-import org.alfresco.service.ServiceRegistry;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.namespace.QName;
-
-/**
- * Concrete class carrying general information for a fileplan component node
- *
- * @author Ana Bozianu
- * @since 2.6
- */
-public class FileplanComponentNode extends Node
-{
-    protected Boolean isCategory;
-    protected Boolean isRecordFolder;
-
-    public FileplanComponentNode(NodeRef nodeRef, NodeRef parentNodeRef, Map nodeProps, Map mapUserInfo, ServiceRegistry sr)
-    {
-        super(nodeRef, parentNodeRef, nodeProps, mapUserInfo, sr);
-        defineType();
-    }
-
-    public FileplanComponentNode(Node node)
-    {
-        this.nodeRef = node.getNodeRef();
-        this.name = node.getName();
-        this.createdAt = node.getCreatedAt();
-        this.modifiedAt = node.getModifiedAt();
-        this.createdByUser = node.getCreatedByUser();
-        this.modifiedByUser = node.getModifiedByUser();
-        this.archivedAt = node.getArchivedAt();
-        this.archivedByUser = node.getArchivedByUser();
-        this.parentNodeRef = node.getParentId();
-        this.pathInfo = node.getPath();
-        this.prefixTypeQName = node.getNodeType();
-        this.relativePath = node.getRelativePath();
-        this.secondaryChildren = node.getSecondaryChildren();
-        this.targets = node.getTargets();
-        this.aspectNames = node.getAspectNames();
-        this.properties =node.getProperties();
-        this.allowableOperations = node.getAllowableOperations();
-        this.contentInfo = node.getContent();
-        this.description = node.getDescription();
-        defineType();
-    }
-
-    protected void defineType()
-    {
-        isCategory = false;
-        isRecordFolder = false;
-        isFile = false;
-    }
-
-    public Boolean getIsCategory()
-    {
-        return isCategory;
-    }
-
-    public Boolean getIsRecordFolder()
-    {
-        return isRecordFolder;
-    }
-
-    public void setIsCategory(Boolean isCategory)
-    {
-        this.isCategory = isCategory;
-    }
-
-    public void setIsRecordFolder(Boolean isRecordFolder)
-    {
-        this.isRecordFolder = isRecordFolder;
-    }
-}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMNode.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMNode.java
new file mode 100644
index 0000000000..d4d590e3e8
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMNode.java
@@ -0,0 +1,223 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.model;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.rest.api.model.PathInfo;
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.resource.UniqueId;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Abstract base class carrying general information for an RM node
+ *
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+public abstract class RMNode
+{
+    public static final String PARAM_ID = "id";
+    public static final String PARAM_PARENT_ID = "parentId";
+    public static final String PARAM_NAME = "name";
+    public static final String PARAM_NODE_TYPE = "nodeType";
+    public static final String PARAM_CREATED_AT = "createdAt";
+    public static final String PARAM_MODIFIED_AT = "modifiedAt";
+    public static final String PARAM_CREATED_BY_USER = "createdByUser";
+    public static final String PARAM_MODIFIED_BY_USER = "modifiedByUser";
+
+    public static final String PARAM_ASPECT_NAMES = "aspectNames";
+    public static final String PARAM_PROPERTIES = "properties";
+    public static final String PARAM_PATH = "path";
+    public static final String PARAM_ALLOWABLE_OPERATIONS = "allowableOperations";
+    
+    public static final String PARAM_ISPRIMARY = "isPrimary";
+    
+    public static final String PARAM_INCLUDE_SUBTYPES = "INCLUDESUBTYPES";
+    
+    public static final String PARAM_HAS_RETENTION_SCHEDULE = "hasRetentionSchedule";
+    public static final String PARAM_IS_CLOSED = "isClosed";
+    
+    public static final String FILE_PLAN_TYPE = "rma:filePlan";
+    public static final String RECORD_CATEGORY_TYPE = "rma:recordCategory";
+    public static final String RECORD_FOLDER_TYPE = "rma:recordFolder";
+    public static final String RECORD_TYPE = "rma:record"; // generic record type
+    public static final String UNFILED_RECORD_FOLDER_TYPE = "rma:unfiledRecordFolder";
+    public static final String TRANSFER_TYPE = "rma:transfer";
+    public static final String TRANSFER_CONTAINER_TYPE = "rma:transferContainer";
+    public static final String UNFILED_CONTAINER_TYPE = "rma:unfiledRecordContainer";
+    public static final String FOLDER_TYPE = "cm:folder";
+    public static final String CONTENT_TYPE = "cm:content";
+    public static final String NON_ELECTRONIC_RECORD_TYPE = "rma:nonElectronicDocument";
+
+    // required properties
+    protected NodeRef nodeRef;
+    protected NodeRef parentNodeRef;
+    protected String name;
+    protected String nodeType;
+
+    protected Date createdAt;
+    protected Date modifiedAt;
+    protected UserInfo createdByUser;
+    protected UserInfo modifiedByUser;
+
+    // optional properties
+    protected List aspectNames;
+    protected Map properties;
+    protected PathInfo path;
+    protected List allowableOperations;
+
+    public RMNode()
+    {
+
+    }
+
+    @UniqueId
+    public NodeRef getNodeRef()
+    {
+        return nodeRef;
+    }
+
+    public void setNodeRef(NodeRef nodeRef)
+    {
+        this.nodeRef = nodeRef;
+    }
+
+    public NodeRef getParentId()
+    {
+        return parentNodeRef;
+    }
+
+    public void setParentId(NodeRef parentNodeRef)
+    {
+        this.parentNodeRef = parentNodeRef;
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getNodeType()
+    {
+        return nodeType;
+    }
+
+    public void setNodeType(String nodeType)
+    {
+        this.nodeType = nodeType;
+    }
+
+    public Date getCreatedAt()
+    {
+        return createdAt;
+    }
+
+    public void setCreatedAt(Date createdAt)
+    {
+        this.createdAt = createdAt;
+    }
+
+    public Date getModifiedAt()
+    {
+        return modifiedAt;
+    }
+
+    public void setModifiedAt(Date modifiedAt)
+    {
+        this.modifiedAt = modifiedAt;
+    }
+
+    public UserInfo getCreatedByUser()
+    {
+        return createdByUser;
+    }
+
+    public void setCreatedByUser(UserInfo createdByUser)
+    {
+        this.createdByUser = createdByUser;
+    }
+
+    public UserInfo getModifiedByUser()
+    {
+        return modifiedByUser;
+    }
+
+    public void setModifiedByUser(UserInfo modifiedByUser)
+    {
+        this.modifiedByUser = modifiedByUser;
+    }
+
+    public List getAspectNames()
+    {
+        return aspectNames;
+    }
+
+    public void setAspectNames(List aspectNames)
+    {
+        this.aspectNames = aspectNames;
+    }
+
+    public Map getProperties()
+    {
+        return properties;
+    }
+
+    public void setProperties(Map properties)
+    {
+        this.properties = properties;
+    }
+
+    public PathInfo getPath()
+    {
+        return path;
+    }
+
+    public void setPath(PathInfo path)
+    {
+        this.path = path;
+    }
+
+    public List getAllowableOperations()
+    {
+        return allowableOperations;
+    }
+
+    public void setAllowableOperations(List allowableOperations)
+    {
+        this.allowableOperations = allowableOperations;
+    }
+
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMSite.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMSite.java
index a3ab500c0a..10bd43f46e 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMSite.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMSite.java
@@ -27,6 +27,9 @@
 
 package org.alfresco.rm.rest.api.model;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.alfresco.rest.api.model.Site;
 import org.alfresco.service.cmr.site.SiteInfo;
 
@@ -38,7 +41,9 @@ import org.alfresco.service.cmr.site.SiteInfo;
  */
 public class RMSite extends Site
 {
+    public static final String COMPLIANCE = "compliance";
     private RMSiteCompliance compliance;
+    private Map setRMFields = new HashMap<>(7);
 
     public RMSiteCompliance getCompliance()
     {
@@ -48,6 +53,18 @@ public class RMSite extends Site
     public void setCompliance(RMSiteCompliance compliance)
     {
         this.compliance = compliance;
+        setRMFields.put(COMPLIANCE, true);
+    }
+
+    @Override
+    public boolean wasSet(String fieldName)
+    {
+        if(COMPLIANCE.equalsIgnoreCase(fieldName))
+        {
+            Boolean b = setRMFields.get(fieldName);
+            return (b != null ? b : false);
+        }
+        return super.wasSet(fieldName);
     }
 
     public RMSite()
@@ -57,19 +74,19 @@ public class RMSite extends Site
 
     public RMSite(Site site, RMSiteCompliance compliance)
     {
-        this.id = site.getId();
-        this.guid = site.getGuid();
-        this.title = site.getTitle();
-        this.description = site.getDescription();
-        this.visibility = site.getVisibility();
-        this.role = site.getRole();
-        this.compliance = compliance;
+        setId(site.getId());
+        setGuid(site.getGuid());
+        setTitle(site.getTitle());
+        setDescription(site.getDescription());
+        setVisibility(site.getVisibility());
+        setRole(site.getRole());
+        setCompliance(compliance);
     }
 
     public RMSite(SiteInfo siteInfo, String role, RMSiteCompliance compliance)
     {
         super(siteInfo, role);
-        this.compliance = compliance;
+        setCompliance(compliance);
     }
 
     @Override
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordNode.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/Record.java
similarity index 61%
rename from rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordNode.java
rename to rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/Record.java
index d8a95e7036..01e20d685d 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordNode.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/Record.java
@@ -24,45 +24,24 @@
  * along with Alfresco. If not, see .
  * #L%
  */
-
 package org.alfresco.rm.rest.api.model;
 
-import java.io.Serializable;
-import java.util.Map;
-
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.api.model.UserInfo;
-import org.alfresco.service.ServiceRegistry;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.namespace.QName;
+import org.alfresco.rest.api.model.ContentInfo;
 
 /**
- * Concrete class carrying specific information for a record
+ * Concrete class carrying information for a record
  *
  * @author Ana Bozianu
  * @since 2.6
  */
-public class RecordNode extends FileplanComponentNode
+public class Record extends RMNode
 {
-    private Boolean isCompleted;
+    public static final String PARAM_HIDE_RECORD = "hideRecord";
+    public static final String PARAM_IS_COMPLETED = "isCompleted";
+    public static final String PARAM_CONTENT = "content";
 
-    public RecordNode(NodeRef nodeRef, NodeRef parentNodeRef, Map nodeProps, Map mapUserInfo, ServiceRegistry sr)
-    {
-        super(nodeRef, parentNodeRef, nodeProps, mapUserInfo, sr);
-    }
-
-    public RecordNode(Node node)
-    {
-        super(node);
-    }
-
-    @Override
-    protected void defineType()
-    {
-        setIsFile(true);
-        setIsCategory(false);
-        setIsRecordFolder(false);
-    }
+    protected Boolean isCompleted;
+    protected ContentInfo content;
 
     public Boolean getIsCompleted()
     {
@@ -73,4 +52,14 @@ public class RecordNode extends FileplanComponentNode
     {
         this.isCompleted = isCompleted;
     }
+
+    public ContentInfo getContent()
+    {
+        return content;
+    }
+
+    public void setContent(ContentInfo content)
+    {
+        this.content = content;
+    }
 }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordCategory.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordCategory.java
new file mode 100644
index 0000000000..2787d9767c
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordCategory.java
@@ -0,0 +1,56 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.model;
+
+/**
+ * Concrete class carrying general information for an record category
+ * 
+ * @author Ramona Popa
+ * @since 2.6
+ */
+public class RecordCategory extends RMNode
+{
+
+    protected Boolean hasRetentionSchedule;
+
+    public RecordCategory()
+    {
+        super();
+    }
+
+    public Boolean getHasRetentionSchedule()
+    {
+        return hasRetentionSchedule;
+    }
+
+    public void setHasRetentionSchedule(Boolean hasRetentionSchedule)
+    {
+        this.hasRetentionSchedule = hasRetentionSchedule;
+    }
+
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordCategoryNode.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordCategoryChild.java
similarity index 50%
rename from rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordCategoryNode.java
rename to rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordCategoryChild.java
index 23500db262..b72318599e 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordCategoryNode.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordCategoryChild.java
@@ -27,41 +27,47 @@
 
 package org.alfresco.rm.rest.api.model;
 
-import java.io.Serializable;
-import java.util.Map;
-
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.api.model.UserInfo;
-import org.alfresco.service.ServiceRegistry;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.namespace.QName;
-
 /**
- * Concrete class carrying specific information for a category
+ * Concrete class carrying information for a record category child.
  *
- * @author Ana Bozianu
+ * @author Silviu Dinuta
  * @since 2.6
  */
-public class RecordCategoryNode extends FileplanComponentNode
+public class RecordCategoryChild extends RMNode
 {
-    protected Boolean hasRetentionSchedule;
+    public static final String PARAM_IS_RECORD_FOLDER = "isRecordFolder";
+    public static final String PARAM_IS_RECORD_CATEGORY = "isRecordCategory";
+    public static final String PARAM_IS_CLOSED = "isClosed";
+    public static final String PARAM_HAS_RETENTION_SCHEDULE = "hasRetentionSchedule";
 
-    public RecordCategoryNode(NodeRef nodeRef, NodeRef parentNodeRef, Map nodeProps, Map mapUserInfo, ServiceRegistry sr)
+    private Boolean isRecordCategory;
+    private Boolean isRecordFolder;
+    private Boolean hasRetentionSchedule;
+    private String relativePath;
+    private Boolean isClosed;
+
+    public RecordCategoryChild()
     {
-        super(nodeRef, parentNodeRef, nodeProps, mapUserInfo, sr);
     }
 
-    public RecordCategoryNode(Node node)
+    public Boolean getIsRecordCategory()
     {
-        super(node);
+        return isRecordCategory;
     }
 
-    @Override
-    protected void defineType()
+    public void setIsRecordCategory(Boolean isRecordCategory)
     {
-        setIsCategory(true);
-        setIsRecordFolder(false);
-        setIsFile(false);
+        this.isRecordCategory = isRecordCategory;
+    }
+
+    public Boolean getIsRecordFolder()
+    {
+        return isRecordFolder;
+    }
+
+    public void setIsRecordFolder(Boolean isRecordFolder)
+    {
+        this.isRecordFolder = isRecordFolder;
     }
 
     public Boolean getHasRetentionSchedule()
@@ -73,4 +79,24 @@ public class RecordCategoryNode extends FileplanComponentNode
     {
         this.hasRetentionSchedule = hasRetentionSchedule;
     }
+
+    public String getRelativePath()
+    {
+        return relativePath;
+    }
+
+    public void setRelativePath(String relativePath)
+    {
+        this.relativePath = relativePath;
+    }
+
+    public Boolean getIsClosed()
+    {
+        return isClosed;
+    }
+
+    public void setIsClosed(Boolean isClosed)
+    {
+        this.isClosed = isClosed;
+    }
 }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordFolder.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordFolder.java
new file mode 100644
index 0000000000..0cfe928eb6
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordFolder.java
@@ -0,0 +1,57 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.model;
+
+/**
+ * Concrete class carrying general information for a record folder
+ * 
+ * @author Ramona Popa
+ * @since 2.6
+ */
+public class RecordFolder extends RMNode
+{
+    public static final String PARAM_IS_CLOSED = "isClosed";
+    
+    private Boolean isClosed;
+
+    public RecordFolder()
+    {
+        super();
+    }
+
+    public Boolean getIsClosed()
+    {
+        return isClosed;
+    }
+
+    public void setIsClosed(Boolean isClosed)
+    {
+        this.isClosed = isClosed;
+    }
+
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/SiteUpdate.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/SiteUpdate.java
deleted file mode 100644
index 0e9bcfc07d..0000000000
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/SiteUpdate.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api.model;
-
-import org.alfresco.service.cmr.site.SiteVisibility;
-
-import java.io.Serializable;
-
-/**
- * TODO Just copied this from latest core version, to be removed when we'll upgrade from 5.2.a-EA
- *
- * Class representing a site update API operation.
- *
- * @author Matt Ward
- * @since 5.2
- */
-public class SiteUpdate implements Serializable
-{
-    private static final long serialVersionUID = 1L;
-    private String title;
-    private String description;
-    private SiteVisibility visibility;
-
-    public SiteUpdate(String title, String description, SiteVisibility visibility)
-    {
-        this.title = title;
-        this.description = description;
-        this.visibility = visibility;
-    }
-
-    public String getTitle()
-    {
-        return title;
-    }
-
-    public void setTitle(String title)
-    {
-        this.title = title;
-    }
-
-    public String getDescription()
-    {
-        return description;
-    }
-
-    public void setDescription(String description)
-    {
-        this.description = description;
-    }
-
-    public SiteVisibility getVisibility()
-    {
-        return visibility;
-    }
-
-    public void setVisibility(SiteVisibility visibility)
-    {
-        this.visibility = visibility;
-    }
-
-    @Override
-    public boolean equals(Object o)
-    {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        SiteUpdate that = (SiteUpdate) o;
-
-        if (title != null ? !title.equals(that.title) : that.title != null) return false;
-        if (description != null ? !description.equals(that.description) : that.description != null) return false;
-        return visibility == that.visibility;
-    }
-
-    @Override
-    public int hashCode()
-    {
-        int result = title != null ? title.hashCode() : 0;
-        result = 31 * result + (description != null ? description.hashCode() : 0);
-        result = 31 * result + (visibility != null ? visibility.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString()
-    {
-        return "SiteUpdate{" +
-                "title='" + title + '\'' +
-                ", description='" + description + '\'' +
-                ", visibility=" + visibility +
-                '}';
-    }
-}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TargetContainer.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TargetContainer.java
index b5cea579f0..b0025aeef5 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TargetContainer.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TargetContainer.java
@@ -36,7 +36,6 @@ package org.alfresco.rm.rest.api.model;
 public class TargetContainer
 {
     String targetParentId;
-    String relativePath;
 
     public TargetContainer()
     {
@@ -52,22 +51,11 @@ public class TargetContainer
         this.targetParentId = targetParentId;
     }
 
-    public String getRelativePath()
-    {
-        return relativePath;
-    }
-
-    public void setRelativePath(String relativePath)
-    {
-        this.relativePath = relativePath;
-    }
-
     @Override
     public String toString()
     {
-        final StringBuilder sb = new StringBuilder("NodeTarget{");
+        final StringBuilder sb = new StringBuilder("TargetContainer{");
         sb.append("targetParentId=").append(targetParentId);
-        sb.append(", relativePath='").append(relativePath).append('\'');
         sb.append('}');
         return sb.toString();
     }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/Transfer.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/Transfer.java
new file mode 100644
index 0000000000..b07f32d799
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/Transfer.java
@@ -0,0 +1,190 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.model;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.resource.UniqueId;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * POJO object carrying information of a transfer node
+ * 
+ * @author Silviu Dinuta
+ * @since 2.6
+ *
+ */
+public class Transfer
+{
+    public static final String PARAM_TRANSFER_ACCESSION_INDICATOR = "transferAccessionIndicator";
+    public static final String PARAM_TRANSFER_LOCATION = "transferLocation";
+    public static final String PARAM_TRANSFER_PDF_INDICATOR = "transferPDFIndicator";
+
+    protected NodeRef nodeRef;
+    protected NodeRef parentNodeRef;
+    protected String name;
+    protected String nodeType;
+
+    protected Date createdAt;
+    protected UserInfo createdByUser;
+    // optional properties
+    protected List aspectNames;
+    protected Map properties;
+    protected List allowableOperations;
+    private Boolean transferPDFIndicator;
+    private String transferLocation;
+    private Boolean transferAccessionIndicator;
+
+    public Transfer()
+    {
+    }
+
+    @UniqueId
+    public NodeRef getNodeRef()
+    {
+        return nodeRef;
+    }
+
+    public void setNodeRef(NodeRef nodeRef)
+    {
+        this.nodeRef = nodeRef;
+    }
+
+    public NodeRef getParentId()
+    {
+        return parentNodeRef;
+    }
+
+    public void setParentId(NodeRef parentNodeRef)
+    {
+        this.parentNodeRef = parentNodeRef;
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getNodeType()
+    {
+        return nodeType;
+    }
+
+    public void setNodeType(String nodeType)
+    {
+        this.nodeType = nodeType;
+    }
+
+    public Date getCreatedAt()
+    {
+        return createdAt;
+    }
+
+    public void setCreatedAt(Date createdAt)
+    {
+        this.createdAt = createdAt;
+    }
+
+    public UserInfo getCreatedByUser()
+    {
+        return createdByUser;
+    }
+
+    public void setCreatedByUser(UserInfo createdByUser)
+    {
+        this.createdByUser = createdByUser;
+    }
+
+    public List getAspectNames()
+    {
+        return aspectNames;
+    }
+
+    public void setAspectNames(List aspectNames)
+    {
+        this.aspectNames = aspectNames;
+    }
+
+    public Map getProperties()
+    {
+        return properties;
+    }
+
+    public void setProperties(Map properties)
+    {
+        this.properties = properties;
+    }
+
+    public List getAllowableOperations()
+    {
+        return allowableOperations;
+    }
+
+    public void setAllowableOperations(List allowableOperations)
+    {
+        this.allowableOperations = allowableOperations;
+    }
+
+    public Boolean getTransferPDFIndicator()
+    {
+        return transferPDFIndicator;
+    }
+
+    public void setTransferPDFIndicator(Boolean transferPDFIndicator)
+    {
+        this.transferPDFIndicator = transferPDFIndicator;
+    }
+
+    public String getTransferLocation()
+    {
+        return transferLocation;
+    }
+
+    public void setTransferLocation(String transferLocation)
+    {
+        this.transferLocation = transferLocation;
+    }
+
+    public Boolean getTransferAccessionIndicator()
+    {
+        return transferAccessionIndicator;
+    }
+
+    public void setTransferAccessionIndicator(Boolean transferAccessionIndicator)
+    {
+        this.transferAccessionIndicator = transferAccessionIndicator;
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordFolderNode.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TransferChild.java
similarity index 56%
rename from rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordFolderNode.java
rename to rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TransferChild.java
index 7247c17754..1defbb7e84 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RecordFolderNode.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TransferChild.java
@@ -27,41 +27,35 @@
 
 package org.alfresco.rm.rest.api.model;
 
-import java.io.Serializable;
-import java.util.Map;
-
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.api.model.UserInfo;
-import org.alfresco.service.ServiceRegistry;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.namespace.QName;
-
 /**
- * Concrete class carrying specific information for a record folder
- *
- * @author Ana Bozianu
+ * POJO object carrying information of a transfer child
+ * 
+ * @author Silviu Dinuta
  * @since 2.6
  */
-public class RecordFolderNode extends FileplanComponentNode
+public class TransferChild extends RMNode
 {
-    private Boolean isClosed;
+    public static final String PARAM_IS_COMPLETED = "isCompleted";
+    public static final String PARAM_IS_RECORD_FOLDER = "isRecordFolder";
+    public static final String PARAM_IS_RECORD = "isRecord";
 
-    public RecordFolderNode(NodeRef nodeRef, NodeRef parentNodeRef, Map nodeProps, Map mapUserInfo, ServiceRegistry sr)
+    protected Boolean isCompleted;
+    protected Boolean isClosed;
+    protected Boolean isRecordFolder;
+    protected Boolean isRecord;
+
+    public TransferChild()
     {
-        super(nodeRef, parentNodeRef, nodeProps, mapUserInfo, sr);
     }
 
-    public RecordFolderNode(Node node)
+    public Boolean getIsCompleted()
     {
-        super(node);
+        return isCompleted;
     }
 
-    @Override
-    protected void defineType()
+    public void setIsCompleted(Boolean isCompleted)
     {
-        setIsRecordFolder(true);
-        setIsCategory(false);
-        setIsFile(false);
+        this.isCompleted = isCompleted;
     }
 
     public Boolean getIsClosed()
@@ -73,4 +67,24 @@ public class RecordFolderNode extends FileplanComponentNode
     {
         this.isClosed = isClosed;
     }
+
+    public Boolean getIsRecordFolder()
+    {
+        return isRecordFolder;
+    }
+
+    public void setIsRecordFolder(Boolean isRecordFolder)
+    {
+        this.isRecordFolder = isRecordFolder;
+    }
+
+    public Boolean getIsRecord()
+    {
+        return isRecord;
+    }
+
+    public void setIsRecord(Boolean isRecord)
+    {
+        this.isRecord = isRecord;
+    }
 }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TransferContainer.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TransferContainer.java
new file mode 100644
index 0000000000..742973c091
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/TransferContainer.java
@@ -0,0 +1,173 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.model;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.resource.UniqueId;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ *
+ * @author Silviu Dinuta
+ * @since 2.6
+ */
+public class TransferContainer
+{
+    protected NodeRef nodeRef;
+    protected NodeRef parentNodeRef;
+    protected String name;
+    protected String nodeType;
+
+    protected Date createdAt;
+    protected UserInfo createdByUser;
+    protected Date modifiedAt;
+    protected UserInfo modifiedByUser;
+    // optional properties
+    protected List aspectNames;
+    protected Map properties;
+    protected List allowableOperations;
+
+    public TransferContainer()
+    {
+    }
+
+    @UniqueId
+    public NodeRef getNodeRef()
+    {
+        return nodeRef;
+    }
+
+    public void setNodeRef(NodeRef nodeRef)
+    {
+        this.nodeRef = nodeRef;
+    }
+
+    public NodeRef getParentId()
+    {
+        return parentNodeRef;
+    }
+
+    public void setParentId(NodeRef parentNodeRef)
+    {
+        this.parentNodeRef = parentNodeRef;
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getNodeType()
+    {
+        return nodeType;
+    }
+
+    public void setNodeType(String nodeType)
+    {
+        this.nodeType = nodeType;
+    }
+
+    public Date getCreatedAt()
+    {
+        return createdAt;
+    }
+
+    public void setCreatedAt(Date createdAt)
+    {
+        this.createdAt = createdAt;
+    }
+
+    public Date getModifiedAt()
+    {
+        return modifiedAt;
+    }
+
+    public void setModifiedAt(Date modifiedAt)
+    {
+        this.modifiedAt = modifiedAt;
+    }
+
+    public UserInfo getCreatedByUser()
+    {
+        return createdByUser;
+    }
+
+    public void setCreatedByUser(UserInfo createdByUser)
+    {
+        this.createdByUser = createdByUser;
+    }
+
+    public UserInfo getModifiedByUser()
+    {
+        return modifiedByUser;
+    }
+
+    public void setModifiedByUser(UserInfo modifiedByUser)
+    {
+        this.modifiedByUser = modifiedByUser;
+    }
+
+    public List getAspectNames()
+    {
+        return aspectNames;
+    }
+
+    public void setAspectNames(List aspectNames)
+    {
+        this.aspectNames = aspectNames;
+    }
+
+    public Map getProperties()
+    {
+        return properties;
+    }
+
+    public void setProperties(Map properties)
+    {
+        this.properties = properties;
+    }
+
+    public List getAllowableOperations()
+    {
+        return allowableOperations;
+    }
+
+    public void setAllowableOperations(List allowableOperations)
+    {
+        this.allowableOperations = allowableOperations;
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledChild.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledChild.java
new file mode 100644
index 0000000000..b86922abca
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledChild.java
@@ -0,0 +1,69 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * Abstract class carrying information for an unfiled container or unfiled record folder child
+ *
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+public abstract class UnfiledChild extends RMNode
+{
+    public static final String PARAM_IS_UNFILED_RECORD_FOLDER = "isUnfiledRecordFolder";
+    public static final String PARAM_IS_RECORD = "isRecord";
+
+    protected Boolean isUnfiledRecordFolder;
+    protected Boolean isRecord;
+
+    public Boolean getIsUnfiledRecordFolder()
+    {
+        return isUnfiledRecordFolder;
+    }
+
+    @JsonIgnore
+    public void setIsUnfiledRecordFolder(Boolean isUnfiledRecordFolder)
+    {
+        this.isUnfiledRecordFolder = isUnfiledRecordFolder;
+    }
+
+    public Boolean getIsRecord()
+    {
+        return isRecord;
+    }
+
+    @JsonIgnore
+    public void setIsRecord(Boolean isRecord)
+    {
+        this.isRecord = isRecord;
+    }
+
+
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledContainer.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledContainer.java
new file mode 100644
index 0000000000..cd7e0893a1
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledContainer.java
@@ -0,0 +1,43 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.model;
+
+/**
+ * Concrete class carrying general information for an unfiled container
+ * 
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+public class UnfiledContainer extends RMNode
+{
+    public UnfiledContainer()
+    {
+        super();
+    }
+
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledContainerChild.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledContainerChild.java
new file mode 100644
index 0000000000..8b28a4ad6d
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledContainerChild.java
@@ -0,0 +1,37 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rm.rest.api.model;
+
+/**
+ * Specific POJO object carrying information for an unfiled container child
+ *
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+public class UnfiledContainerChild extends UnfiledChild
+{
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledRecordFolder.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledRecordFolder.java
new file mode 100644
index 0000000000..5ab1968ae7
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledRecordFolder.java
@@ -0,0 +1,43 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.model;
+
+/**
+ * Concrete class carrying specific information for an unfiled record folder
+ * 
+ * @author Ramona Popa
+ * @since 2.6
+ */
+public class UnfiledRecordFolder extends RMNode
+{
+    public UnfiledRecordFolder()
+    {
+        super();
+    }
+
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledRecordFolderChild.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledRecordFolderChild.java
new file mode 100644
index 0000000000..04f736b07c
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UnfiledRecordFolderChild.java
@@ -0,0 +1,53 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rm.rest.api.model;
+
+/**
+ * Specific POJO object carrying information for an unfiled record folder child
+ *
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+public class UnfiledRecordFolderChild extends UnfiledChild
+{
+    protected String relativePath;
+
+    public UnfiledRecordFolderChild()
+    {
+        super();
+    }
+
+    public void setRelativePath(String relativePath)
+    {
+        this.relativePath = relativePath;
+    }
+
+    public String getRelativePath()
+    {
+        return relativePath;
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UploadInfo.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UploadInfo.java
new file mode 100644
index 0000000000..bca3e81c29
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/UploadInfo.java
@@ -0,0 +1,124 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rm.rest.api.model;
+
+import java.util.Map;
+
+import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
+import org.alfresco.service.namespace.QName;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.extensions.surf.util.Content;
+import org.springframework.extensions.webscripts.servlet.FormData;
+
+/**
+ * Encapsulates the elements of an upload request
+ * 
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+public class UploadInfo
+{
+    private String fileName;
+    private String nodeType;
+    private String relativePath;
+    private Content content;
+    private Map properties;
+
+    public UploadInfo(FormData formData)
+    {
+        for (FormData.FormField field : formData.getFields())
+        {
+            switch (field.getName().toLowerCase())
+            {
+                case "name":
+                    fileName = getStringOrNull(field.getValue());
+                    break;
+                case "nodetype":
+                    nodeType = getStringOrNull(field.getValue());
+                    break;
+                case "relativepath":
+                    relativePath = getStringOrNull(field.getValue());
+                    break;
+                case "filedata":
+                    if (field.getIsFile())
+                    {
+                        fileName = (fileName != null ? fileName : field.getFilename());
+                        content = field.getContent();
+                    }
+                    break;
+
+                default:
+                {
+                    final String propName = field.getName();
+                    if (propName.indexOf(QName.NAMESPACE_PREFIX) > -1)
+                    {
+                        properties.put(propName, field.getValue());
+                    }
+                }
+            }
+        }
+
+        if (StringUtils.isBlank(fileName) || content == null)
+        {
+            throw new InvalidArgumentException("Required parameters are missing");
+        }
+    }
+
+    private String getStringOrNull(String value)
+    {
+        if (StringUtils.isNotEmpty(value))
+        {
+            return value.equalsIgnoreCase("null") ? null : value;
+        }
+        return null;
+    }
+
+    public String getFileName()
+    {
+        return fileName;
+    }
+
+    public String getNodeType()
+    {
+        return nodeType;
+    }
+
+    public String getRelativePath()
+    {
+        return relativePath;
+    }
+
+    public Content getContent()
+    {
+        return content;
+    }
+
+    public Map getProperties()
+    {
+        return properties;
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoriesEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoriesEntityResource.java
new file mode 100644
index 0000000000..4b82ed9750
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoriesEntityResource.java
@@ -0,0 +1,131 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.recordcategories;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.rest.api.Nodes;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.resource.EntityResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.model.RecordCategory;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * Record category entity resource
+ *
+ * @author Ana Bozianu
+ * @author Tuna Aksoy
+ * @since 2.6
+ */
+@EntityResource(name="record-categories", title = "Record Categories")
+public class RecordCategoriesEntityResource implements
+        EntityResourceAction.ReadById,
+        EntityResourceAction.Delete,
+        EntityResourceAction.Update,
+        InitializingBean
+{
+    private FilePlanComponentsApiUtils apiUtils;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception
+    {
+        mandatory("apiUtils", apiUtils);
+        mandatory("fileFolderService", fileFolderService);
+        mandatory("apiNodesModelFactory", nodesModelFactory);
+    }
+
+    @WebApiDescription(title = "Get record category information", description = "Gets information for a record category with id 'recordCategoryId'")
+    @WebApiParam(name = "recordCategoryId", title = "The record category id")
+    public RecordCategory readById(String recordCategoryId, Parameters parameters)
+    {
+        checkNotBlank("recordCategoryId", recordCategoryId);
+        mandatory("parameters", parameters);
+
+        String relativePath = parameters.getParameter(Nodes.PARAM_RELATIVE_PATH);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(recordCategoryId, RecordsManagementModel.TYPE_RECORD_CATEGORY, relativePath, true);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+
+        return nodesModelFactory.createRecordCategory(info, parameters, null, false);
+    }
+
+    @Override
+    @WebApiDescription(title="Update record category", description = "Updates a record category with id 'recordCategoryId'")
+    public RecordCategory update(String recordCategoryId, RecordCategory recordCategoryInfo, Parameters parameters)
+    {
+        checkNotBlank("recordCategoryId", recordCategoryId);
+        mandatory("recordCategoryInfo", recordCategoryInfo);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(recordCategoryId, RecordsManagementModel.TYPE_RECORD_CATEGORY);
+        apiUtils.updateNode(nodeRef, recordCategoryInfo, parameters);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+        return nodesModelFactory.createRecordCategory(info, parameters, null, false);
+    }
+
+    @Override
+    @WebApiDescription(title = "Delete record category", description="Deletes a record category with id 'recordCategoryId'")
+    public void delete(String recordCategoryId, Parameters parameters)
+    {
+        checkNotBlank("recordCategoryId", recordCategoryId);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(recordCategoryId, RecordsManagementModel.TYPE_RECORD_CATEGORY);
+
+        fileFolderService.delete(nodeRef);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoryChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoryChildrenRelation.java
new file mode 100644
index 0000000000..7863035e60
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoryChildrenRelation.java
@@ -0,0 +1,184 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.recordcategories;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.query.PagingResults;
+import org.alfresco.repo.node.getchildren.FilterProp;
+import org.alfresco.rest.api.Nodes;
+import org.alfresco.rest.api.impl.Util;
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.resource.RelationshipResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
+import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.impl.SearchTypesFactory;
+import org.alfresco.rm.rest.api.model.RMNode;
+import org.alfresco.rm.rest.api.model.RecordCategory;
+import org.alfresco.rm.rest.api.model.RecordCategoryChild;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Record category children relation
+ *
+ * @author Ana Bozianu
+ * @author Tuna Aksoy
+ * @since 2.6
+ */
+@RelationshipResource(name="children", entityResource = RecordCategoriesEntityResource.class, title = "Children of a record category")
+public class RecordCategoryChildrenRelation implements RelationshipResourceAction.Read,
+                                                 RelationshipResourceAction.Create
+{
+    private final static Set LIST_RECORD_CATEGORY_CHILDREN_EQUALS_QUERY_PROPERTIES = new HashSet<>(Arrays
+            .asList(new String[] { RecordCategoryChild.PARAM_IS_RECORD_CATEGORY, RecordCategoryChild.PARAM_IS_RECORD_FOLDER,
+                                   RecordCategoryChild.PARAM_IS_CLOSED, RecordCategoryChild.PARAM_HAS_RETENTION_SCHEDULE, RMNode.PARAM_NODE_TYPE }));
+
+    private FilePlanComponentsApiUtils apiUtils;
+    private SearchTypesFactory searchTypesFactory;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setSearchTypesFactory(SearchTypesFactory searchTypesFactory)
+    {
+        this.searchTypesFactory = searchTypesFactory;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    @Override
+    @WebApiDescription(title = "Return a paged list of record category children for the container identified by 'recordCategoryId'")
+    public CollectionWithPagingInfo readAll(String recordCategoryId, Parameters parameters)
+    {
+        checkNotBlank("recordCategoryId", recordCategoryId);
+        mandatory("parameters", parameters);
+
+        String relativePath = parameters.getParameter(Nodes.PARAM_RELATIVE_PATH);
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(recordCategoryId, RecordsManagementModel.TYPE_RECORD_CATEGORY, relativePath, true);
+
+        // list record categories and record folders
+        Set searchTypeQNames = searchTypesFactory.buildSearchTypesCategoriesEndpoint(parameters, LIST_RECORD_CATEGORY_CHILDREN_EQUALS_QUERY_PROPERTIES);
+        List filterProps = apiUtils.getListChildrenFilterProps(parameters, LIST_RECORD_CATEGORY_CHILDREN_EQUALS_QUERY_PROPERTIES);
+
+        final PagingResults pagingResults = fileFolderService.list(parentNodeRef,
+                null,
+                searchTypeQNames,
+                null,
+                apiUtils.getSortProperties(parameters),
+                filterProps,
+                Util.getPagingRequest(parameters.getPaging()));
+
+        final List page = pagingResults.getPage();
+        Map mapUserInfo = new HashMap<>();
+        List nodes = new AbstractList()
+        {
+            @Override
+            public RecordCategoryChild get(int index)
+            {
+                FileInfo info = page.get(index);
+                return nodesModelFactory.createRecordCategoryChild(info, parameters, mapUserInfo, true);
+            }
+
+            @Override
+            public int size()
+            {
+                return page.size();
+            }
+        };
+
+        RecordCategory sourceEntity = null;
+        if (parameters.includeSource())
+        {
+            FileInfo info = fileFolderService.getFileInfo(parentNodeRef);
+            sourceEntity = nodesModelFactory.createRecordCategory(info, parameters, mapUserInfo, true);
+        }
+
+        return CollectionWithPagingInfo.asPaged(parameters.getPaging(), nodes, pagingResults.hasMoreItems(), pagingResults.getTotalResultCount().getFirst(), sourceEntity);
+    }
+
+    @Override
+    @WebApiDescription(title="Create one (or more) nodes as children of a record category identified by 'recordCategoryId'")
+    public List create(String recordCategoryId, List nodeInfos, Parameters parameters)
+    {
+        checkNotBlank("recordCategoryId", recordCategoryId);
+        mandatory("nodeInfos", nodeInfos);
+        mandatory("parameters", parameters);
+
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(recordCategoryId, RecordsManagementModel.TYPE_RECORD_CATEGORY);
+
+        List result = new ArrayList<>(nodeInfos.size());
+        Map mapUserInfo = new HashMap<>();
+        for (RecordCategoryChild nodeInfo : nodeInfos)
+        {
+            // Resolve the parent node
+            NodeRef nodeParent = parentNodeRef;
+            if(StringUtils.isNoneBlank(nodeInfo.getRelativePath()))
+            {
+                nodeParent = apiUtils.lookupAndValidateRelativePath(parentNodeRef, nodeInfo.getRelativePath(), RecordsManagementModel.TYPE_RECORD_CATEGORY);
+            }
+
+            // Create the node
+            NodeRef newNode = apiUtils.createRMNode(nodeParent, nodeInfo.getName(), nodeInfo.getNodeType(), nodeInfo.getProperties(), nodeInfo.getAspectNames());
+            FileInfo info = fileFolderService.getFileInfo(newNode);
+            result.add(nodesModelFactory.createRecordCategoryChild(info, parameters, mapUserInfo, false));
+        }
+
+        return result;
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/package-info.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/package-info.java
similarity index 85%
rename from rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/package-info.java
rename to rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/package-info.java
index f4ef3b5150..7a92fbf3ea 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplancomponents/package-info.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/package-info.java
@@ -26,12 +26,12 @@
  */
 
 /**
- * Package info that defines the Information Governance Fileplan Components REST API
- * 
+ * Package info that defines the Information Governance Record Categories REST API
+ *
  * @author Ana Bozianu
  * @since 2.6
  */
-@WebApi(name="ig", scope=Api.SCOPE.PUBLIC, version=1)
-package org.alfresco.rm.rest.api.fileplancomponents;
+@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
+package org.alfresco.rm.rest.api.recordcategories;
 import org.alfresco.rest.framework.Api;
 import org.alfresco.rest.framework.WebApi;
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderChildrenRelation.java
new file mode 100644
index 0000000000..9ba8c695ba
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderChildrenRelation.java
@@ -0,0 +1,223 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.recordfolders;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.query.PagingResults;
+import org.alfresco.repo.activities.ActivityType;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.rest.api.impl.Util;
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.resource.RelationshipResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction;
+import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
+import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rest.framework.webscripts.WithResponse;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.impl.SearchTypesFactory;
+import org.alfresco.rm.rest.api.model.Record;
+import org.alfresco.rm.rest.api.model.RecordFolder;
+import org.alfresco.rm.rest.api.model.UnfiledContainerChild;
+import org.alfresco.rm.rest.api.model.UploadInfo;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.springframework.extensions.webscripts.servlet.FormData;
+
+/**
+ * Record folder children relation
+ *
+ * @author Ramona Popa
+ * @since 2.6
+ */
+@RelationshipResource(name = "records", entityResource = RecordFolderEntityResource.class, title = "Children of a record folder")
+public class RecordFolderChildrenRelation implements RelationshipResourceAction.Read, RelationshipResourceAction.Create,
+        MultiPartRelationshipResourceAction.Create
+{
+    private FilePlanComponentsApiUtils apiUtils;
+    private SearchTypesFactory searchTypesFactory;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+    private TransactionService transactionService;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setSearchTypesFactory(SearchTypesFactory searchTypesFactory)
+    {
+        this.searchTypesFactory = searchTypesFactory;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    public void setTransactionService(TransactionService transactionService)
+    {
+        this.transactionService = transactionService;
+    }
+
+    @Override
+    @WebApiDescription(title = "Return a paged list of records for the record folder identified by 'recordFolderId'")
+    public CollectionWithPagingInfo readAll(String recordFolderId, Parameters parameters)
+    {
+        checkNotBlank("recordFolderId", recordFolderId);
+        mandatory("parameters", parameters);
+
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(recordFolderId, RecordsManagementModel.TYPE_RECORD_FOLDER);
+
+        // list record folders
+        // FIXME searchParam
+        Set searchTypeQNames = searchTypesFactory.buildSearchTypesForUnfiledEndpoint(parameters, null);
+
+        final PagingResults pagingResults = fileFolderService.list(parentNodeRef, null, searchTypeQNames, null,
+                apiUtils.getSortProperties(parameters), null, Util.getPagingRequest(parameters.getPaging()));
+
+        final List page = pagingResults.getPage();
+        Map mapUserInfo = new HashMap<>();
+        List nodes = new AbstractList()
+        {
+            @Override
+            public Record get(int index)
+            {
+                FileInfo info = page.get(index);
+                return nodesModelFactory.createRecord(info, parameters, mapUserInfo, true);
+            }
+
+            @Override
+            public int size()
+            {
+                return page.size();
+            }
+        };
+
+        RecordFolder sourceEntity = null;
+        if (parameters.includeSource())
+        {
+            FileInfo info = fileFolderService.getFileInfo(parentNodeRef);
+            sourceEntity = nodesModelFactory.createRecordFolder(info, parameters, mapUserInfo, true);
+        }
+
+        return CollectionWithPagingInfo.asPaged(parameters.getPaging(), nodes, pagingResults.hasMoreItems(),
+                pagingResults.getTotalResultCount().getFirst(), sourceEntity);
+    }
+
+    @Override
+    @WebApiDescription(title = "Create one (or more) records as children of a record folder identified by 'recordFolderId'")
+    public List create(String recordFolderId, List nodeInfos, Parameters parameters)
+    {
+        checkNotBlank("recordFolderId", recordFolderId);
+        mandatory("nodeInfos", nodeInfos);
+        mandatory("parameters", parameters);
+
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(recordFolderId, RecordsManagementModel.TYPE_RECORD_FOLDER);
+
+        RetryingTransactionCallback> callback = new RetryingTransactionCallback>()
+        {
+            public List execute()
+            {
+                List createdNodes = new LinkedList<>();
+                for (Record nodeInfo : nodeInfos)
+                {
+                    NodeRef newNodeRef = apiUtils.createRMNode(parentNodeRef, nodeInfo.getName(), nodeInfo.getNodeType(), nodeInfo.getProperties(), nodeInfo.getAspectNames());
+                    createdNodes.add(newNodeRef);
+                }
+                return createdNodes;
+            }
+        };
+        List createdNodes = transactionService.getRetryingTransactionHelper().doInTransaction(callback);
+
+        // Get the nodes info
+        List result = new LinkedList<>();
+        Map mapUserInfo = new HashMap<>();
+        for(NodeRef newNodeRef : createdNodes)
+        {
+            FileInfo info = fileFolderService.getFileInfo(newNodeRef);
+            apiUtils.postActivity(info, parentNodeRef, ActivityType.FILE_ADDED);
+            result.add(nodesModelFactory.createRecord(info, parameters, mapUserInfo, false));
+        }
+
+        return result;
+    }
+
+    @Override
+    @WebApiDescription(title = "Upload file content and meta-data into the repository.")
+    @WebApiParam(name = "formData", title = "A single form data", description = "A single form data which holds FormFields.")
+    public Record create(String recordFolderId, FormData formData, Parameters parameters, WithResponse withResponse)
+    {
+        checkNotBlank("recordFolderId", recordFolderId);
+        mandatory("formData", formData);
+        mandatory("parameters", parameters);
+
+        // Retrieve the input data and resolve the parent node
+        final UploadInfo uploadInfo = new UploadInfo(formData);
+        final NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(recordFolderId, RecordsManagementModel.TYPE_RECORD_FOLDER,
+                uploadInfo.getRelativePath());
+
+        // Create the record
+        RetryingTransactionCallback callback = new RetryingTransactionCallback()
+        {
+            public NodeRef execute()
+            {
+                return apiUtils.uploadRecord(parentNodeRef, uploadInfo.getFileName(), uploadInfo.getNodeType(), uploadInfo.getProperties(),
+                        uploadInfo.getContent().getInputStream());
+            }
+        };
+        NodeRef newNode = transactionService.getRetryingTransactionHelper().doInTransaction(callback);
+
+        // Get file info for response
+        FileInfo info = fileFolderService.getFileInfo(newNode);
+        apiUtils.postActivity(info, parentNodeRef, ActivityType.FILE_ADDED);
+        return nodesModelFactory.createRecord(info, parameters, null, false);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderEntityResource.java
new file mode 100644
index 0000000000..83acf7c9ca
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderEntityResource.java
@@ -0,0 +1,127 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.recordfolders;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.resource.EntityResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.model.RecordFolder;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * Record folder entity resource
+ *
+ * @author Ana Bozianu
+ * @author Tuna Aksoy
+ * @since 2.6
+ */
+@EntityResource(name = "record-folders", title = "Record Folders")
+public class RecordFolderEntityResource implements EntityResourceAction.ReadById, EntityResourceAction.Delete,
+        EntityResourceAction.Update, InitializingBean
+{
+
+    private FilePlanComponentsApiUtils apiUtils;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception
+    {
+        mandatory("apiUtils", apiUtils);
+        mandatory("fileFolderService", fileFolderService);
+        mandatory("apiNodesModelFactory", nodesModelFactory);
+    }
+
+    @WebApiDescription(title = "Get record folder information", description = "Gets information for a record folder with id 'recordFolderId'")
+    @WebApiParam(name = "recordFolderId", title = "The record folder id")
+    public RecordFolder readById(String recordFolderId, Parameters parameters)
+    {
+        checkNotBlank("recordFolderId", recordFolderId);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(recordFolderId, RecordsManagementModel.TYPE_RECORD_FOLDER);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+
+        return nodesModelFactory.createRecordFolder(info, parameters, null, false);
+    }
+
+    @Override
+    @WebApiDescription(title = "Update record folder", description = "Updates a record folder with id 'recordFolderId'")
+    public RecordFolder update(String recordFolderId, RecordFolder recordFolderInfo, Parameters parameters)
+    {
+        checkNotBlank("recordFolderId", recordFolderId);
+        mandatory("recordFolderInfo", recordFolderInfo);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(recordFolderId, RecordsManagementModel.TYPE_RECORD_FOLDER);
+        apiUtils.updateNode(nodeRef, recordFolderInfo, parameters);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+        return nodesModelFactory.createRecordFolder(info, parameters, null, false);
+    }
+
+    @Override
+    @WebApiDescription(title = "Delete record folder", description = "Deletes a record folder with id 'recordFolderId'")
+    public void delete(String recordFolderId, Parameters parameters)
+    {
+        checkNotBlank("recordFolderId", recordFolderId);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(recordFolderId, RecordsManagementModel.TYPE_RECORD_FOLDER);
+
+        fileFolderService.delete(nodeRef);
+    }
+
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/package-info.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/package-info.java
new file mode 100644
index 0000000000..398d9d62a3
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/package-info.java
@@ -0,0 +1,37 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+/**
+ * Package info that defines the Information Governance Record Folders REST API
+ *
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
+package org.alfresco.rm.rest.api.recordfolders;
+import org.alfresco.rest.framework.Api;
+import org.alfresco.rest.framework.WebApi;
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/RecordsEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/RecordsEntityResource.java
index 9075648c11..931fd9e950 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/RecordsEntityResource.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/RecordsEntityResource.java
@@ -27,73 +27,202 @@
 
 package org.alfresco.rm.rest.api.records;
 
-import org.alfresco.rest.api.model.Node;
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
+import org.alfresco.repo.activities.ActivityType;
+import org.alfresco.repo.node.integrity.IntegrityException;
 import org.alfresco.rest.framework.BinaryProperties;
 import org.alfresco.rest.framework.Operation;
 import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
 import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
 import org.alfresco.rest.framework.resource.EntityResource;
 import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction;
+import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
 import org.alfresco.rest.framework.resource.content.BinaryResource;
 import org.alfresco.rest.framework.resource.parameters.Parameters;
 import org.alfresco.rest.framework.webscripts.WithResponse;
-import org.alfresco.rm.rest.api.RMNodes;
-import org.alfresco.rm.rest.api.Records;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.model.Record;
 import org.alfresco.rm.rest.api.model.TargetContainer;
+import org.alfresco.service.cmr.activities.ActivityPoster;
+import org.alfresco.service.cmr.model.FileExistsException;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.model.FileNotFoundException;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
 import org.alfresco.util.ParameterCheck;
 import org.springframework.beans.factory.InitializingBean;
+import org.springframework.dao.ConcurrencyFailureException;
 
 /**
  * An implementation of an Entity Resource for a record
  *
  * @author Ana Bozianu
+ * @author Tuna Aksoy
  * @since 2.6
  */
 @EntityResource(name="records", title = "Records")
 public class RecordsEntityResource implements BinaryResourceAction.Read,
+                                              EntityResourceAction.ReadById,
+                                              EntityResourceAction.Delete,
+                                              EntityResourceAction.Update,
                                               InitializingBean
 {
 
-    private RMNodes nodes;
-    private Records records;
+    private ApiNodesModelFactory nodesModelFactory;
+    private FilePlanComponentsApiUtils apiUtils;
+    private FileFolderService fileFolderService;
+    private RecordService recordService;
+    private NodeService nodeService;
 
-    public void setNodes(RMNodes nodes)
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
     {
-        this.nodes = nodes;
+        this.nodesModelFactory = nodesModelFactory;
     }
 
-    public void setRecords(Records records)
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
     {
-        this.records = records;
+        this.apiUtils = apiUtils;
     }
 
+    public void setRecordService(RecordService recordService)
+    {
+        this.recordService = recordService;
+    }
+
+    public void setNodeService(NodeService nodeService)
+    {
+        this.nodeService = nodeService;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
     /**
      * Download content
-     * 
+     *
      * @param recordId the id of the record to get the content from
      * @param parameters {@link Parameters}
      * @return binary content resource
      * @throws EntityNotFoundException
      */
     @Override
-    @WebApiDescription(title = "Download content", description = "Download content")
+    @WebApiDescription(title = "Download content", description = "Download content for a record with id 'recordId'")
     @BinaryProperties({"content"})
     public BinaryResource readProperty(String recordId, Parameters parameters) throws EntityNotFoundException
     {
-        return nodes.getContent(recordId, parameters, true);
+        checkNotBlank("recordId", recordId);
+        mandatory("parameters", parameters);
+
+        NodeRef record = apiUtils.validateRecord(recordId);
+        if(nodeService.getType(record).equals(RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT))
+        {
+            throw new IllegalArgumentException("Cannot read content from Non-electronic record " + recordId + ".");
+        }
+        BinaryResource content = apiUtils.getContent(record, parameters, true);
+        NodeRef primaryParent = nodeService.getPrimaryParent(record).getParentRef();
+        FileInfo info = fileFolderService.getFileInfo(record);
+        apiUtils.postActivity(info, primaryParent, ActivityPoster.DOWNLOADED);
+        return content;
     }
 
     @Operation("file")
     @WebApiDescription(title = "File record", description="File a record into fileplan.")
-    public Node fileRecord(String recordId, TargetContainer target, Parameters parameters, WithResponse withResponse)
+    public Record fileRecord(String recordId, TargetContainer target, Parameters parameters, WithResponse withResponse)
     {
-        return records.fileOrLinkRecord(recordId, target, parameters);
+        checkNotBlank("recordId", recordId);
+        mandatory("target", target);
+        mandatory("targetParentId", target.getTargetParentId());
+        mandatory("parameters", parameters);
+
+        // Get record and target folder
+        NodeRef record = apiUtils.validateRecord(recordId);
+        NodeRef targetRecordFolder = apiUtils.lookupAndValidateNodeType(target.getTargetParentId(), RecordsManagementModel.TYPE_RECORD_FOLDER);
+
+        // Get the current parent type to decide if we link or move the record
+        NodeRef primaryParent = nodeService.getPrimaryParent(record).getParentRef();
+        if(RecordsManagementModel.TYPE_RECORD_FOLDER.equals(nodeService.getType(primaryParent)))
+        {    
+            recordService.link(record, targetRecordFolder);
+        }
+        else
+        {
+            try
+            {
+                fileFolderService.moveFrom(record, primaryParent, targetRecordFolder, null);
+            }
+            catch (FileExistsException e)
+            {
+                throw new IntegrityException(e.getMessage(), null);
+            }
+            catch (FileNotFoundException e)
+            {
+                throw new ConcurrencyFailureException("The record was deleted while filing it", e);
+            }
+        }
+
+        // return record state
+        FileInfo info = fileFolderService.getFileInfo(record);
+        return nodesModelFactory.createRecord(info, parameters, null, false);
+    }
+
+    @WebApiDescription(title = "Get record information", description = "Gets information for a record with id 'recordId'")
+    @WebApiParam(name = "recordId", title = "The record id")
+    public Record readById(String recordId, Parameters parameters)
+    {
+        checkNotBlank("recordId", recordId);
+        mandatory("parameters", parameters);
+
+        NodeRef record = apiUtils.validateRecord(recordId);
+        FileInfo info = fileFolderService.getFileInfo(record);
+        return nodesModelFactory.createRecord(info, parameters, null, false);
+    }
+
+    @Override
+    @WebApiDescription(title="Update record", description = "Updates a record with id 'recordId'")
+    public Record update(String recordId, Record recordInfo, Parameters parameters)
+    {
+        checkNotBlank("recordId", recordId);
+        mandatory("recordInfo", recordInfo);
+        mandatory("parameters", parameters);
+
+        // Get record
+        NodeRef record = apiUtils.validateRecord(recordId);
+
+        // update info
+        apiUtils.updateNode(record, recordInfo, parameters);
+
+        // return record state
+        FileInfo info = fileFolderService.getFileInfo(record);
+        apiUtils.postActivity(info, recordInfo.getParentId(), ActivityType.FILE_UPDATED);
+        return nodesModelFactory.createRecord(info, parameters, null, false);
+    }
+
+    @Override
+    @WebApiDescription(title = "Delete record", description="Deletes a record with id 'recordId'")
+    public void delete(String recordId, Parameters parameters)
+    {
+        checkNotBlank("recordId", recordId);
+        mandatory("parameters", parameters);
+
+        NodeRef record = apiUtils.validateRecord(recordId);
+        fileFolderService.delete(record);
     }
 
     @Override
     public void afterPropertiesSet() throws Exception
     {
-        ParameterCheck.mandatory("nodes", this.nodes);
-        ParameterCheck.mandatory("records", this.records);
+        ParameterCheck.mandatory("nodesModelFactory", nodesModelFactory);
+        ParameterCheck.mandatory("apiUtils", apiUtils);
+        ParameterCheck.mandatory("fileFolderService", fileFolderService);
+        ParameterCheck.mandatory("recordService", recordService);
+        ParameterCheck.mandatory("nodeService", nodeService);
     }
 }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/package-info.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/package-info.java
index 0eaaf894a5..8c7a83190e 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/package-info.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/package-info.java
@@ -27,11 +27,11 @@
 
 /**
  * Package info that defines the Information Governance Records REST API
- * 
+ *
  * @author Ana Bozianu
  * @since 2.6
  */
-@WebApi(name="ig", scope=Api.SCOPE.PUBLIC, version=1)
+@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
 package org.alfresco.rm.rest.api.records;
 import org.alfresco.rest.framework.Api;
 import org.alfresco.rest.framework.WebApi;
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/sites/RMSiteEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/sites/RMSiteEntityResource.java
index 4a543a9ed1..573fdeb415 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/sites/RMSiteEntityResource.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/sites/RMSiteEntityResource.java
@@ -31,13 +31,14 @@ import java.security.InvalidParameterException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.alfresco.rest.api.model.Site;
+import org.alfresco.rest.api.model.SiteUpdate;
 import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
 import org.alfresco.rest.framework.resource.EntityResource;
 import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
 import org.alfresco.rest.framework.resource.parameters.Parameters;
 import org.alfresco.rm.rest.api.RMSites;
 import org.alfresco.rm.rest.api.model.RMSite;
-import org.alfresco.rm.rest.api.model.SiteUpdate;
 
 /**
  * RM Site operations
@@ -46,7 +47,7 @@ import org.alfresco.rm.rest.api.model.SiteUpdate;
  * @since 2.6
  *
  */
-@EntityResource(name = "ig-sites", title = "IG Sites")
+@EntityResource(name = "gs-sites", title = "GS Sites")
 public class RMSiteEntityResource implements EntityResourceAction.Delete, EntityResourceAction.Create,
             EntityResourceAction.Update, EntityResourceAction.ReadById
 {
@@ -90,38 +91,7 @@ public class RMSiteEntityResource implements EntityResourceAction.Delete, Entity
             throw new InvalidParameterException("The Update is supported only for siteId = rm.");
         }
 
-        // Until REPO-110 is solved, we need to explicitly test for the presence of fields
-        // on the Site object that aren't valid SiteUpdate fields. Once REPO-110 is solved,
-        // the update method will take a SiteUpdate as a parameter rather than a Site
-        // and only the correct fields will be exposed. Any attempt to access illegal fields
-        // should then result in the framework returning a 400 automatically.
-        if (site.getId() != null)
-        {
-            throw new InvalidArgumentException("Site update does not support field: id");
-        }
-        if (site.getGuid() != null)
-        {
-            throw new InvalidArgumentException("Site update does not support field: guid");
-        }
-        if (site.getRole() != null)
-        {
-            throw new InvalidArgumentException("Site update does not support field: role");
-        }
-        if (site.getCompliance() != null)
-        {
-            throw new InvalidArgumentException("Site update does not support field: compliance");
-        }
-        if (site.getVisibility() != null)
-        {
-            throw new InvalidArgumentException("Site update does not support field: visibility");
-        }
-
-        // Bind valid fields to a SiteUpdate instance.
-        final String title = site.getTitle();
-        final String description = site.getDescription();
-        SiteUpdate update = new SiteUpdate(title, description, null);
-
-        return sites.updateRMSite(siteId, update, parameters);
+        return sites.updateRMSite(siteId, convert(site), parameters);
     }
 
     @Override
@@ -133,4 +103,50 @@ public class RMSiteEntityResource implements EntityResourceAction.Delete, Entity
         }
         return sites.getRMSite(siteId);
     }
+
+    protected SiteUpdate convert(RMSite site)
+    {
+        // Until REPO-110 is solved, we need to explicitly test for the presence of fields
+        // on the Site object that aren't valid SiteUpdate fields. Once REPO-110 is solved,
+        // the update method will take a SiteUpdate as a parameter rather than a Site
+        // and only the correct fields will be exposed. Any attempt to access illegal fields
+        // should then result in the framework returning a 400 automatically.
+        if (site.wasSet(Site.ID))
+        {
+            throw new InvalidArgumentException("Site update does not support field: id");
+        }
+        if (site.wasSet(Site.GUID))
+        {
+            throw new InvalidArgumentException("Site update does not support field: guid");
+        }
+        if (site.wasSet(Site.ROLE))
+        {
+            throw new InvalidArgumentException("Site update does not support field: role");
+        }
+        if (site.wasSet(Site.PRESET))
+        {
+            throw new InvalidArgumentException("Site update does not support field: preset");
+        }
+        if (site.wasSet(RMSite.COMPLIANCE))
+        {
+            throw new InvalidArgumentException("Site update does not support field: compliance");
+        }
+        if (site.wasSet(Site.VISIBILITY))
+        {
+            throw new InvalidArgumentException("Site update does not support field: visibility");
+        }
+
+        // Bind valid fields to a SiteUpdate instance.
+        SiteUpdate siteUpdate = new SiteUpdate();
+        if (site.wasSet(Site.TITLE))
+        {
+            siteUpdate.setTitle(site.getTitle());
+        }
+        if (site.wasSet(Site.DESCRIPTION))
+        {
+            siteUpdate.setDescription(site.getDescription());
+        }
+
+        return siteUpdate;
+    }
 }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/sites/package-info.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/sites/package-info.java
index 3a8c97131a..74cf42763e 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/sites/package-info.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/sites/package-info.java
@@ -31,7 +31,7 @@
  * @author Silviu Dinuta
  * @since 2.6
  */
-@WebApi(name="ig", scope=Api.SCOPE.PUBLIC, version=1)
+@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
 package org.alfresco.rm.rest.api.sites;
 import org.alfresco.rest.framework.Api;
 import org.alfresco.rest.framework.WebApi;
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/TransferContainerChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/TransferContainerChildrenRelation.java
new file mode 100644
index 0000000000..426a937f31
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/TransferContainerChildrenRelation.java
@@ -0,0 +1,140 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.transfercontainers;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import java.util.AbstractList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.query.PagingResults;
+import org.alfresco.rest.api.impl.Util;
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.resource.RelationshipResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
+import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.impl.SearchTypesFactory;
+import org.alfresco.rm.rest.api.model.Transfer;
+import org.alfresco.rm.rest.api.model.TransferContainer;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+
+/**
+* Transfer Container children relation
+*
+* @author Silviu Dinuta
+* @since 2.6
+*/
+@RelationshipResource(name="transfers", entityResource = TransferContainerEntityResource.class, title = "Children of a transfer container")
+public class TransferContainerChildrenRelation implements RelationshipResourceAction.Read
+{
+    private FilePlanComponentsApiUtils apiUtils;
+    private SearchTypesFactory searchTypesFactory;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setSearchTypesFactory(SearchTypesFactory searchTypesFactory)
+    {
+        this.searchTypesFactory = searchTypesFactory;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    @Override
+    @WebApiDescription(title = "Return a paged list of transfers for the transfer container identified by 'transferContainerId'")
+    public CollectionWithPagingInfo readAll(String transferContainerId, Parameters parameters)
+    {
+        checkNotBlank("transferContainerId", transferContainerId);
+        mandatory("parameters", parameters);
+
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(transferContainerId, RecordsManagementModel.TYPE_TRANSFER_CONTAINER);
+
+        // list transfers
+        Set searchTypeQNames = searchTypesFactory.buildSearchTypesForTransferContainersEndpoint();
+
+        final PagingResults pagingResults = fileFolderService.list(parentNodeRef,
+                null,
+                searchTypeQNames,
+                null,
+                apiUtils.getSortProperties(parameters),
+                null,
+                Util.getPagingRequest(parameters.getPaging()));
+
+        final List page = pagingResults.getPage();
+        Map mapUserInfo = new HashMap<>();
+        List nodes = new AbstractList()
+        {
+            @Override
+            public Transfer get(int index)
+            {
+                FileInfo info = page.get(index);
+                return nodesModelFactory.createTransfer(info, parameters, mapUserInfo, true);
+            }
+
+            @Override
+            public int size()
+            {
+                return page.size();
+            }
+        };
+
+        TransferContainer sourceEntity = null;
+        if (parameters.includeSource())
+        {
+            FileInfo info = fileFolderService.getFileInfo(parentNodeRef);
+            sourceEntity = nodesModelFactory.createTransferContainer(info, parameters, mapUserInfo, true);
+        }
+
+        return CollectionWithPagingInfo.asPaged(parameters.getPaging(), nodes, pagingResults.hasMoreItems(),
+                pagingResults.getTotalResultCount().getFirst(), sourceEntity);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/TransferContainerEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/TransferContainerEntityResource.java
new file mode 100644
index 0000000000..b3b106e6ee
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/TransferContainerEntityResource.java
@@ -0,0 +1,116 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.transfercontainers;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
+import org.alfresco.rest.framework.resource.EntityResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.model.TransferContainer;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * Transfer Container entity resource
+ *
+ * @author Silviu Dinuta
+ * @since 2.6
+ */
+@EntityResource(name="transfer-containers", title = "Transfer Containers")
+public class TransferContainerEntityResource implements
+        EntityResourceAction.ReadById,
+        EntityResourceAction.Update,
+        InitializingBean
+{
+    private FilePlanComponentsApiUtils apiUtils;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception
+    {
+        mandatory("apiUtils", apiUtils);
+        mandatory("fileFolderService", fileFolderService);
+        mandatory("apiNodesModelFactory", nodesModelFactory);
+    }
+
+    @WebApiDescription(title = "Get transfer container information", description = "Gets information for a transfer container with id 'transferContainerId'")
+    @WebApiParam(name = "transferContainerId", title = "The transfer container id")
+    @Override
+    public TransferContainer readById(String transferContainerId, Parameters parameters) throws EntityNotFoundException
+    {
+        checkNotBlank("transferContainerId", transferContainerId);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(transferContainerId, RecordsManagementModel.TYPE_TRANSFER_CONTAINER);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+
+        return nodesModelFactory.createTransferContainer(info, parameters, null, false);
+    }
+
+    @Override
+    @WebApiDescription(title="Update transfer container", description = "Updates a transfer container with id 'transferContainerId'")
+    public TransferContainer update(String transferContainerId, TransferContainer transferContainerInfo, Parameters parameters)
+    {
+        checkNotBlank("transferContainerId", transferContainerId);
+        mandatory("transferContainerInfo", transferContainerInfo);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(transferContainerId, RecordsManagementModel.TYPE_TRANSFER_CONTAINER);
+        apiUtils.updateTransferContainer(nodeRef, transferContainerInfo, parameters);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+        return nodesModelFactory.createTransferContainer(info, parameters, null, false);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/package-info.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/package-info.java
new file mode 100644
index 0000000000..22d77fe8f8
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfercontainers/package-info.java
@@ -0,0 +1,37 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+/**
+ * Package info that defines Alfresco Governance Services transfer-containers REST API
+ *
+ * @author Silviu Dinuta
+ * @since 2.6
+ */
+@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
+package org.alfresco.rm.rest.api.transfercontainers;
+import org.alfresco.rest.framework.Api;
+import org.alfresco.rest.framework.WebApi;
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/TransferChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/TransferChildrenRelation.java
new file mode 100644
index 0000000000..8b5aaa9f67
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/TransferChildrenRelation.java
@@ -0,0 +1,137 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.transfers;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import java.util.AbstractList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.query.PagingResults;
+import org.alfresco.rest.api.impl.Util;
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.resource.RelationshipResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
+import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.impl.SearchTypesFactory;
+import org.alfresco.rm.rest.api.model.Transfer;
+import org.alfresco.rm.rest.api.model.TransferChild;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+* Transfer children relation
+*
+* @author Silviu Dinuta
+* @since 2.6
+*/
+@RelationshipResource(name="children", entityResource = TransferEntityResource.class, title = "Children of a transfer")
+public class TransferChildrenRelation implements RelationshipResourceAction.Read
+{
+    private FilePlanComponentsApiUtils apiUtils;
+    private SearchTypesFactory searchTypesFactory;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setSearchTypesFactory(SearchTypesFactory searchTypesFactory)
+    {
+        this.searchTypesFactory = searchTypesFactory;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    @Override
+    @WebApiDescription(title = "Return a paged list of record folders or records for the transfer identified by 'transferId'")
+    public CollectionWithPagingInfo readAll(String transferId, Parameters parameters)
+    {
+        checkNotBlank("transferId", transferId);
+        mandatory("parameters", parameters);
+
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(transferId, RecordsManagementModel.TYPE_TRANSFER);
+
+        // list record folder, electronic record or non electronic record
+        final PagingResults pagingResults = fileFolderService.list(parentNodeRef,
+                null,
+                null,
+                null,
+                apiUtils.getSortProperties(parameters),
+                null,
+                Util.getPagingRequest(parameters.getPaging()));
+
+        final List page = pagingResults.getPage();
+        Map mapUserInfo = new HashMap<>();
+        List nodes = new AbstractList()
+        {
+            @Override
+            public TransferChild get(int index)
+            {
+                FileInfo info = page.get(index);
+                return nodesModelFactory.createTransferChild(info, parameters, mapUserInfo, true);
+            }
+
+            @Override
+            public int size()
+            {
+                return page.size();
+            }
+        };
+
+        Transfer sourceEntity = null;
+        if (parameters.includeSource())
+        {
+            FileInfo info = fileFolderService.getFileInfo(parentNodeRef);
+            sourceEntity = nodesModelFactory.createTransfer(info, parameters, mapUserInfo, true);
+        }
+
+        return CollectionWithPagingInfo.asPaged(parameters.getPaging(), nodes, pagingResults.hasMoreItems(),
+                pagingResults.getTotalResultCount().getFirst(), sourceEntity);
+    }
+
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/TransferEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/TransferEntityResource.java
new file mode 100644
index 0000000000..3089a843f0
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/TransferEntityResource.java
@@ -0,0 +1,100 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.transfers;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
+import org.alfresco.rest.framework.resource.EntityResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.model.Transfer;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * Transfer entity resource
+ *
+ * @author Silviu Dinuta
+ * @since 2.6
+ */
+@EntityResource(name="transfers", title = "Transfers")
+public class TransferEntityResource implements
+        EntityResourceAction.ReadById,
+        InitializingBean
+{
+    private FilePlanComponentsApiUtils apiUtils;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception
+    {
+        mandatory("apiUtils", apiUtils);
+        mandatory("fileFolderService", fileFolderService);
+        mandatory("apiNodesModelFactory", nodesModelFactory);
+    }
+
+    @Override
+    @WebApiDescription(title = "Get transfer information", description = "Gets information for a transfer with id 'transferId'")
+    @WebApiParam(name = "transferId", title = "The transfer id")
+    public Transfer readById(String transferId, Parameters parameters) throws EntityNotFoundException
+    {
+        checkNotBlank("transferId", transferId);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(transferId, RecordsManagementModel.TYPE_TRANSFER);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+
+        return nodesModelFactory.createTransfer(info, parameters, null, false);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/package-info.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/package-info.java
new file mode 100644
index 0000000000..fbf7cb1c71
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/transfers/package-info.java
@@ -0,0 +1,37 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+/**
+ * Package info that defines Alfresco Governance Services transfers REST API
+ *
+ * @author Silviu Dinuta
+ * @since 2.6
+ */
+@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
+package org.alfresco.rm.rest.api.transfers;
+import org.alfresco.rest.framework.Api;
+import org.alfresco.rest.framework.WebApi;
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerChildrenRelation.java
new file mode 100644
index 0000000000..ab419a57ca
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerChildrenRelation.java
@@ -0,0 +1,237 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.unfiledcontainers;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.query.PagingResults;
+import org.alfresco.repo.activities.ActivityType;
+import org.alfresco.repo.node.getchildren.FilterProp;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.rest.api.impl.Util;
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.resource.RelationshipResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction;
+import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
+import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rest.framework.webscripts.WithResponse;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.impl.SearchTypesFactory;
+import org.alfresco.rm.rest.api.model.RMNode;
+import org.alfresco.rm.rest.api.model.UnfiledChild;
+import org.alfresco.rm.rest.api.model.UnfiledContainer;
+import org.alfresco.rm.rest.api.model.UnfiledContainerChild;
+import org.alfresco.rm.rest.api.model.UploadInfo;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.springframework.extensions.webscripts.servlet.FormData;
+
+
+/**
+ * Unfiled container children relation
+ *
+ * @author Tuna Aksoy
+ * @author Ana Bozianu
+ * @since 2.6
+ */
+@RelationshipResource(name="children", entityResource = UnfiledContainerEntityResource.class, title = "Children of an unfiled container")
+public class UnfiledContainerChildrenRelation implements RelationshipResourceAction.Read,
+                                                 RelationshipResourceAction.Create,
+                                                 MultiPartRelationshipResourceAction.Create
+{
+
+    private final static Set LIST_UNFILED_CONTAINER_CHILDREN_EQUALS_QUERY_PROPERTIES =
+            new HashSet<>(Arrays.asList(new String[] {UnfiledChild.PARAM_IS_UNFILED_RECORD_FOLDER, UnfiledChild.PARAM_IS_RECORD, RMNode.PARAM_NODE_TYPE}));
+
+    private FilePlanComponentsApiUtils apiUtils;
+    private SearchTypesFactory searchTypesFactory;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+    private TransactionService transactionService;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setSearchTypesFactory(SearchTypesFactory searchTypesFactory)
+    {
+        this.searchTypesFactory = searchTypesFactory;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    public void setTransactionService(TransactionService transactionService)
+    {
+        this.transactionService = transactionService;
+    }
+
+    @Override
+    @WebApiDescription(title = "Return a paged list of unfiled container children for the container identified by 'unfiledContainerId'")
+    public CollectionWithPagingInfo readAll(String unfiledContainerId, Parameters parameters)
+    {
+        // validate parameters
+        checkNotBlank("unfiledContainerId", unfiledContainerId);
+        mandatory("parameters", parameters);
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(unfiledContainerId, RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER);
+
+        // list unfiled record folders and records
+        Set searchTypeQNames = searchTypesFactory.buildSearchTypesForUnfiledEndpoint(parameters, LIST_UNFILED_CONTAINER_CHILDREN_EQUALS_QUERY_PROPERTIES);
+
+        List filterProps = apiUtils.getListChildrenFilterProps(parameters, LIST_UNFILED_CONTAINER_CHILDREN_EQUALS_QUERY_PROPERTIES);
+
+        final PagingResults pagingResults = fileFolderService.list(parentNodeRef,
+                null,
+                searchTypeQNames,
+                null,
+                apiUtils.getSortProperties(parameters),
+                filterProps,
+                Util.getPagingRequest(parameters.getPaging()));
+
+        final List page = pagingResults.getPage();
+        Map mapUserInfo = new HashMap<>();
+        List nodes = new AbstractList()
+        {
+            @Override
+            public UnfiledContainerChild get(int index)
+            {
+                FileInfo info = page.get(index);
+                return nodesModelFactory.createUnfiledContainerChild(info, parameters, mapUserInfo, true);
+            }
+
+            @Override
+            public int size()
+            {
+                return page.size();
+            }
+        };
+
+        UnfiledContainer sourceEntity = null;
+        if (parameters.includeSource())
+        {
+            FileInfo info = fileFolderService.getFileInfo(parentNodeRef);
+            sourceEntity = nodesModelFactory.createUnfiledContainer(info, parameters, mapUserInfo, true);
+        }
+
+        return CollectionWithPagingInfo.asPaged(parameters.getPaging(), nodes, pagingResults.hasMoreItems(),
+                pagingResults.getTotalResultCount().getFirst(), sourceEntity);
+    }
+
+    @Override
+    @WebApiDescription(title="Create one (or more) nodes as children of a unfiled container identified by 'unfiledContainerId'")
+    public List create(String unfiledContainerId, final List nodeInfos, Parameters parameters)
+    {
+        checkNotBlank("unfiledContainerId", unfiledContainerId);
+        mandatory("nodeInfos", nodeInfos);
+        mandatory("parameters", parameters);
+
+        final NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(unfiledContainerId, RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER);
+
+        // Create the nodes
+        RetryingTransactionCallback> callback = new RetryingTransactionCallback>()
+        {
+            public List execute()
+            {
+                List createdNodes = new LinkedList<>();
+                for (UnfiledContainerChild nodeInfo : nodeInfos)
+                {
+                    NodeRef newNodeRef = apiUtils.createRMNode(parentNodeRef, nodeInfo.getName(), nodeInfo.getNodeType(), nodeInfo.getProperties(), nodeInfo.getAspectNames());
+                    createdNodes.add(newNodeRef);
+                }
+                return createdNodes;
+            }
+        };
+        List createdNodes = transactionService.getRetryingTransactionHelper().doInTransaction(callback);
+
+        // Get the nodes info
+        List result = new LinkedList<>();
+        Map mapUserInfo = new HashMap<>();
+        for(NodeRef newNodeRef : createdNodes)
+        {
+            FileInfo info = fileFolderService.getFileInfo(newNodeRef);
+            apiUtils.postActivity(info, parentNodeRef, ActivityType.FILE_ADDED);
+            result.add(nodesModelFactory.createUnfiledContainerChild(info, parameters, mapUserInfo, false));
+        }
+
+        return result;
+    }
+
+    @Override
+    @WebApiDescription(title = "Upload file content and meta-data into a unfiled record container identified by 'unfiledContainerId'.")
+    @WebApiParam(name = "formData", title = "A single form data", description = "A single form data which holds FormFields.")
+    public UnfiledContainerChild create(String unfiledContainerId, FormData formData, Parameters parameters, WithResponse withResponse)
+    {
+        checkNotBlank("unfiledContainerId", unfiledContainerId);
+        mandatory("formData", formData);
+        mandatory("parameters", parameters);
+
+        UploadInfo uploadInfo = new UploadInfo(formData);
+
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(unfiledContainerId, RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER);
+        RetryingTransactionCallback callback = new RetryingTransactionCallback()
+        {
+            public NodeRef execute()
+            {
+                return apiUtils.uploadRecord(parentNodeRef, uploadInfo.getFileName(), uploadInfo.getNodeType(), uploadInfo.getProperties(), uploadInfo.getContent().getInputStream());
+            }
+        };
+        NodeRef newNode = transactionService.getRetryingTransactionHelper().doInTransaction(callback);
+
+        // Get file info for response
+        FileInfo info = fileFolderService.getFileInfo(newNode);
+        apiUtils.postActivity(info, parentNodeRef, ActivityType.FILE_ADDED);
+        return nodesModelFactory.createUnfiledContainerChild(info, parameters, null, false);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerEntityResource.java
new file mode 100644
index 0000000000..e3219d634f
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerEntityResource.java
@@ -0,0 +1,116 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.unfiledcontainers;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.repo.activities.ActivityType;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.resource.EntityResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.model.UnfiledContainer;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * Unfiled container entity resource
+ *
+ * @author Ana Bozianu
+ * @author Tuna Aksoy
+ * @since 2.6
+ */
+@EntityResource(name = "unfiled-containers", title = "Unfiled containers")
+public class UnfiledContainerEntityResource
+        implements EntityResourceAction.ReadById, EntityResourceAction.Update, InitializingBean
+{
+
+    private FilePlanComponentsApiUtils apiUtils;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception
+    {
+        mandatory("apiUtils", apiUtils);
+        mandatory("fileFolderService", fileFolderService);
+        mandatory("apiNodesModelFactory", nodesModelFactory);
+    }
+
+    @WebApiDescription(title = "Get unfiled container information", description = "Gets information for a unfiled container with id 'unfiledContainerId'")
+    @WebApiParam(name = "unfiledContainerId", title = "The unfiled container id")
+    public UnfiledContainer readById(String unfiledContainerId, Parameters parameters)
+    {
+        checkNotBlank("unfiledContainerId", unfiledContainerId);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(unfiledContainerId, RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+
+        return nodesModelFactory.createUnfiledContainer(info, parameters, null, false);
+    }
+
+    @Override
+    @WebApiDescription(title = "Update unfiled record container", description = "Updates an unfiled record container with id 'unfiledContainerId'")
+    public UnfiledContainer update(String unfiledContainerId, UnfiledContainer unfiledContainerInfo, Parameters parameters)
+    {
+        checkNotBlank("unfiledContainerId", unfiledContainerId);
+        mandatory("unfiledContainerInfo", unfiledContainerInfo);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(unfiledContainerId, RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER);
+        apiUtils.updateNode(nodeRef, unfiledContainerInfo, parameters);
+        
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+        apiUtils.postActivity(info, unfiledContainerInfo.getParentId(), ActivityType.FILE_UPDATED);
+        return nodesModelFactory.createUnfiledContainer(info, parameters, null, false);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/package-info.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/package-info.java
new file mode 100644
index 0000000000..b6a3db0131
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/package-info.java
@@ -0,0 +1,37 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+/**
+ * Package info that defines the Information Governance Unfiled Containers REST API
+ *
+ * @author Tuna Aksoy
+ * @since 2.6
+ */
+@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
+package org.alfresco.rm.rest.api.unfiledcontainers;
+import org.alfresco.rest.framework.Api;
+import org.alfresco.rest.framework.WebApi;
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderChildrenRelation.java
new file mode 100644
index 0000000000..0a7faf4771
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderChildrenRelation.java
@@ -0,0 +1,246 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.unfiledrecordfolders;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.query.PagingResults;
+import org.alfresco.repo.activities.ActivityType;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.rest.api.Nodes;
+import org.alfresco.rest.api.impl.Util;
+import org.alfresco.rest.api.model.UserInfo;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.resource.RelationshipResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction;
+import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
+import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rest.framework.webscripts.WithResponse;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.impl.SearchTypesFactory;
+import org.alfresco.rm.rest.api.model.RMNode;
+import org.alfresco.rm.rest.api.model.UnfiledChild;
+import org.alfresco.rm.rest.api.model.UnfiledContainerChild;
+import org.alfresco.rm.rest.api.model.UnfiledRecordFolder;
+import org.alfresco.rm.rest.api.model.UnfiledRecordFolderChild;
+import org.alfresco.rm.rest.api.model.UploadInfo;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.extensions.webscripts.servlet.FormData;
+
+/**
+ * Unfiled Record folder children relation
+ *
+ * @author Ramona Popa
+ * @since 2.6
+ */
+@RelationshipResource(name="children", entityResource = UnfiledRecordFolderEntityResource.class, title = "Children of an unfiled record folder")
+public class UnfiledRecordFolderChildrenRelation implements RelationshipResourceAction.Read,
+                                                 RelationshipResourceAction.Create,
+                                                 MultiPartRelationshipResourceAction.Create
+{
+    private final static Set LIST_UNFILED_RECORD_FOLDER_CHILDREN_EQUALS_QUERY_PROPERTIES = new HashSet<>(Arrays
+            .asList(new String[] { UnfiledChild.PARAM_IS_UNFILED_RECORD_FOLDER, UnfiledChild.PARAM_IS_RECORD, RMNode.PARAM_NODE_TYPE }));
+
+    private FilePlanComponentsApiUtils apiUtils;
+    private SearchTypesFactory searchTypesFactory;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+    private TransactionService transactionService;
+
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setSearchTypesFactory(SearchTypesFactory searchTypesFactory)
+    {
+        this.searchTypesFactory = searchTypesFactory;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    public void setTransactionService(TransactionService transactionService)
+    {
+        this.transactionService = transactionService;
+    }
+
+    @Override
+    @WebApiDescription(title = "Return a paged list of unfiled container children for the container identified by 'unfiledContainerId'")
+    public CollectionWithPagingInfo readAll(String unfileRecordFolderId, Parameters parameters)
+    {
+        checkNotBlank("unfileRecordFolderId", unfileRecordFolderId);
+        mandatory("parameters", parameters);
+
+        String relativePath = parameters.getParameter(Nodes.PARAM_RELATIVE_PATH);
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(unfileRecordFolderId, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER, relativePath, true);
+
+        // list unfiled record folders and records
+        Set searchTypeQNames = searchTypesFactory.buildSearchTypesForUnfiledEndpoint(parameters, LIST_UNFILED_RECORD_FOLDER_CHILDREN_EQUALS_QUERY_PROPERTIES);
+
+        final PagingResults pagingResults = fileFolderService.list(parentNodeRef,
+                null,
+                searchTypeQNames,
+                null,
+                apiUtils.getSortProperties(parameters),
+                null,
+                Util.getPagingRequest(parameters.getPaging()));
+
+        final List page = pagingResults.getPage();
+        Map mapUserInfo = new HashMap<>();
+        List nodes = new AbstractList()
+        {
+            @Override
+            public UnfiledRecordFolderChild get(int index)
+            {
+                FileInfo info = page.get(index);
+                return nodesModelFactory.createUnfiledRecordFolderChild(info, parameters, mapUserInfo, true);
+            }
+
+            @Override
+            public int size()
+            {
+                return page.size();
+            }
+        };
+
+        UnfiledRecordFolder sourceEntity = null;
+        if (parameters.includeSource())
+        {
+            FileInfo info = fileFolderService.getFileInfo(parentNodeRef);
+            sourceEntity = nodesModelFactory.createUnfiledRecordFolder(info, parameters, mapUserInfo, true);
+        }
+
+        return CollectionWithPagingInfo.asPaged(parameters.getPaging(), nodes, pagingResults.hasMoreItems(), pagingResults.getTotalResultCount().getFirst(), sourceEntity);
+    }
+
+    @Override
+    @WebApiDescription(title = "Create one (or more) nodes as children of a unfiled record folder identified by 'unfiledRecordFolderId'")
+    public List create(String unfiledRecordFolderId, final List nodeInfos, Parameters parameters)
+    {
+        checkNotBlank("unfiledRecordFolderId", unfiledRecordFolderId);
+        mandatory("nodeInfos", nodeInfos);
+        mandatory("parameters", parameters);
+
+        NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(unfiledRecordFolderId, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER);
+
+        // Create the children
+        RetryingTransactionCallback> callback = new RetryingTransactionCallback>()
+        {
+            public List execute()
+            {
+                List createdNodes = new LinkedList<>();
+                for (UnfiledRecordFolderChild nodeInfo : nodeInfos)
+                {
+                    NodeRef nodeParent;
+                    if(StringUtils.isNoneBlank(nodeInfo.getRelativePath()))
+                    {
+                        nodeParent = apiUtils.lookupAndValidateRelativePath(parentNodeRef, nodeInfo.getRelativePath(), RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER);
+                    }
+                    else
+                    {
+                        nodeParent = parentNodeRef;
+                    }
+
+                    NodeRef newNodeRef = apiUtils.createRMNode(nodeParent, nodeInfo.getName(), nodeInfo.getNodeType(), nodeInfo.getProperties(), nodeInfo.getAspectNames());
+                    createdNodes.add(newNodeRef);
+                }
+                return createdNodes;
+            }
+        };
+        List createdNodes = transactionService.getRetryingTransactionHelper().doInTransaction(callback);
+ 
+        // Get the nodes info
+        List result = new LinkedList<>();
+        Map mapUserInfo = new HashMap<>();
+        for(NodeRef newNodeRef : createdNodes)
+        {
+            FileInfo info = fileFolderService.getFileInfo(newNodeRef);
+            apiUtils.postActivity(info, parentNodeRef, ActivityType.FILE_ADDED);
+            result.add(nodesModelFactory.createUnfiledRecordFolderChild(info, parameters, mapUserInfo, false));
+        }
+
+        return result;
+    }
+
+    @Override
+    @WebApiDescription(title = "Upload file content and meta-data into the repository.")
+    @WebApiParam(name = "formData", title = "A single form data", description = "A single form data which holds FormFields.")
+    public UnfiledRecordFolderChild create(String unfiledRecordFolderId, FormData formData, Parameters parameters, WithResponse withResponse)
+    {
+        checkNotBlank("unfiledRecordFolderId", unfiledRecordFolderId);
+        mandatory("formData", formData);
+        mandatory("parameters", parameters);
+
+        // Retrieve the input data and resolve the parent node
+        final UploadInfo uploadInfo = new UploadInfo(formData);
+        final NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(unfiledRecordFolderId, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER, uploadInfo.getRelativePath());
+
+        // Create the record
+        RetryingTransactionCallback callback = new RetryingTransactionCallback()
+        {
+            public NodeRef execute()
+            {
+                return apiUtils.uploadRecord(parentNodeRef, uploadInfo.getFileName(), uploadInfo.getNodeType(), uploadInfo.getProperties(), uploadInfo.getContent().getInputStream());
+            }
+        };
+        NodeRef newNode = transactionService.getRetryingTransactionHelper().doInTransaction(callback);
+
+        // Get file info for response
+        FileInfo info = fileFolderService.getFileInfo(newNode);
+        apiUtils.postActivity(info, parentNodeRef, ActivityType.FILE_ADDED);
+        return nodesModelFactory.createUnfiledRecordFolderChild(info, parameters, null, false);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderEntityResource.java
new file mode 100644
index 0000000000..c03f04ca05
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderEntityResource.java
@@ -0,0 +1,131 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.rm.rest.api.unfiledrecordfolders;
+
+import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
+import static org.alfresco.util.ParameterCheck.mandatory;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.repo.activities.ActivityType;
+import org.alfresco.rest.api.Nodes;
+import org.alfresco.rest.framework.WebApiDescription;
+import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.resource.EntityResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
+import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
+import org.alfresco.rm.rest.api.model.UnfiledRecordFolder;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * Unfiled record folder entity resource
+ *
+ * @author Ramona Popa
+ * @since 2.6
+ */
+@EntityResource(name = "unfiled-record-folders", title = "Unfiled Record Folders")
+public class UnfiledRecordFolderEntityResource implements EntityResourceAction.ReadById,
+                                                          EntityResourceAction.Delete,
+                                                          EntityResourceAction.Update, InitializingBean
+
+{
+    private FilePlanComponentsApiUtils apiUtils;
+    private FileFolderService fileFolderService;
+    private ApiNodesModelFactory nodesModelFactory;
+    public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
+    {
+        this.apiUtils = apiUtils;
+    }
+
+    public void setFileFolderService(FileFolderService fileFolderService)
+    {
+        this.fileFolderService = fileFolderService;
+    }
+
+    public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
+    {
+        this.nodesModelFactory = nodesModelFactory;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception
+    {
+        mandatory("apiUtils", apiUtils);
+        mandatory("fileFolderService", fileFolderService);
+        mandatory("apiNodesModelFactory", nodesModelFactory);
+    }
+
+    @WebApiDescription(title = "Get unfiled record folder information", description = "Gets information for an unfiled record folder with id 'unfiledRecordFolderId'")
+    @WebApiParam(name = "unfiledRecordFolderId", title = "The unfiled record folder id")
+    public UnfiledRecordFolder readById(String unfiledRecordFolderId, Parameters parameters)
+    {
+        checkNotBlank("unfiledRecordFolderId", unfiledRecordFolderId);
+        mandatory("parameters", parameters);
+
+        String relativePath = parameters.getParameter(Nodes.PARAM_RELATIVE_PATH);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(unfiledRecordFolderId, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER, relativePath, true);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+
+        return nodesModelFactory.createUnfiledRecordFolder(info, parameters, null, false);
+
+    }
+
+    @Override
+    @WebApiDescription(title = "Update unfiled record folder", description = "Updates an unfiled record folder with id 'unfiledRecordFolderId'")
+    public UnfiledRecordFolder update(String unfiledRecordFolderId, UnfiledRecordFolder unfiledRecordFolderInfo, Parameters parameters)
+    {
+        checkNotBlank("unfiledRecordFolderId", unfiledRecordFolderId);
+        mandatory("unfiledRecordFolderInfo", unfiledRecordFolderInfo);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(unfiledRecordFolderId, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER);
+        apiUtils.updateNode(nodeRef, unfiledRecordFolderInfo, parameters);
+
+        FileInfo info = fileFolderService.getFileInfo(nodeRef);
+        apiUtils.postActivity(info, unfiledRecordFolderInfo.getParentId(), ActivityType.FILE_UPDATED);
+        return nodesModelFactory.createUnfiledRecordFolder(info, parameters, null, false);
+    }
+
+    @Override
+    @WebApiDescription(title = "Delete unfiled record folder", description = "Deletes an unfiled record folder with id 'unfiledRecordFolderId'")
+    public void delete(String unfiledRecordFolderId, Parameters parameters)
+    {
+        checkNotBlank("unfiledRecordFolderId", unfiledRecordFolderId);
+        mandatory("parameters", parameters);
+
+        NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(unfiledRecordFolderId, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER);
+
+        fileFolderService.delete(nodeRef);
+    }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/package-info.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/package-info.java
new file mode 100644
index 0000000000..c3a85cb149
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/package-info.java
@@ -0,0 +1,37 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+/**
+ * Package info that defines the Information Governance Unfiled Record Folders REST API
+ *
+ * @author Ramona Popa
+ * @since 2.6
+ */
+@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
+package org.alfresco.rm.rest.api.unfiledrecordfolders;
+import org.alfresco.rest.framework.Api;
+import org.alfresco.rest.framework.WebApi;
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentChildrenRelationUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentChildrenRelationUnitTest.java
deleted file mode 100644
index 0586e73a3a..0000000000
--- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentChildrenRelationUnitTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api.fileplancomponents;
-
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
-import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.framework.resource.parameters.Parameters;
-import org.alfresco.rest.framework.webscripts.WithResponse;
-import org.alfresco.rm.rest.api.impl.RMNodesImpl;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.springframework.extensions.webscripts.servlet.FormData;
-
-/**
- * Unit Test class for FileplanComponentChildrenRelation.
- *
- * @author Silviu Dinuta
- * @since 2.6
- *
- */
-public class FileplanComponentChildrenRelationUnitTest extends BaseUnitTest
-{
-
-    @Mock
-    private RMNodesImpl mockedRMNodes;
-
-    @InjectMocks
-    private FileplanComponentChildrenRelation filePlanComponentChildrenRelation;
-
-    @Before
-    public void before()
-    {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void testReadAll() throws Exception
-    {
-        Parameters mockedParameters = mock(Parameters.class);
-        NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        filePlanComponentChildrenRelation.readAll(parentNodeRef.getId(), mockedParameters);
-        verify(mockedRMNodes, times(1)).listChildren(parentNodeRef.getId(), mockedParameters);
-    }
-
-    @Test
-    public void testCreate() throws Exception
-    {
-        Parameters mockedParameters = mock(Parameters.class);
-        NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-
-        List nodeInfos = new ArrayList();
-        Node mokedNodeInfo = mock(Node.class);
-        nodeInfos.add(mokedNodeInfo);
-
-        filePlanComponentChildrenRelation.create(parentNodeRef.getId(), nodeInfos, mockedParameters);
-        verify(mockedRMNodes, times(1)).createNode(parentNodeRef.getId(), nodeInfos.get(0), mockedParameters);
-    }
-
-    @Test
-    public void testUpload() throws Exception
-    {
-        Parameters mockedParameters = mock(Parameters.class);
-        NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        FormData mockedFormData = mock(FormData.class);
-        WithResponse mockedWithResponse = mock(WithResponse.class);
-        filePlanComponentChildrenRelation.create(parentNodeRef.getId(), mockedFormData, mockedParameters, mockedWithResponse);
-        verify(mockedRMNodes, times(1)).upload(parentNodeRef.getId(), mockedFormData, mockedParameters);
-    }
-}
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentsEntityResourceUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentsEntityResourceUnitTest.java
deleted file mode 100644
index f7cb5fc8ae..0000000000
--- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/fileplancomponents/FileplanComponentsEntityResourceUnitTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api.fileplancomponents;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
-import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
-import org.alfresco.rest.framework.resource.parameters.Parameters;
-import org.alfresco.rm.rest.api.impl.RMNodesImpl;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Unit Test class for FileplanComponentsEntityResource.
- *
- * @author Silviu Dinuta
- * @since 2.6
- *
- */
-public class FileplanComponentsEntityResourceUnitTest extends BaseUnitTest
-{
-    private static final String PERMANENT_PARAMETER = "permanent";
-
-    @Mock
-    private RMNodesImpl mockedRMNodes;
-
-    @InjectMocks
-    private FileplanComponentsEntityResource filePlanComponentsEntityResource;
-
-    @Before
-    public void before()
-    {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void testReadById() throws Exception
-    {
-        NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        Parameters mockedParameters = mock(Parameters.class);
-        filePlanComponentsEntityResource.readById(parentNodeRef.getId(), mockedParameters);
-        verify(mockedRMNodes, times(1)).getFolderOrDocument(parentNodeRef.getId(), mockedParameters);
-    }
-
-    @Test
-    public void testUpdate() throws Exception
-    {
-        NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        Parameters mockedParameters = mock(Parameters.class);
-        Node mockedNodeInfo = mock(Node.class);
-        filePlanComponentsEntityResource.update(parentNodeRef.getId(), mockedNodeInfo, mockedParameters);
-        verify(mockedRMNodes, times(1)).updateNode(parentNodeRef.getId(), mockedNodeInfo, mockedParameters);
-    }
-
-    @Test
-    public void testDelete() throws Exception
-    {
-        NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        Parameters mockedParameters = mock(Parameters.class);
-        when(mockedParameters.getParameter(PERMANENT_PARAMETER)).thenReturn(null);
-        filePlanComponentsEntityResource.delete(parentNodeRef.getId(), mockedParameters);
-        verify(mockedRMNodes, times(1)).deleteNode(parentNodeRef.getId(), mockedParameters);
-    }
-
-    @Test
-    public void testDeleteWithPermanentParameter() throws Exception
-    {
-        NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        Parameters mockedParameters = mock(Parameters.class);
-        when(mockedParameters.getParameter(PERMANENT_PARAMETER)).thenReturn(Boolean.toString(true));
-
-        try
-        {
-            filePlanComponentsEntityResource.delete(parentNodeRef.getId(), mockedParameters);
-            fail("Expected ecxeption as DELETE does not support parameter: permanent.");
-        }
-        catch(InvalidArgumentException ex)
-        {
-            assertEquals("DELETE does not support parameter: permanent", ex.getMsgId());
-        }
-        verify(mockedRMNodes, never()).deleteNode(parentNodeRef.getId(), mockedParameters);
-    }
-}
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMNodesImplRelativePathUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMNodesImplRelativePathUnitTest.java
deleted file mode 100644
index 0a0db60ae7..0000000000
--- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMNodesImplRelativePathUnitTest.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import org.alfresco.model.ContentModel;
-import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
-import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
-import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.service.cmr.model.FileInfo;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.namespace.NamespaceService;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Unit Test class for RMNodesImpl.getOrCreatePath method
- * 
- * @author Ana Bozianu
- * @since 2.6
- */
-public class RMNodesImplRelativePathUnitTest  extends BaseUnitTest
-{
-    @InjectMocks
-    private RMNodesImpl rmNodesImpl;
-
-    @Before
-    public void before()
-    {
-        MockitoAnnotations.initMocks(this);
-
-        when(mockedDictionaryService.isSubClass(TYPE_RECORD_CATEGORY, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER)).thenReturn(false);
-        when(mockedDictionaryService.isSubClass(TYPE_RECORD_CATEGORY, RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER)).thenReturn(false);
-        when(mockedDictionaryService.isSubClass(ContentModel.TYPE_CONTENT, ContentModel.TYPE_CONTENT)).thenReturn(true);
-        when(mockedDictionaryService.isSubClass(TYPE_UNFILED_RECORD_CONTAINER, RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER)).thenReturn(true);
-        when(mockedDictionaryService.isSubClass(TYPE_RECORD_FOLDER, ContentModel.TYPE_CONTENT)).thenReturn(false);
-        when(mockedNamespaceService.getNamespaceURI(NamespaceService.CONTENT_MODEL_PREFIX)).thenReturn(NamespaceService.CONTENT_MODEL_1_0_URI);
-        when(mockedNamespaceService.getNamespaceURI(RM_PREFIX)).thenReturn(RM_URI);
-    }
-
-    /**
-     * Given any parent node
-     * When trying to create a node in the parent node with no relative path
-     * Then the parent node is returned and no node is created
-     */
-    @Test
-    public void testNoRelativePath() throws Exception
-    {
-        /*
-         * Given any parent node
-         */
-        NodeRef parentNode = AlfMock.generateNodeRef(mockedNodeService);
-
-        /*
-         *  When trying to create a node in the parent node with no relative path
-         */
-        Node nodeInfo = mock(Node.class);
-        NodeRef returnedPath = rmNodesImpl.getOrCreatePath(parentNode.getId(), null, ContentModel.TYPE_CONTENT);
-
-        /*
-         * Then the parent node is returned and no node is created
-         */
-        assertEquals(parentNode, returnedPath);
-        verify(mockedFileFolderService, never()).create(any(), any(), any());
-        verify(mockedFilePlanService, never()).createRecordCategory(any(), any());
-    }
-
-    /**
-     * Given a parent node and an existing path c1/f1 under it
-     * When trying to create a node in the parent node with the relative path c1/f1
-     * Then the node f1 is returned and no node is created
-     */
-    @Test
-    public void testGetExistingRelativePath() throws Exception
-    {
-        /*
-         * Given a parent node and an existing path c1/f1 under it
-         */
-        NodeRef parentNode = AlfMock.generateNodeRef(mockedNodeService);
-
-        String category = "c1";
-        NodeRef categoryNode = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getChildByName(parentNode, ContentModel.ASSOC_CONTAINS, category)).thenReturn(categoryNode);
-
-        String recordFolder = "f1";
-        NodeRef recordFolderNode = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getChildByName(categoryNode, ContentModel.ASSOC_CONTAINS, recordFolder)).thenReturn(recordFolderNode);
-
-        /*
-         * When trying to create a node in the parent node with the relative path c1/f1
-         */
-        Node nodeInfo = mock(Node.class);
-        NodeRef returnedPath = rmNodesImpl.getOrCreatePath(parentNode.getId(), category + "/" + recordFolder, ContentModel.TYPE_CONTENT);
-
-        /*
-         * Then the node f1 is returned and no node is created
-         */
-        assertEquals(recordFolderNode, returnedPath);
-        verify(mockedFileFolderService, never()).create(any(), any(), any());
-        verify(mockedFilePlanService, never()).createRecordCategory(any(), any());
-    }
-
-    /**
-     * Given the fileplan and an existing path c1/c2 under fileplan
-     * When creating a content node under fileplan with the relative path c1/c2/c3/f1
-     * Then the category c3 and the record folder f1 should be created and f1 should be returned
-     * 
-     * @throws Exception
-     */
-    @Test
-    public void testCreatePartiallyExistingRelativePath() throws Exception
-    {
-        /*
-         *  Given the fileplan and an existing path c1/c2 under fileplan
-         */
-        NodeRef fileplanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-
-        // create c1
-        String category1 = "c1";
-        NodeRef categoryNode1 = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getChildByName(fileplanNodeRef, ContentModel.ASSOC_CONTAINS, category1)).thenReturn(categoryNode1);
-
-        // create c2
-        String category2 = "c2";
-        NodeRef categoryNode2 = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getChildByName(categoryNode1, ContentModel.ASSOC_CONTAINS, category2)).thenReturn(categoryNode2);
-        when(mockedNodeService.getType(categoryNode2)).thenReturn(TYPE_RECORD_CATEGORY);
-
-        /*
-         *  When trying to create a content node in the relative path c1/c2/c3/f1
-         */
-        // c3
-        String category3 = "c3";
-        NodeRef categoryNode3 = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.createRecordCategory(categoryNode2, category3)).thenReturn(categoryNode3);
-
-        // f1
-        String recordFolder = "f1";
-        NodeRef recordFolderNode = AlfMock.generateNodeRef(mockedNodeService);
-        FileInfo recordFolderFileInfo = mock(FileInfo.class);
-        when(recordFolderFileInfo.getNodeRef()).thenReturn(recordFolderNode);
-        when(mockedFileFolderService.create(categoryNode3, recordFolder, RecordsManagementModel.TYPE_RECORD_FOLDER)).thenReturn(recordFolderFileInfo);
-
-        // call the class under tests
-        NodeRef returnedPath = rmNodesImpl.getOrCreatePath(fileplanNodeRef.getId(), category1 + "/" + category2 + "/" + category3 + "/" + recordFolder, ContentModel.TYPE_CONTENT);
-
-        /*
-         *  Then the category c1 and the record folder f1 should be created and f1 should be returned
-         */
-        assertEquals(recordFolderNode, returnedPath);
-        verify(mockedFilePlanService, times(1)).createRecordCategory(categoryNode2, category3);
-        verify(mockedFileFolderService, times(1)).create(categoryNode3, recordFolder, RecordsManagementModel.TYPE_RECORD_FOLDER);
-    }
-
-    /**
-     * Given the unfiled record container
-     * When creating a content node under fileplan with the relative path f1/f2/f3
-     * Then the 3 unfiled record folders should be created and f3 should be returned
-     */
-    @Test
-    public void testCreateRelativePathInUnfiledRecords() throws Exception
-    {
-        /*
-         *  Given the unfiled record folder
-         */
-        NodeRef unfiledRecordContainer = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(unfiledRecordContainer)).thenReturn(TYPE_UNFILED_RECORD_CONTAINER);
-
-        /*
-         *  When trying to create a content node in the relative path f1/f2/f3
-         */
-        // f1
-        String folder1 = "f1";
-        NodeRef folderNode1 = AlfMock.generateNodeRef(mockedNodeService);
-        FileInfo folderFileInfo1 = mock(FileInfo.class);
-        when(folderFileInfo1.getNodeRef()).thenReturn(folderNode1);
-        when(mockedFileFolderService.create(unfiledRecordContainer, folder1, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER)).thenReturn(folderFileInfo1);
-
-        // f2
-        String folder2 = "f2";
-        NodeRef folderNode2 = AlfMock.generateNodeRef(mockedNodeService);
-        FileInfo folderFileInfo2 = mock(FileInfo.class);
-        when(folderFileInfo2.getNodeRef()).thenReturn(folderNode2);
-        when(mockedFileFolderService.create(folderNode1, folder2, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER)).thenReturn(folderFileInfo2);
-
-        // f3
-        String folder3 = "f3";
-        NodeRef folderNode3 = AlfMock.generateNodeRef(mockedNodeService);
-        FileInfo folderFileInfo3 = mock(FileInfo.class);
-        when(folderFileInfo3.getNodeRef()).thenReturn(folderNode3);
-        when(mockedFileFolderService.create(folderNode2, folder3, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER)).thenReturn(folderFileInfo3);
-
-        // call the class under tests
-        NodeRef returnedParentNode = rmNodesImpl.getOrCreatePath(unfiledRecordContainer.getId(), folder1 + "/" + folder2 + "/" + folder3, ContentModel.TYPE_CONTENT);
-
-        /*
-         *  Then the category c1 and the record folder rf1 should be created 
-         *  and an instance to the record folder should be returned
-         */
-        assertEquals(folderNode3, returnedParentNode);
-        verify(mockedFileFolderService, times(1)).create(unfiledRecordContainer, folder1, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER);
-        verify(mockedFileFolderService, times(1)).create(folderNode1, folder2, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER);
-        verify(mockedFileFolderService, times(1)).create(folderNode2, folder3, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER);
-
-        //check no other node is created
-        verify(mockedFilePlanService, never()).createRecordCategory(any(), any());
-        verify(mockedFileFolderService, times(3)).create(any(), any(), any());
-    }
-
-    /**
-     * Given the fileplan
-     * When creating a record folder node under fileplan with the relative path c1/c2/c3
-     * Then the categories c1, c2 and c3 should be created and c3 should be returned
-     */
-    @Test
-    public void testCreateRelativePathToRecordFolder() throws Exception
-    {
-        /*
-         *  Given the fileplan
-         */
-        NodeRef fileplanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(fileplanNodeRef)).thenReturn(TYPE_FILE_PLAN);
-
-        /*
-         *  When trying to create a folder node in the relative path c1/c2/c3
-         */
-        // c1
-        String category1 = "c1";
-        NodeRef categoryNode1 = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.createRecordCategory(fileplanNodeRef, category1)).thenReturn(categoryNode1);
-
-        // c2
-        String category2 = "c2";
-        NodeRef categoryNode2 = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.createRecordCategory(categoryNode1, category2)).thenReturn(categoryNode2);
-
-        // c3
-        String category3 = "c3";
-        NodeRef categoryNode3 = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.createRecordCategory(categoryNode2, category3)).thenReturn(categoryNode3);
-
-        // call the class under tests
-        NodeRef returnedParentNode = rmNodesImpl.getOrCreatePath(fileplanNodeRef.getId(), category1 + "/" + category2 + "/" + category3, RecordsManagementModel.TYPE_RECORD_FOLDER);
-
-        /*
-         *  Then the categories c1, c2 and c3 should be created and c3 should be returned
-         */
-        assertEquals(categoryNode3, returnedParentNode);
-        verify(mockedFilePlanService, times(1)).createRecordCategory(fileplanNodeRef, category1);
-        verify(mockedFilePlanService, times(1)).createRecordCategory(categoryNode1, category2);
-        verify(mockedFilePlanService, times(1)).createRecordCategory(categoryNode2, category3);
-
-        // check no other node is created
-        verify(mockedFilePlanService, times(3)).createRecordCategory(any(), any());
-        verify(mockedFileFolderService, never()).create(any(), any(), any());
-    }
-}
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMNodesImplUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMNodesImplUnitTest.java
deleted file mode 100644
index e174775483..0000000000
--- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMNodesImplUnitTest.java
+++ /dev/null
@@ -1,671 +0,0 @@
-/*
- * #%L
- * Alfresco Records Management Module
- * %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * -
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail.  Otherwise, the software is
- * provided under the following open source license terms:
- * -
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * -
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * -
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.rm.rest.api.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.security.InvalidParameterException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.alfresco.model.ContentModel;
-import org.alfresco.module.org_alfresco_module_rm.capability.Capability;
-import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
-import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
-import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
-import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
-import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
-import org.alfresco.repo.model.Repository;
-import org.alfresco.rest.api.model.Node;
-import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
-import org.alfresco.rm.rest.api.RMNodes;
-import org.alfresco.rm.rest.api.model.FileplanComponentNode;
-import org.alfresco.rm.rest.api.model.RecordCategoryNode;
-import org.alfresco.rm.rest.api.model.RecordFolderNode;
-import org.alfresco.rm.rest.api.model.RecordNode;
-import org.alfresco.service.ServiceRegistry;
-import org.alfresco.service.cmr.repository.ChildAssociationRef;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.security.AccessStatus;
-import org.alfresco.service.cmr.security.PermissionService;
-import org.alfresco.service.cmr.security.PersonService;
-import org.alfresco.service.cmr.site.SiteService;
-import org.alfresco.service.namespace.NamespaceService;
-import org.alfresco.service.namespace.QName;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import net.sf.acegisecurity.vote.AccessDecisionVoter;
-
-/**
- * Unit Test class for RMNodesImpl.
- *
- * @author Silviu Dinuta
- * @since 2.6
- */
-public class RMNodesImplUnitTest extends BaseUnitTest
-{
-    private static final String UNFILED_ALIAS = "-unfiled-";
-
-    private static final String HOLDS_ALIAS = "-holds-";
-
-    private static final String TRANSFERS_ALIAS = "-transfers-";
-
-    private static final String FILE_PLAN_ALIAS = "-filePlan-";
-
-    private static final String RM_SITE_ID = "rm";
-
-    @Mock
-    private SiteService mockedSiteService;
-
-    @Mock
-    private Repository mockedRepositoryHelper;
-
-    @Mock
-    private PersonService mockedPersonService;
-
-    @Mock
-    private ServiceRegistry mockedServiceRegistry;
-    
-    @Mock
-    private CapabilityService mockedCapabilityService;
-
-    @InjectMocks
-    private RMNodesImpl rmNodesImpl;
-
-    private Capability deleteCapability;
-    private Capability createCapability;
-    private Capability updateCapability;
-
-    @Before
-    public void before()
-    {
-        MockitoAnnotations.initMocks(this);
-
-        List prefixes = new ArrayList();
-        prefixes.add(NamespaceService.DEFAULT_PREFIX);
-        when(mockedNamespaceService.getPrefixes(any(String.class))).thenReturn(prefixes);
-        when(mockedNamespaceService.getNamespaceURI(any(String.class))).thenReturn(RM_URI);
-
-        deleteCapability = mock(Capability.class);
-        when(mockedCapabilityService.getCapability("Delete")).thenReturn(deleteCapability);
-        createCapability = mock(Capability.class);
-        when(mockedCapabilityService.getCapability("FillingPermissionOnly")).thenReturn(createCapability);
-        updateCapability = mock(Capability.class);
-        when(mockedCapabilityService.getCapability("Update")).thenReturn(updateCapability);
-    }
-
-    @Test
-    public void testGetFileplanComponent() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        QName mockedType = AlfMock.generateQName();
-        when(mockedNodeService.getType(nodeRef)).thenReturn(mockedType);
-        when(mockedDictionaryService.isSubClass(mockedType, ContentModel.TYPE_CMOBJECT)).thenReturn(true);
-        when(mockedDictionaryService.isSubClass(mockedType, ContentModel.TYPE_SYSTEM_FOLDER)).thenReturn(false);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        when(mockedFilePlanService.isFilePlanComponent(nodeRef)).thenReturn(true);
-        List includeParamList = new ArrayList();
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(FileplanComponentNode.class.isInstance(folderOrDocument));
-
-        FileplanComponentNode resultNode = (FileplanComponentNode) folderOrDocument;
-        assertEquals(false, resultNode.getIsRecordFolder());
-        assertEquals(false, resultNode.getIsFile());
-        assertEquals(false, resultNode.getIsCategory());
-    }
-
-    @Test
-    public void testGetFilePlanAllowableOperations() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        QName mockedType = AlfMock.generateQName();
-        when(mockedNodeService.getType(nodeRef)).thenReturn(mockedType);
-        when(mockedDictionaryService.isSubClass(mockedType, ContentModel.TYPE_CMOBJECT)).thenReturn(true);
-        when(mockedDictionaryService.isSubClass(mockedType, ContentModel.TYPE_SYSTEM_FOLDER)).thenReturn(false);
-        when(mockedDictionaryService.isSubClass(mockedType, ContentModel.TYPE_FOLDER)).thenReturn(true);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        when(mockedFilePlanService.isFilePlanComponent(nodeRef)).thenReturn(true);
-        List includeParamList = new ArrayList();
-        includeParamList.add(RMNodes.PARAM_INCLUDE_ALLOWABLEOPERATIONS);
-
-        setPermissions(nodeRef, AccessStatus.ALLOWED);
-
-        when(deleteCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-        when(createCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-        when(updateCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(nodeRef);
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        checksAllowedOperations(folderOrDocument, true, true, false);
-    }
-
-    @Test
-    public void testGetFilePlanAllowableOperationsWithoutPermissions() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        QName mockedType = AlfMock.generateQName();
-        when(mockedNodeService.getType(nodeRef)).thenReturn(mockedType);
-        when(mockedDictionaryService.isSubClass(mockedType, ContentModel.TYPE_CMOBJECT)).thenReturn(true);
-        when(mockedDictionaryService.isSubClass(mockedType, ContentModel.TYPE_SYSTEM_FOLDER)).thenReturn(false);
-        when(mockedDictionaryService.isSubClass(mockedType, ContentModel.TYPE_FOLDER)).thenReturn(true);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        when(mockedFilePlanService.isFilePlanComponent(nodeRef)).thenReturn(true);
-        List includeParamList = new ArrayList();
-        includeParamList.add(RMNodes.PARAM_INCLUDE_ALLOWABLEOPERATIONS);
-
-        setPermissions(nodeRef, AccessStatus.DENIED);
-
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(nodeRef);
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(FileplanComponentNode.class.isInstance(folderOrDocument));
-
-        FileplanComponentNode resultNode = (FileplanComponentNode) folderOrDocument;
-        assertEquals(false, resultNode.getIsRecordFolder());
-        assertEquals(false, resultNode.getIsFile());
-        assertEquals(false, resultNode.getIsCategory());
-        List allowableOperations = resultNode.getAllowableOperations();
-        assertNull(allowableOperations);
-    }
-
-    @Test
-    public void testGetTransferContainerAllowableOperations() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(nodeRef)).thenReturn(RecordsManagementModel.TYPE_TRANSFER_CONTAINER);
-        when(mockedDictionaryService.isSubClass(RecordsManagementModel.TYPE_TRANSFER_CONTAINER, ContentModel.TYPE_FOLDER)).thenReturn(true);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        List includeParamList = new ArrayList();
-        includeParamList.add(RMNodes.PARAM_INCLUDE_ALLOWABLEOPERATIONS);
-
-        setPermissions(nodeRef, AccessStatus.ALLOWED);
-
-        NodeRef filePlanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(filePlanNodeRef);
-        when(mockedFilePlanService.getTransferContainer(filePlanNodeRef)).thenReturn(nodeRef);
-
-        when(deleteCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-        when(createCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-        when(updateCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-
-        when(mockedFilePlanService.isFilePlanComponent(nodeRef)).thenReturn(true);
-
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        checksAllowedOperations(folderOrDocument, false, true, false);
-    }
-
-    @Test
-    public void testGetHoldContainerAllowableOperations() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(nodeRef)).thenReturn(RecordsManagementModel.TYPE_HOLD_CONTAINER);
-        when(mockedDictionaryService.isSubClass(RecordsManagementModel.TYPE_HOLD_CONTAINER, ContentModel.TYPE_FOLDER)).thenReturn(true);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        List includeParamList = new ArrayList();
-        includeParamList.add(RMNodes.PARAM_INCLUDE_ALLOWABLEOPERATIONS);
-
-        setPermissions(nodeRef, AccessStatus.ALLOWED);
-
-        NodeRef filePlanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(filePlanNodeRef);
-
-        NodeRef transferContainerNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getTransferContainer(filePlanNodeRef)).thenReturn(transferContainerNodeRef);
-
-        when(mockedFilePlanService.getHoldContainer(filePlanNodeRef)).thenReturn(nodeRef);
-
-        when(deleteCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-        when(createCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-        when(updateCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-
-        when(mockedFilePlanService.isFilePlanComponent(nodeRef)).thenReturn(true);
-
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        checksAllowedOperations(folderOrDocument, true, true, false);
-    }
-
-    @Test
-    public void testGetUnfiledContainerAllowableOperations() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(nodeRef)).thenReturn(RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER);
-        when(mockedDictionaryService.isSubClass(RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER, ContentModel.TYPE_FOLDER)).thenReturn(true);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        List includeParamList = new ArrayList();
-        includeParamList.add(RMNodes.PARAM_INCLUDE_ALLOWABLEOPERATIONS);
-
-        setPermissions(nodeRef, AccessStatus.ALLOWED);
-
-        NodeRef filePlanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(filePlanNodeRef);
-
-        NodeRef transferContainerNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getTransferContainer(filePlanNodeRef)).thenReturn(transferContainerNodeRef);
-
-        NodeRef holdContainerNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getHoldContainer(filePlanNodeRef)).thenReturn(holdContainerNodeRef);
-
-        when(mockedFilePlanService.getUnfiledContainer(filePlanNodeRef)).thenReturn(nodeRef);
-
-        when(deleteCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-        when(createCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-        when(updateCapability.evaluate(nodeRef)).thenReturn(AccessDecisionVoter.ACCESS_GRANTED);
-
-        when(mockedFilePlanService.isFilePlanComponent(nodeRef)).thenReturn(true);
-
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        checksAllowedOperations(folderOrDocument, true, true, false);
-    }
-
-    @Test
-    public void testGetNonFileplanComponent() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        QName mockedType = AlfMock.generateQName();
-        when(mockedNodeService.getType(nodeRef)).thenReturn(mockedType);
-        when(mockedDictionaryService.isSubClass(mockedType, ContentModel.TYPE_CMOBJECT)).thenReturn(true);
-        when(mockedDictionaryService.isSubClass(mockedType, ContentModel.TYPE_SYSTEM_FOLDER)).thenReturn(false);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        when(mockedFilePlanService.isFilePlanComponent(nodeRef)).thenReturn(false);
-        List includeParamList = new ArrayList();
-        try
-        {
-            rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-            fail("Expected exception since the requested node is not a fileplan component.");
-        }
-        catch(InvalidParameterException ex)
-        {
-            assertEquals("The provided node is not a fileplan component", ex.getMessage());
-        }
-
-    }
-
-    @Test
-    public void testGetRecordCategory() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(nodeRef)).thenReturn(RecordsManagementModel.TYPE_RECORD_CATEGORY);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        List includeParamList = new ArrayList();
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(RecordCategoryNode.class.isInstance(folderOrDocument));
-
-        RecordCategoryNode resultNode = (RecordCategoryNode) folderOrDocument;
-        assertEquals(false, resultNode.getIsRecordFolder());
-        assertEquals(false, resultNode.getIsFile());
-        assertEquals(true, resultNode.getIsCategory());
-    }
-
-    @Test
-    public void testGetRecordCategoryWithHasRetentionScheduleParam() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(nodeRef)).thenReturn(RecordsManagementModel.TYPE_RECORD_CATEGORY);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        List includeParamList = new ArrayList();
-        includeParamList.add(RMNodes.PARAM_INCLUDE_HAS_RETENTION_SCHEDULE);
-
-        //test has retention schedule true
-        DispositionSchedule mockedDispositionSchedule = mock(DispositionSchedule.class);
-        when(mockedDispositionService.getDispositionSchedule(nodeRef)).thenReturn(mockedDispositionSchedule);
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(RecordCategoryNode.class.isInstance(folderOrDocument));
-
-        RecordCategoryNode resultNode = (RecordCategoryNode) folderOrDocument;
-        assertEquals(false, resultNode.getIsRecordFolder());
-        assertEquals(false, resultNode.getIsFile());
-        assertEquals(true, resultNode.getIsCategory());
-        assertEquals(true, resultNode.getHasRetentionSchedule());
-
-        //test has retention schedule false
-        when(mockedDispositionService.getDispositionSchedule(nodeRef)).thenReturn(null);
-        folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(RecordCategoryNode.class.isInstance(folderOrDocument));
-
-        resultNode = (RecordCategoryNode) folderOrDocument;
-        assertEquals(false, resultNode.getIsRecordFolder());
-        assertEquals(false, resultNode.getIsFile());
-        assertEquals(true, resultNode.getIsCategory());
-        assertEquals(false, resultNode.getHasRetentionSchedule());
-    }
-
-    @Test
-    public void testGetRecordFolder() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(nodeRef)).thenReturn(RecordsManagementModel.TYPE_RECORD_FOLDER);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        List includeParamList = new ArrayList();
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(RecordFolderNode.class.isInstance(folderOrDocument));
-
-        RecordFolderNode resultNode = (RecordFolderNode) folderOrDocument;
-        assertEquals(true, resultNode.getIsRecordFolder());
-        assertEquals(false, resultNode.getIsFile());
-        assertEquals(false, resultNode.getIsCategory());
-    }
-
-    @Test
-    public void testGetRecordFolderWithIsClosedParam() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(nodeRef)).thenReturn(RecordsManagementModel.TYPE_RECORD_FOLDER);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        List includeParamList = new ArrayList();
-        includeParamList.add(RMNodes.PARAM_INCLUDE_IS_CLOSED);
-
-        //check closed record folder
-        when(mockedNodeService.getProperty(nodeRef, RecordsManagementModel.PROP_IS_CLOSED)).thenReturn(true);
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(RecordFolderNode.class.isInstance(folderOrDocument));
-
-        RecordFolderNode resultNode = (RecordFolderNode) folderOrDocument;
-        assertEquals(true, resultNode.getIsRecordFolder());
-        assertEquals(false, resultNode.getIsFile());
-        assertEquals(false, resultNode.getIsCategory());
-        assertEquals(true, resultNode.getIsClosed());
-
-        //check opened record folder
-        when(mockedNodeService.getProperty(nodeRef, RecordsManagementModel.PROP_IS_CLOSED)).thenReturn(false);
-        folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(RecordFolderNode.class.isInstance(folderOrDocument));
-
-        resultNode = (RecordFolderNode) folderOrDocument;
-        assertEquals(true, resultNode.getIsRecordFolder());
-        assertEquals(false, resultNode.getIsFile());
-        assertEquals(false, resultNode.getIsCategory());
-        assertEquals(false, resultNode.getIsClosed());
-    }
-
-    @Test
-    public void testGetRecord() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(nodeRef)).thenReturn(ContentModel.TYPE_CONTENT);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        List includeParamList = new ArrayList();
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(RecordNode.class.isInstance(folderOrDocument));
-
-        RecordNode resultNode = (RecordNode) folderOrDocument;
-        assertEquals(false, resultNode.getIsRecordFolder());
-        assertEquals(true, resultNode.getIsFile());
-        assertEquals(false, resultNode.getIsCategory());
-    }
-
-    @Test
-    public void testGetRecordWithIsCompletedParam() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodeService.getType(nodeRef)).thenReturn(ContentModel.TYPE_CONTENT);
-
-        setupCompanyHomeAndPrimaryParent(nodeRef);
-
-        List includeParamList = new ArrayList();
-
-        includeParamList.add(RMNodes.PARAM_INCLUDE_IS_COMPLETED);
-
-        //test completed record
-        when(mockedNodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_DECLARED_RECORD)).thenReturn(true);
-        Node folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(RecordNode.class.isInstance(folderOrDocument));
-
-        RecordNode resultNode = (RecordNode) folderOrDocument;
-        assertEquals(false, resultNode.getIsRecordFolder());
-        assertEquals(true, resultNode.getIsFile());
-        assertEquals(false, resultNode.getIsCategory());
-        assertEquals(true, resultNode.getIsCompleted());
-
-        //test incomplete record
-        when(mockedNodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_DECLARED_RECORD)).thenReturn(false);
-        folderOrDocument = rmNodesImpl.getFolderOrDocument(nodeRef, null, null, includeParamList, null);
-        assertNotNull(folderOrDocument);
-        assertTrue(RecordNode.class.isInstance(folderOrDocument));
-
-        resultNode = (RecordNode) folderOrDocument;
-        assertEquals(false, resultNode.getIsRecordFolder());
-        assertEquals(true, resultNode.getIsFile());
-        assertEquals(false, resultNode.getIsCategory());
-        assertEquals(false, resultNode.getIsCompleted());
-    }
-
-    @Test
-    public void testValidateNodeWithFilePlanAlias() throws Exception
-    {
-        NodeRef filePlanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(filePlanNodeRef);
-        NodeRef validateOrLookupNode = rmNodesImpl.validateNode(FILE_PLAN_ALIAS);
-        assertEquals(filePlanNodeRef, validateOrLookupNode);
-    }
-
-    @Test
-    public void testValidateNodeWithTransfersAlias() throws Exception
-    {
-        NodeRef filePlanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(filePlanNodeRef);
-
-        NodeRef transferContainerNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getTransferContainer(filePlanNodeRef)).thenReturn(transferContainerNodeRef);
-
-        NodeRef validateOrLookupNode = rmNodesImpl.validateNode(TRANSFERS_ALIAS);
-        assertEquals(transferContainerNodeRef, validateOrLookupNode);
-    }
-
-    @Test
-    public void testValidateNodeWithHoldsAlias() throws Exception
-    {
-        NodeRef filePlanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(filePlanNodeRef);
-
-        NodeRef holdsContainerNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getHoldContainer(filePlanNodeRef)).thenReturn(holdsContainerNodeRef);
-
-        NodeRef validateOrLookupNode = rmNodesImpl.validateNode(HOLDS_ALIAS);
-        assertEquals(holdsContainerNodeRef, validateOrLookupNode);
-    }
-
-    @Test
-    public void testValidateNodeWithUnfiledAlias() throws Exception
-    {
-        NodeRef filePlanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(filePlanNodeRef);
-
-        NodeRef unfiledContainerNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getUnfiledContainer(filePlanNodeRef)).thenReturn(unfiledContainerNodeRef);
-
-        NodeRef validateOrLookupNode = rmNodesImpl.validateNode(UNFILED_ALIAS);
-        assertEquals(unfiledContainerNodeRef, validateOrLookupNode);
-    }
-
-    @Test
-    public void testValidateNodeWithFilePlanAliasRMSiteNotCreated() throws Exception
-    {
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(null);
-
-        try
-        {
-            rmNodesImpl.validateNode(FILE_PLAN_ALIAS);
-            fail("Expected exception as RM site is not created.");
-        }
-        catch(EntityNotFoundException ex)
-        {
-            //it is ok since exception is thrown
-        }
-    }
-
-    @Test
-    public void testValidateNodeWithTransferAliasRMSiteNotCreated() throws Exception
-    {
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(null);
-
-        try
-        {
-            rmNodesImpl.validateNode(TRANSFERS_ALIAS);
-            fail("Expected exception as RM site is not created.");
-        }
-        catch(EntityNotFoundException ex)
-        {
-            //it is ok since exception is thrown
-        }
-    }
-
-    @Test
-    public void testValidateNodeWithHoldsAliasRMSiteNotCreated() throws Exception
-    {
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(null);
-
-        try
-        {
-            rmNodesImpl.validateNode(HOLDS_ALIAS);
-            fail("Expected exception as RM site is not created.");
-        }
-        catch(EntityNotFoundException ex)
-        {
-            //it is ok since exception is thrown
-        }
-    }
-
-    @Test
-    public void testValidateNodeWithUnfiledAliasRMSiteNotCreated() throws Exception
-    {
-        when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(null);
-
-        try
-        {
-            rmNodesImpl.validateNode(UNFILED_ALIAS);
-            fail("Expected exception as RM site is not created.");
-        }
-        catch(EntityNotFoundException ex)
-        {
-            //it is ok since exception is thrown
-        }
-    }
-
-    @Test
-    public void testValidateNodeNullNodeRef() throws Exception
-    {
-        try
-        {
-            rmNodesImpl.validateNode((String)null);
-            fail("Expected exception as nodId should not be null or empty.");
-        }
-        catch(IllegalArgumentException ex)
-        {
-            assertEquals("nodeId is a mandatory parameter", ex.getMessage());
-        }
-    }
-
-    @Test
-    public void testValidateNode() throws Exception
-    {
-        NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        NodeRef validateOrLookupNode = rmNodesImpl.validateNode(nodeRef.getId());
-        assertEquals(nodeRef, validateOrLookupNode);
-    }
-
-    private void setupCompanyHomeAndPrimaryParent(NodeRef nodeRef)
-    {
-        NodeRef companyHomeNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedRepositoryHelper.getCompanyHome()).thenReturn(companyHomeNodeRef);
-        NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
-        ChildAssociationRef mockedChildAssoc = mock(ChildAssociationRef.class);
-        when(mockedChildAssoc.getParentRef()).thenReturn(parentNodeRef);
-        when(mockedNodeService.getPrimaryParent(nodeRef)).thenReturn(mockedChildAssoc);
-    }
-
-    private void setPermissions(NodeRef nodeRef, AccessStatus permissionToSet)
-    {
-        when(mockedPermissionService.hasPermission(nodeRef, PermissionService.WRITE)).thenReturn(permissionToSet);
-        when(mockedPermissionService.hasPermission(nodeRef, PermissionService.DELETE)).thenReturn(permissionToSet);
-        when(mockedPermissionService.hasPermission(nodeRef, PermissionService.ADD_CHILDREN)).thenReturn(permissionToSet);
-    }
-
-    private void checksAllowedOperations(Node containerNode, boolean allowCreate, boolean allowUpdate, boolean allowDelete)
-    {
-        assertNotNull(containerNode);
-        assertTrue(FileplanComponentNode.class.isInstance(containerNode));
-        FileplanComponentNode resultNode = (FileplanComponentNode) containerNode;
-        List allowableOperations = resultNode.getAllowableOperations();
-
-        assertEquals("Create operation should " + (allowCreate?"":"not ") + "be available for provided container.", 
-                allowCreate, 
-                allowableOperations.contains(RMNodes.OP_CREATE));
-  
-        assertEquals("Update operation should " + (allowCreate?"":"not ") + "be available for provided container.", 
-                allowUpdate, 
-                allowableOperations.contains(RMNodes.OP_UPDATE));
-
-        assertEquals("Delete operation should " + (allowCreate?"":"not ") + "be available for provided container.", 
-                allowDelete, 
-                allowableOperations.contains(RMNodes.OP_DELETE));
-    }
-}
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMSitesImplUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMSitesImplUnitTest.java
index 2e7366a152..5b7a06506c 100644
--- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMSitesImplUnitTest.java
+++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RMSitesImplUnitTest.java
@@ -43,10 +43,11 @@ import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
 import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
 import org.alfresco.repo.security.authentication.AuthenticationUtil;
 import org.alfresco.rest.api.impl.SiteImportPackageHandler;
+import org.alfresco.rest.api.model.Site;
+import org.alfresco.rest.api.model.SiteUpdate;
 import org.alfresco.rest.framework.resource.parameters.Parameters;
 import org.alfresco.rm.rest.api.model.RMSite;
 import org.alfresco.rm.rest.api.model.RMSiteCompliance;
-import org.alfresco.rm.rest.api.model.SiteUpdate;
 import org.alfresco.service.cmr.favourites.FavouritesService;
 import org.alfresco.service.cmr.repository.NodeRef;
 import org.alfresco.service.cmr.site.SiteInfo;
@@ -259,6 +260,8 @@ public class RMSitesImplUnitTest  extends BaseUnitTest
         when(mockedSiteUpdate.getDescription()).thenReturn(RM_SITE_DESCRIPTION_AFTER_UPDATE);
         when(mockedSiteUpdate.getTitle()).thenReturn(RM_SITE_TITLE_AFTER_UPDATE);
         when(mockedSiteUpdate.getVisibility()).thenReturn(null);
+        when(mockedSiteUpdate.wasSet(Site.TITLE)).thenReturn(true);
+        when(mockedSiteUpdate.wasSet(Site.DESCRIPTION)).thenReturn(true);
 
         //mock Parameters
         Parameters mockedParameters = mock(Parameters.class);
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RecordsImplUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RecordsImplUnitTest.java
index c10b272441..9d41921dd6 100644
--- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RecordsImplUnitTest.java
+++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/impl/RecordsImplUnitTest.java
@@ -41,8 +41,6 @@ import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
 import org.alfresco.module.org_alfresco_module_rm.test.util.MockAuthenticationUtilHelper;
 import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
 import org.alfresco.rest.framework.resource.parameters.Parameters;
-import org.alfresco.rm.rest.api.RMNodes;
-import org.alfresco.rm.rest.api.Records;
 import org.alfresco.rm.rest.api.model.TargetContainer;
 import org.alfresco.service.cmr.dictionary.DictionaryService;
 import org.alfresco.service.cmr.model.FileFolderService;
@@ -64,274 +62,274 @@ import org.mockito.MockitoAnnotations;
  */
 public class RecordsImplUnitTest
 {
-    @Mock
-    private RecordService mockedRecordService;
-    @Mock
-    protected FilePlanService mockedFilePlanService;
-    @Mock
-    protected NodeService mockedNodeService;
-    @Mock
-    protected FileFolderService mockedFileFolderService;
-    @Mock
-    protected DictionaryService mockedDictionaryService;
-    @Mock
-    protected AuthenticationUtil mockedAuthenticationUtil;
-    @Mock
-    protected RMNodes mockedNodes;
-
-    @InjectMocks
-    private RecordsImpl recordsImpl;
-
-    @Before
-    public void before()
-    {
-        MockitoAnnotations.initMocks(this);
-
-        // setup mocked authentication util
-        MockAuthenticationUtilHelper.setup(mockedAuthenticationUtil);
-    }
-
-    /**
-     * Given a file and an existing fileplan
-     * When declaring a file as record
-     * Then a record is created under the existing fileplan from the provided file
-     */
-    @Test
-    public void testDeclareFileAsRecord()
-    {
-        /*
-         * Given a file and an existing fileplan
-         */
-        NodeRef mockedFile = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodes.validateNode(mockedFile.getId())).thenReturn(mockedFile);
-
-        NodeRef fileplan = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID)).thenReturn(fileplan);
-
-        /*
-         * When declare the file as record
-         */
-        Parameters params = Mockito.mock(Parameters.class);
-        when(params.getParameter(Records.PARAM_HIDE_RECORD)).thenReturn("true");
-        recordsImpl.declareFileAsRecord(mockedFile.getId(), params);
-
-        /*
-         * Then a record is created under the existing fileplan from the provided file
-         */
-        verify(mockedRecordService).createRecord(fileplan, mockedFile, false);
-        verify(mockedNodes).getFolderOrDocument(mockedFile.getId(), params);
-    }
-
-    /**
-     * Given a record
-     * When trying to filing a record providing a blank destination path
-     * Then an InvalidParameterException is thrown
-     */
-    @Test(expected=InvalidParameterException.class)
-    public void testFileRecord_BlankDestinationPath()
-    {
-        /*
-         * Given a record
-         */
-        NodeRef mockedRecord = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodes.validateNode(mockedRecord.getId())).thenReturn(mockedRecord);
-
-        /*
-         * When trying to filing a record providing a blank destination path
-         */
-        Parameters params = Mockito.mock(Parameters.class);
-        TargetContainer target = new TargetContainer();
-        recordsImpl.fileOrLinkRecord(mockedRecord.getId(), target, params);
-    }
-
-    /**
-     * Given an unfiled record and an existing record folder
-     * When trying to file the record in the record folder
-     * Then the record is moved under the destination folder
-     */
-    @Test
-    public void testFileRecord_DestinationById() throws Exception
-    {
-        /*
-         * Given an unfiled record and an existing record folder
-         */
-        // mock the record to file
-        NodeRef mockedRecord = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodes.validateNode(mockedRecord.getId())).thenReturn(mockedRecord);
-
-        // mock the current primary parent
-        NodeRef mockedPrimaryParent = AlfMock.generateNodeRef(mockedNodeService);
-        ChildAssociationRef mockedChildAssoc = mock(ChildAssociationRef.class);
-        when(mockedChildAssoc.getParentRef()).thenReturn(mockedPrimaryParent);
-        when(mockedNodeService.getPrimaryParent(mockedRecord)).thenReturn(mockedChildAssoc);
-        when(mockedNodeService.getType(mockedPrimaryParent)).thenReturn(TYPE_UNFILED_RECORD_CONTAINER);
-        when(mockedDictionaryService.isSubClass(TYPE_UNFILED_RECORD_CONTAINER, TYPE_RECORD_FOLDER)).thenReturn(false);
-
-        // mock the target record folder to file the record into
-        NodeRef mockedTargetRecordFolder = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodes.getOrCreatePath(mockedTargetRecordFolder.getId(), null, TYPE_CONTENT)).thenReturn(mockedTargetRecordFolder);
-        when(mockedNodeService.getType(mockedTargetRecordFolder)).thenReturn(TYPE_RECORD_FOLDER);
-        when(mockedDictionaryService.isSubClass(TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER)).thenReturn(true);
-
-        /*
-         * When trying to file the record in the record folder
-         */
-        TargetContainer destination = new TargetContainer();
-        destination.setTargetParentId(mockedTargetRecordFolder.getId());
-
-        Parameters params = Mockito.mock(Parameters.class);
-        recordsImpl.fileOrLinkRecord(mockedRecord.getId(), destination, params);
-
-        /*
-         * Then the record is moved under the destination folder
-         */
-        verify(mockedNodes).getOrCreatePath(mockedTargetRecordFolder.getId(), null, TYPE_CONTENT);
-        verify(mockedFileFolderService).moveFrom(mockedRecord, mockedPrimaryParent, mockedTargetRecordFolder, null);
-    }
-
-    /**
-     * Given an unfiled record 
-     *   and an existing record folder with relative path category/recordFolder from fileplan
-     * When trying to file the record using a relative path and no target id
-     * Then the record is moved under the destination folder relative to the fileplan
-     */
-    @Test
-    public void testFileRecord_DestinationRelativeToFileplan() throws Exception
-    {
-        /*
-         * Given an unfiled record and an existing record folder
-         */
-        // mock the record to file
-        NodeRef mockedRecord = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodes.validateNode(mockedRecord.getId())).thenReturn(mockedRecord);
-
-        // mock the current primary parent
-        NodeRef mockedPrimaryParent = AlfMock.generateNodeRef(mockedNodeService);
-        ChildAssociationRef mockedChildAssoc = mock(ChildAssociationRef.class);
-        when(mockedChildAssoc.getParentRef()).thenReturn(mockedPrimaryParent);
-        when(mockedNodeService.getPrimaryParent(mockedRecord)).thenReturn(mockedChildAssoc);
-        when(mockedNodeService.getType(mockedPrimaryParent)).thenReturn(TYPE_UNFILED_RECORD_CONTAINER);
-        when(mockedDictionaryService.isSubClass(TYPE_UNFILED_RECORD_CONTAINER, TYPE_RECORD_FOLDER)).thenReturn(false);
-
-        // mock the fileplan
-        NodeRef fileplan = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedFilePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID)).thenReturn(fileplan);
-
-        // mock the target record folder to file the record into
-        String relativePath = "category/recordFolder";
-        NodeRef mockedTargetRecordFolder = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodes.getOrCreatePath(fileplan.getId(), relativePath, TYPE_CONTENT)).thenReturn(mockedTargetRecordFolder);
-        when(mockedNodeService.getType(mockedTargetRecordFolder)).thenReturn(TYPE_RECORD_FOLDER);
-        when(mockedDictionaryService.isSubClass(TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER)).thenReturn(true);
-
-        /*
-         * When trying to file the record using a relative path and no target id
-         */
-        TargetContainer destination = new TargetContainer();
-        destination.setRelativePath(relativePath);
-
-        Parameters params = Mockito.mock(Parameters.class);
-        recordsImpl.fileOrLinkRecord(mockedRecord.getId(), destination, params);
-
-        /*
-         * Then the record is moved under the destination folder relative to the fileplan
-         */
-        verify(mockedNodes).getOrCreatePath(fileplan.getId(), relativePath, TYPE_CONTENT);
-        verify(mockedFileFolderService).moveFrom(mockedRecord, mockedPrimaryParent, mockedTargetRecordFolder, null);
-    }
-
-    /**
-     * Given an unfiled record 
-     *   and an existing record folder with relative path category/recordFolder from a given category
-     * When trying to file the record describing the target folder with the category id and the relative path
-     * Then the record is moved under the correct destination folder
-     */
-    @Test
-    public void testFileRecord_DestinationRelativeToProvidedId() throws Exception
-    {
-        /*
-         * Given an unfiled record and an existing record folder with relative path category/recordFolder from a given category
-         */
-        // mock the record to file
-        NodeRef mockedRecord = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodes.validateNode(mockedRecord.getId())).thenReturn(mockedRecord);
-
-        // mock the current primary parent
-        NodeRef mockedPrimaryParent = AlfMock.generateNodeRef(mockedNodeService);
-        ChildAssociationRef mockedChildAssoc = mock(ChildAssociationRef.class);
-        when(mockedChildAssoc.getParentRef()).thenReturn(mockedPrimaryParent);
-        when(mockedNodeService.getPrimaryParent(mockedRecord)).thenReturn(mockedChildAssoc);
-        when(mockedNodeService.getType(mockedPrimaryParent)).thenReturn(TYPE_UNFILED_RECORD_CONTAINER);
-        when(mockedDictionaryService.isSubClass(TYPE_UNFILED_RECORD_CONTAINER, TYPE_RECORD_FOLDER)).thenReturn(false);
-
-        // mock the target category
-        NodeRef mockedTargetCategory = AlfMock.generateNodeRef(mockedNodeService);
-
-        // mock the target record folder to file the record into
-        String relativePath = "category/recordFolder";
-        NodeRef mockedTargetRecordFolder = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodes.getOrCreatePath(mockedTargetCategory.getId(), relativePath, TYPE_CONTENT)).thenReturn(mockedTargetRecordFolder);
-        when(mockedNodeService.getType(mockedTargetRecordFolder)).thenReturn(TYPE_RECORD_FOLDER);
-        when(mockedDictionaryService.isSubClass(TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER)).thenReturn(true);
-
-        /*
-         *  When trying to file the record describing the target folder with the category id and the relative path
-         */
-        TargetContainer destination = new TargetContainer();
-        destination.setTargetParentId(mockedTargetCategory.getId());
-        destination.setRelativePath(relativePath);
-
-        Parameters params = Mockito.mock(Parameters.class);
-        recordsImpl.fileOrLinkRecord(mockedRecord.getId(), destination, params);
-
-        /*
-         * Then the record is moved under the correct destination folder
-         */
-        verify(mockedNodes).getOrCreatePath(mockedTargetCategory.getId(), relativePath, TYPE_CONTENT);
-        verify(mockedFileFolderService).moveFrom(mockedRecord, mockedPrimaryParent, mockedTargetRecordFolder, null);
-    }
-
-    /**
-     * Given an filed record and an existing record folder
-     * When trying to link the record to the record folder
-     * Then the record is linked to the destination folder
-     */
-    @Test
-    public void testLinkRecord()
-    {
-        /*
-         * Given an filed record and an existing record folder
-         */
-        // mock the record to link
-        NodeRef mockedRecord = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodes.validateNode(mockedRecord.getId())).thenReturn(mockedRecord);
-
-        // mock the current primary parent
-        NodeRef mockedPrimaryParent = AlfMock.generateNodeRef(mockedNodeService);
-        ChildAssociationRef mockedChildAssoc = mock(ChildAssociationRef.class);
-        when(mockedChildAssoc.getParentRef()).thenReturn(mockedPrimaryParent);
-        when(mockedNodeService.getPrimaryParent(mockedRecord)).thenReturn(mockedChildAssoc);
-        when(mockedNodeService.getType(mockedPrimaryParent)).thenReturn(TYPE_RECORD_FOLDER);
-        when(mockedDictionaryService.isSubClass(TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER)).thenReturn(true);
-
-        // mock the target record folder to file the record into
-        NodeRef mockedTargetRecordFolder = AlfMock.generateNodeRef(mockedNodeService);
-        when(mockedNodes.getOrCreatePath(mockedTargetRecordFolder.getId(), null, TYPE_CONTENT)).thenReturn(mockedTargetRecordFolder);
-        when(mockedNodeService.getType(mockedTargetRecordFolder)).thenReturn(TYPE_RECORD_FOLDER);
-
-        /*
-         * When trying to link the record to the record folder
-         */
-        TargetContainer destination = new TargetContainer();
-        destination.setTargetParentId(mockedTargetRecordFolder.getId());
-
-        Parameters params = Mockito.mock(Parameters.class);
-        recordsImpl.fileOrLinkRecord(mockedRecord.getId(), destination, params);
-
-        /*
-         * Then the record is linked to the destination folder
-         */
-        verify(mockedNodes).getOrCreatePath(mockedTargetRecordFolder.getId(), null, TYPE_CONTENT);
-        verify(mockedRecordService).link(mockedRecord, mockedTargetRecordFolder);
-    }
+//    @Mock
+//    private RecordService mockedRecordService;
+//    @Mock
+//    protected FilePlanService mockedFilePlanService;
+//    @Mock
+//    protected NodeService mockedNodeService;
+//    @Mock
+//    protected FileFolderService mockedFileFolderService;
+//    @Mock
+//    protected DictionaryService mockedDictionaryService;
+//    @Mock
+//    protected AuthenticationUtil mockedAuthenticationUtil;
+//    @Mock
+//    protected RMNodes mockedNodes;
+//
+//    @InjectMocks
+//    private RecordsImpl recordsImpl;
+//
+//    @Before
+//    public void before()
+//    {
+//        MockitoAnnotations.initMocks(this);
+//
+//        // setup mocked authentication util
+//        MockAuthenticationUtilHelper.setup(mockedAuthenticationUtil);
+//    }
+//
+//    /**
+//     * Given a file and an existing fileplan
+//     * When declaring a file as record
+//     * Then a record is created under the existing fileplan from the provided file
+//     */
+//    @Test
+//    public void testDeclareFileAsRecord()
+//    {
+//        /*
+//         * Given a file and an existing fileplan
+//         */
+//        NodeRef mockedFile = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedNodes.validateNode(mockedFile.getId())).thenReturn(mockedFile);
+//
+//        NodeRef fileplan = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedFilePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID)).thenReturn(fileplan);
+//
+//        /*
+//         * When declare the file as record
+//         */
+//        Parameters params = Mockito.mock(Parameters.class);
+//        when(params.getParameter(Records.PARAM_HIDE_RECORD)).thenReturn("true");
+//        recordsImpl.declareFileAsRecord(mockedFile.getId(), params);
+//
+//        /*
+//         * Then a record is created under the existing fileplan from the provided file
+//         */
+//        verify(mockedRecordService).createRecord(fileplan, mockedFile, false);
+//        verify(mockedNodes).getFolderOrDocument(mockedFile.getId(), params);
+//    }
+//
+//    /**
+//     * Given a record
+//     * When trying to filing a record providing a blank destination path
+//     * Then an InvalidParameterException is thrown
+//     */
+//    @Test(expected=InvalidParameterException.class)
+//    public void testFileRecord_BlankDestinationPath()
+//    {
+//        /*
+//         * Given a record
+//         */
+//        NodeRef mockedRecord = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedNodes.validateNode(mockedRecord.getId())).thenReturn(mockedRecord);
+//
+//        /*
+//         * When trying to filing a record providing a blank destination path
+//         */
+//        Parameters params = Mockito.mock(Parameters.class);
+//        TargetContainer target = new TargetContainer();
+//        recordsImpl.fileOrLinkRecord(mockedRecord.getId(), target, params);
+//    }
+//
+//    /**
+//     * Given an unfiled record and an existing record folder
+//     * When trying to file the record in the record folder
+//     * Then the record is moved under the destination folder
+//     */
+//    @Test
+//    public void testFileRecord_DestinationById() throws Exception
+//    {
+//        /*
+//         * Given an unfiled record and an existing record folder
+//         */
+//        // mock the record to file
+//        NodeRef mockedRecord = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedNodes.validateNode(mockedRecord.getId())).thenReturn(mockedRecord);
+//
+//        // mock the current primary parent
+//        NodeRef mockedPrimaryParent = AlfMock.generateNodeRef(mockedNodeService);
+//        ChildAssociationRef mockedChildAssoc = mock(ChildAssociationRef.class);
+//        when(mockedChildAssoc.getParentRef()).thenReturn(mockedPrimaryParent);
+//        when(mockedNodeService.getPrimaryParent(mockedRecord)).thenReturn(mockedChildAssoc);
+//        when(mockedNodeService.getType(mockedPrimaryParent)).thenReturn(TYPE_UNFILED_RECORD_CONTAINER);
+//        when(mockedDictionaryService.isSubClass(TYPE_UNFILED_RECORD_CONTAINER, TYPE_RECORD_FOLDER)).thenReturn(false);
+//
+//        // mock the target record folder to file the record into
+//        NodeRef mockedTargetRecordFolder = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedNodes.getOrCreatePath(mockedTargetRecordFolder.getId(), null, TYPE_CONTENT)).thenReturn(mockedTargetRecordFolder);
+//        when(mockedNodeService.getType(mockedTargetRecordFolder)).thenReturn(TYPE_RECORD_FOLDER);
+//        when(mockedDictionaryService.isSubClass(TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER)).thenReturn(true);
+//
+//        /*
+//         * When trying to file the record in the record folder
+//         */
+//        TargetContainer destination = new TargetContainer();
+//        destination.setTargetParentId(mockedTargetRecordFolder.getId());
+//
+//        Parameters params = Mockito.mock(Parameters.class);
+//        recordsImpl.fileOrLinkRecord(mockedRecord.getId(), destination, params);
+//
+//        /*
+//         * Then the record is moved under the destination folder
+//         */
+//        verify(mockedNodes).getOrCreatePath(mockedTargetRecordFolder.getId(), null, TYPE_CONTENT);
+//        verify(mockedFileFolderService).moveFrom(mockedRecord, mockedPrimaryParent, mockedTargetRecordFolder, null);
+//    }
+//
+//    /**
+//     * Given an unfiled record 
+//     *   and an existing record folder with relative path category/recordFolder from fileplan
+//     * When trying to file the record using a relative path and no target id
+//     * Then the record is moved under the destination folder relative to the fileplan
+//     */
+//    @Test
+//    public void testFileRecord_DestinationRelativeToFileplan() throws Exception
+//    {
+//        /*
+//         * Given an unfiled record and an existing record folder
+//         */
+//        // mock the record to file
+//        NodeRef mockedRecord = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedNodes.validateNode(mockedRecord.getId())).thenReturn(mockedRecord);
+//
+//        // mock the current primary parent
+//        NodeRef mockedPrimaryParent = AlfMock.generateNodeRef(mockedNodeService);
+//        ChildAssociationRef mockedChildAssoc = mock(ChildAssociationRef.class);
+//        when(mockedChildAssoc.getParentRef()).thenReturn(mockedPrimaryParent);
+//        when(mockedNodeService.getPrimaryParent(mockedRecord)).thenReturn(mockedChildAssoc);
+//        when(mockedNodeService.getType(mockedPrimaryParent)).thenReturn(TYPE_UNFILED_RECORD_CONTAINER);
+//        when(mockedDictionaryService.isSubClass(TYPE_UNFILED_RECORD_CONTAINER, TYPE_RECORD_FOLDER)).thenReturn(false);
+//
+//        // mock the fileplan
+//        NodeRef fileplan = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedFilePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID)).thenReturn(fileplan);
+//
+//        // mock the target record folder to file the record into
+//        String relativePath = "category/recordFolder";
+//        NodeRef mockedTargetRecordFolder = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedNodes.getOrCreatePath(fileplan.getId(), relativePath, TYPE_CONTENT)).thenReturn(mockedTargetRecordFolder);
+//        when(mockedNodeService.getType(mockedTargetRecordFolder)).thenReturn(TYPE_RECORD_FOLDER);
+//        when(mockedDictionaryService.isSubClass(TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER)).thenReturn(true);
+//
+//        /*
+//         * When trying to file the record using a relative path and no target id
+//         */
+//        TargetContainer destination = new TargetContainer();
+//        destination.setRelativePath(relativePath);
+//
+//        Parameters params = Mockito.mock(Parameters.class);
+//        recordsImpl.fileOrLinkRecord(mockedRecord.getId(), destination, params);
+//
+//        /*
+//         * Then the record is moved under the destination folder relative to the fileplan
+//         */
+//        verify(mockedNodes).getOrCreatePath(fileplan.getId(), relativePath, TYPE_CONTENT);
+//        verify(mockedFileFolderService).moveFrom(mockedRecord, mockedPrimaryParent, mockedTargetRecordFolder, null);
+//    }
+//
+//    /**
+//     * Given an unfiled record 
+//     *   and an existing record folder with relative path category/recordFolder from a given category
+//     * When trying to file the record describing the target folder with the category id and the relative path
+//     * Then the record is moved under the correct destination folder
+//     */
+//    @Test
+//    public void testFileRecord_DestinationRelativeToProvidedId() throws Exception
+//    {
+//        /*
+//         * Given an unfiled record and an existing record folder with relative path category/recordFolder from a given category
+//         */
+//        // mock the record to file
+//        NodeRef mockedRecord = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedNodes.validateNode(mockedRecord.getId())).thenReturn(mockedRecord);
+//
+//        // mock the current primary parent
+//        NodeRef mockedPrimaryParent = AlfMock.generateNodeRef(mockedNodeService);
+//        ChildAssociationRef mockedChildAssoc = mock(ChildAssociationRef.class);
+//        when(mockedChildAssoc.getParentRef()).thenReturn(mockedPrimaryParent);
+//        when(mockedNodeService.getPrimaryParent(mockedRecord)).thenReturn(mockedChildAssoc);
+//        when(mockedNodeService.getType(mockedPrimaryParent)).thenReturn(TYPE_UNFILED_RECORD_CONTAINER);
+//        when(mockedDictionaryService.isSubClass(TYPE_UNFILED_RECORD_CONTAINER, TYPE_RECORD_FOLDER)).thenReturn(false);
+//
+//        // mock the target category
+//        NodeRef mockedTargetCategory = AlfMock.generateNodeRef(mockedNodeService);
+//
+//        // mock the target record folder to file the record into
+//        String relativePath = "category/recordFolder";
+//        NodeRef mockedTargetRecordFolder = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedNodes.getOrCreatePath(mockedTargetCategory.getId(), relativePath, TYPE_CONTENT)).thenReturn(mockedTargetRecordFolder);
+//        when(mockedNodeService.getType(mockedTargetRecordFolder)).thenReturn(TYPE_RECORD_FOLDER);
+//        when(mockedDictionaryService.isSubClass(TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER)).thenReturn(true);
+//
+//        /*
+//         *  When trying to file the record describing the target folder with the category id and the relative path
+//         */
+//        TargetContainer destination = new TargetContainer();
+//        destination.setTargetParentId(mockedTargetCategory.getId());
+//        destination.setRelativePath(relativePath);
+//
+//        Parameters params = Mockito.mock(Parameters.class);
+//        recordsImpl.fileOrLinkRecord(mockedRecord.getId(), destination, params);
+//
+//        /*
+//         * Then the record is moved under the correct destination folder
+//         */
+//        verify(mockedNodes).getOrCreatePath(mockedTargetCategory.getId(), relativePath, TYPE_CONTENT);
+//        verify(mockedFileFolderService).moveFrom(mockedRecord, mockedPrimaryParent, mockedTargetRecordFolder, null);
+//    }
+//
+//    /**
+//     * Given an filed record and an existing record folder
+//     * When trying to link the record to the record folder
+//     * Then the record is linked to the destination folder
+//     */
+//    @Test
+//    public void testLinkRecord()
+//    {
+//        /*
+//         * Given an filed record and an existing record folder
+//         */
+//        // mock the record to link
+//        NodeRef mockedRecord = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedNodes.validateNode(mockedRecord.getId())).thenReturn(mockedRecord);
+//
+//        // mock the current primary parent
+//        NodeRef mockedPrimaryParent = AlfMock.generateNodeRef(mockedNodeService);
+//        ChildAssociationRef mockedChildAssoc = mock(ChildAssociationRef.class);
+//        when(mockedChildAssoc.getParentRef()).thenReturn(mockedPrimaryParent);
+//        when(mockedNodeService.getPrimaryParent(mockedRecord)).thenReturn(mockedChildAssoc);
+//        when(mockedNodeService.getType(mockedPrimaryParent)).thenReturn(TYPE_RECORD_FOLDER);
+//        when(mockedDictionaryService.isSubClass(TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER)).thenReturn(true);
+//
+//        // mock the target record folder to file the record into
+//        NodeRef mockedTargetRecordFolder = AlfMock.generateNodeRef(mockedNodeService);
+//        when(mockedNodes.getOrCreatePath(mockedTargetRecordFolder.getId(), null, TYPE_CONTENT)).thenReturn(mockedTargetRecordFolder);
+//        when(mockedNodeService.getType(mockedTargetRecordFolder)).thenReturn(TYPE_RECORD_FOLDER);
+//
+//        /*
+//         * When trying to link the record to the record folder
+//         */
+//        TargetContainer destination = new TargetContainer();
+//        destination.setTargetParentId(mockedTargetRecordFolder.getId());
+//
+//        Parameters params = Mockito.mock(Parameters.class);
+//        recordsImpl.fileOrLinkRecord(mockedRecord.getId(), destination, params);
+//
+//        /*
+//         * Then the record is linked to the destination folder
+//         */
+//        verify(mockedNodes).getOrCreatePath(mockedTargetRecordFolder.getId(), null, TYPE_CONTENT);
+//        verify(mockedRecordService).link(mockedRecord, mockedTargetRecordFolder);
+//    }
 }
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/sites/RMSiteEntityResourceUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/sites/RMSiteEntityResourceUnitTest.java
index 5d69412c81..73474215ac 100644
--- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/sites/RMSiteEntityResourceUnitTest.java
+++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/rm/rest/api/sites/RMSiteEntityResourceUnitTest.java
@@ -30,26 +30,6 @@ package org.alfresco.rm.rest.api.sites;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
-
-import java.security.InvalidParameterException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
-import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
-import org.alfresco.rest.framework.resource.parameters.Parameters;
-import org.alfresco.rest.framework.resource.parameters.Params;
-import org.alfresco.rm.rest.api.RMSites;
-import org.alfresco.rm.rest.api.model.RMSite;
-import org.alfresco.rm.rest.api.model.RMSiteCompliance;
-import org.alfresco.rm.rest.api.model.SiteUpdate;
-import org.alfresco.service.cmr.site.SiteVisibility;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -57,6 +37,25 @@ import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
+import org.alfresco.rest.api.model.SiteUpdate;
+import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rest.framework.resource.parameters.Params;
+import org.alfresco.rm.rest.api.RMSites;
+import org.alfresco.rm.rest.api.model.RMSite;
+import org.alfresco.rm.rest.api.model.RMSiteCompliance;
+import org.alfresco.service.cmr.site.SiteVisibility;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
 /**
  * Unit Test class for RMSiteEntityResource.
  *
diff --git a/rm-community/rm-community-rest-api-explorer/pom.xml b/rm-community/rm-community-rest-api-explorer/pom.xml
index 404e57bbee..e771a9ff00 100644
--- a/rm-community/rm-community-rest-api-explorer/pom.xml
+++ b/rm-community/rm-community-rest-api-explorer/pom.xml
@@ -11,7 +11,7 @@
    
 
    
-      1.4
+      5.2.0.1
       1.8
       1.8
       UTF-8
diff --git a/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml b/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml
new file mode 100644
index 0000000000..070ee6efdd
--- /dev/null
+++ b/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml
@@ -0,0 +1,3691 @@
+swagger: '2.0'
+info:
+  description: |
+    **GS Core API**
+
+    Provides access to the core features of Governance Services.
+  version: '1'
+  title: Alfresco Governance Services REST API
+basePath: /alfresco/api/-default-/public/gs/versions/1
+securityDefinitions:
+  basicAuth:
+    type: basic
+    description: HTTP Basic Authentication
+security:
+  - basicAuth: []
+consumes:
+  - application/json
+produces:
+  - application/json
+tags:
+  - name: file-plans
+    description: Retrieve and manage file plans
+  - name: record-categories
+    description: Retrieve and manage record categories
+  - name: record-folders
+    description: Retrieve and manage record folders
+  - name: gs-sites
+    description: Retrieve and manage the RM site
+  - name: records
+    description: Perform record specific operations
+  - name: files
+    description: Perform operations on non-record files
+  - name: transfer-containers
+    description: Retrieve and manage transfer containers
+  - name: transfers
+    description: Retrieve and manage transfers
+  - name: unfiled-containers
+    description: Retrieve and manage unfiled records containers
+  - name: unfiled-record-folders
+    description: Retrieve and manage unfiled record folders
+paths:
+  ## GS sites
+  '/gs-sites':
+    post:
+      tags:
+        - gs-sites
+      summary: Create the RM site
+      description: |
+        **Note:** this endpoint is available in RM 2.6 and newer versions.
+
+        Creates the RM site with the given details.
+
+        **Note:** the id of a site cannot be updated once the site has been created.
+
+        For example, to create the RM site named "Records Management Title" with "Records Management Description" as description, the following body could be used:
+        ```JSON
+        {
+          "title": "Records Management Title",
+          "description": "Records Management Description"
+        }
+        ```
+
+        The creator will be added as a member with Site Manager role.
+
+        When you create the RM site, the **filePlan** structure is also created.
+
+      operationId: createRMSite
+      produces:
+        - application/json
+      parameters:
+        - name: skipAddToFavorites
+          in: query
+          description: Flag to indicate whether the RM site should not be added to the user's site favorites.
+          type: boolean
+          default: false
+        - in: body
+          name: siteBodyCreate
+          description: The site details
+          required: true
+          schema:
+            $ref: '#/definitions/RMSiteBodyCreate'
+      responses:
+        '201':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RMSiteEntry'
+        '400':
+          description: |
+            Invalid parameter: **title**, or **description** exceed the maximum length;
+            or **siteBodyCreate** invalid
+        '401':
+          description: Authentication failed
+        '409':
+          description: Site with the given identifier already exists
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  '/gs-sites/rm':
+    get:
+      tags:
+        - gs-sites
+      summary: Get the RM site
+      description: |
+        **Note:** this endpoint is available in RM 2.6 and newer versions.
+
+        Gets information for RM site.
+
+      operationId: getRMSite
+      produces:
+        - application/json
+      parameters:
+        - $ref: '#/parameters/fieldsParam'
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RMSiteEntry'
+        '400':
+          description: |
+            Invalid parameter: GET request is suported only for the RM site
+        '401':
+          description: Authentication failed
+        '404':
+          description: |
+            RM site does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    delete:
+      tags:
+        - gs-sites
+      summary: Delete the RM site
+      description: |
+        **Note:** this endpoint is available in RM 2.6 and newer versions.
+
+        Deletes the RM site.
+      operationId: deleteRMSite
+      produces:
+        - application/json
+      responses:
+        '204':
+          description: Successful response
+        '400':
+          description: |
+            Invalid parameter: DELETE request is suported only for the RM site
+        '401':
+          description: Authentication failed
+        '403':
+          description: Current user does not have permission to delete the site that is visible to them.
+        '404':
+          description: |
+            RM site does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    put:
+      tags:
+        - gs-sites
+      summary: Update the RM site
+      description: |
+        **Note:** this endpoint is available in RM 2.6 and newer versions.
+
+        Update the details for the RM site. Site Manager or otherwise a
+        (site) admin can update title or description.
+
+        **Note**: the id, site visibility or compliance of the RM site cannot be updated once the RM site has been created.
+
+      operationId: updateRMSite
+      produces:
+        - application/json
+      parameters:
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: siteBodyUpdate
+          description: The RM site information to update.
+          required: true
+          schema:
+            $ref: '#/definitions/RMSiteBodyUpdate'
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RMSiteEntry'
+        '400':
+          description: |
+            Invalid parameter: PUT request is suported only for the RM site, or **siteBodyUpdate** invalid
+        '401':
+          description: Authentication failed
+        '403':
+          description: Current user does not have permission to update the RM site that is visible to them.
+        '404':
+          description: |
+            RM site does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  ## File plans
+  '/file-plans/{filePlanId}':
+    get:
+      tags:
+        - file-plans
+      summary: Get a file plan
+      description: |
+        Get information for file plan **filePlanId**
+
+        Besides mandatory fields the file plan's aspects and properties are returned by default.
+      operationId: getFilePlan
+      parameters:
+        - $ref: '#/parameters/filePlanIdWithAliasParam'
+        - $ref: '#/parameters/filePlanEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/FilePlanEntry'
+        '400':
+          description: |
+            Invalid parameter: **filePlanId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **filePlanId**
+        '404':
+          description: If **filePlanId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    put:
+      tags:
+        - file-plans
+      summary : Update a file plan
+      description: |
+        Updates file plan **filePlanId**.
+        You can only set or update description and title properties:
+        ```JSON
+        {
+          "properties":
+            {
+               "cm:description": "New Description",
+               "cm:title":"New Title"
+            }
+        }
+        ```
+
+        **Note:** Currently there is no optimistic locking for updates, so they are applied in "last one wins" order.
+      operationId: updateFilePlan
+      parameters:
+        - $ref: '#/parameters/filePlanIdWithAliasParam'
+        - $ref: '#/parameters/filePlanEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: filePlanBodyUpdate
+          description: The file plan information to update.
+          required: true
+          schema:
+            $ref: '#/definitions/FilePlanBodyUpdate'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/FilePlanEntry'
+        '400':
+          description: |
+            Invalid parameter: The update request is invalid or **filePlanId** is not a valid format or **filePlanBodyUpdate** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to update **filePlanId**
+        '404':
+          description: If **filePlanId** does not exist
+        '409':
+          description: If the updated name clashes with an existing fileplan
+        '422':
+          description: Model integrity exception, including file name with invalid characters
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  '/file-plans/{filePlanId}/categories':
+    get:
+      tags:
+        - file-plans
+      summary: List file plans's children
+      description: |
+        Returns a list of record categories.
+
+        Minimal information for each child is returned by default.
+
+        You can use the **include** parameter to return additional information.
+
+        You can use the **include** parameter (include=association) to return child association details
+        for each child, including the **assocType** and the **isPrimary** flag.
+      operationId: getFilePlanCategories
+      produces:
+        - application/json
+      parameters:
+        - $ref: '#/parameters/filePlanIdWithAliasParam'
+        - $ref: '#/parameters/skipCountParam'
+        - $ref: '#/parameters/maxItemsParam'
+        - $ref: '#/parameters/orderByParam'
+        - $ref: '#/parameters/recordCategoryEntryIncludeParam'
+        - $ref: '#/parameters/filePlanIncludeSourceParam'
+        - $ref: '#/parameters/fieldsParam'
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordCategoryAssociationPaging'
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **filePlanId**
+        '404':
+          description: If **filePlanId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    post:
+      tags:
+        - file-plans
+      summary: Create record categories for given file plan
+      description: |
+        Create a record category as a primary child of **filePlanId**.
+
+        You can set the **autoRename** boolean field to automatically resolve name clashes. If there is a name clash, then
+        the API method tries to create
+        a unique name using an integer suffix.
+
+        Any field in the JSON body defined below can also be passed as a form-data field.
+
+        This API method also supports record category creation using application/json.
+
+        You must specify at least a **name**.
+
+        You can create a category like this:
+        ```JSON
+        {
+          "name":"My Record Category"
+        }
+        ```
+        You can set properties when creating a record category:
+        ```JSON
+        {
+          "name":"My Record Category",
+          "properties":
+          {
+            "rma:vitalRecordIndicator":"true",
+            "rma:reviewPeriod":"month|1"
+          }
+        }
+        ```
+
+        Any missing aspects are applied automatically. You can set aspects explicitly, if needed, using an **aspectNames** field.
+
+        If you specify a list as input, then a paginated list rather than an entry is returned in the response body. For example:
+
+        ```JSON
+        {
+          "list": {
+            "pagination": {
+              "count": 2,
+              "hasMoreItems": false,
+              "totalItems": 2,
+              "skipCount": 0,
+              "maxItems": 100
+            },
+            "entries": [
+              {
+                "entry": {
+                  ...
+                }
+              },
+              {
+                "entry": {
+                  ...
+                }
+              }
+            ]
+          }
+        }
+        ```
+      operationId: createFilePlanCategories
+      parameters:
+        - $ref: '#/parameters/filePlanIdWithAliasParam'
+        - $ref: '#/parameters/autoRenameParam'
+        - $ref: '#/parameters/recordCategoryEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: nodeBodyCreate
+          description: The node information to create.
+          required: true
+          schema:
+            $ref: '#/definitions/RootCategoryBodyCreate'
+      consumes:
+        - application/json
+        - multipart/form-data
+      produces:
+        - application/json
+      responses:
+        '201':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordCategoryEntry'
+        '400':
+          description: |
+            Invalid parameter: **filePlanId** is not a valid format or **filePlanId** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to add children to **filePlanId**
+        '404':
+          description: If **filePlanId** does not exist
+        '409':
+          description: If new name clashes with an existing node in the current parent container
+        '422':
+          description: Model integrity exception, including node name with invalid characters
+  ## Unfiled records containers
+  '/unfiled-containers/{unfiledContainerId}':
+    get:
+      tags:
+        - unfiled-containers
+      summary: Get the unfiled records container
+      description: |
+        Get information for unfiled records contianer **unfiledContainerId**
+
+        Besides mandatory fields the unfiled records container's aspects and properties are returned by default.
+      operationId: getUnfiledContainer
+      parameters:
+        - $ref: '#/parameters/unfiledContainerIdParam'
+        - $ref: '#/parameters/unfiledContainerEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/UnfiledContainerEntry'
+        '400':
+          description: |
+            Invalid parameter: **unfiledContainerId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **unfiledContainerId**
+        '404':
+          description: If **unfiledContainerId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    put:
+      tags:
+        - unfiled-containers
+      summary : Update an unfiled record container
+      description: |
+        Updates unfiled record container **unfiledContainerId**. For example, you can rename an unfiled record container:
+        ```JSON
+        {
+          "name":"My new name"
+        }
+        ```
+        You can also set or update description and title properties:
+        ```JSON
+        {
+          "properties":
+            {
+               "cm:description": "New Description",
+               "cm:title":"New Title"
+            }
+        }
+        ```
+
+        **Note:** Currently there is no optimistic locking for updates, so they are applied in "last one wins" order.
+      operationId: updateUnfiledContainer
+      parameters:
+        - $ref: '#/parameters/unfiledContainerIdParam'
+        - $ref: '#/parameters/unfiledContainerEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: unfiledContainerBodyUpdate
+          description: The unfiled record container information to update.
+          required: true
+          schema:
+            $ref: '#/definitions/UnfiledRecordContainerBodyUpdate'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/UnfiledContainerEntry'
+        '400':
+          description: |
+            Invalid parameter: The update request is invalid or **unfiledContainerId** is not a valid format or **unfiledContainerBodyUpdate** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to update **unfiledContainerId**
+        '404':
+          description: If **unfiledContainerId** does not exist
+        '409':
+          description: If the updated name clashes with an existing root category of special container in the current fileplan
+        '422':
+          description: Model integrity exception, including file name with invalid characters
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  '/unfiled-containers/{unfiledContainerId}/children':
+    get:
+      tags:
+        - unfiled-containers
+      summary: List unfiled record container's children
+      description: |
+        Returns a list of records or unfiled record folders.
+
+        Minimal information for each child is returned by default.
+
+        You can use the **include** parameter to return additional information.
+      operationId: listUnfiledContainerChildren
+      produces:
+        - application/json
+      parameters:
+        - $ref: '#/parameters/unfiledContainerIdParam'
+        - $ref: '#/parameters/skipCountParam'
+        - $ref: '#/parameters/maxItemsParam'
+        - $ref: '#/parameters/orderByParam'
+        - $ref: '#/parameters/unfiledRecordFolderAndContainerWhereParam'
+        - $ref: '#/parameters/unfiledContainerEntryIncludeParam'
+        - $ref: '#/parameters/unfiledContainerIncludeSourceParam'
+        - $ref: '#/parameters/fieldsParam'
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/UnfiledContainerAssociationPaging'
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **unfiledContainerId**
+        '404':
+          description: If **unfiledContainerId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    post:
+      tags:
+        - unfiled-containers
+      summary: Create a record or an unfiled record folder
+      description: |
+        Create a record or an unfiled record folder as a primary child of **unfiledContainerId**.
+
+        You can set the **autoRename** boolean field to automatically resolve name clashes. If there is a name clash, then
+        the API method tries to create a unique name using an integer suffix.
+
+        This endpoint supports both JSON and multipart/form-data (file upload).
+
+        **Using multipart/form-data**
+
+        Use the **filedata** field to represent the content to upload, for example, the following curl command will
+        create a node with the contents of test.txt in the test user's home folder.
+
+        ```curl -utest:test -X POST host:port/alfresco/api/-default-/public/gs/versions/1/unfiled-containers/{unfiledContainerId}/children -F filedata=@test.txt```
+
+        This API method also supports record and unfiled record folder creation using application/json.
+
+        You must specify at least a **name** and **nodeType**.
+
+        You can create a non-electronic record like this:
+        ```JSON
+        {
+          "name":"My Non-electronic Record",
+          "nodeType":"rma:nonElectronicDocument",
+          "properties":
+            {
+              "cm:description":"My Non-electronic Record Description",
+              "cm:title":"My Non-electronic Record Title",
+              "rma:box":"My Non-electronic Record Box",
+              "rma:file":"My Non-electronic Record File",
+              "rma:numberOfCopies":1,
+              "rma:physicalSize":30,
+              "rma:shelf":"My Non-electronic Record Shelf",
+              "rma:storageLocation":"My Non-electronic Record Location"
+            }
+        }
+        ```
+
+        You can create an empty electronic record:
+        ```JSON
+        {
+          "name":"My Electronic Record",
+          "nodeType":"cm:content"
+        }
+        ```
+
+        You can create an unfiled record folder like this:
+        ```JSON
+        {
+          "name": "My Unfiled Record Folder",
+          "nodeType": "rma:unfiledRecordFolder",
+          "properties":
+          {
+            "cm:title": "My Unfiled Record Folder Title"
+          }
+        }
+        ```
+
+        Any missing aspects are applied automatically. You can set aspects explicitly, if needed, using an **aspectNames** field.
+
+        **Note:** You can create more than one child by
+        specifying a list of nodes in the JSON body. For example, the following JSON
+        body creates a record and an unfiled record folder inside the specified **unfiledContainerId**:
+        ```JSON
+        [
+          {
+            "name":"My Record",
+            "nodeType":"cm:content"
+          },
+          {
+            "name":"My Unfiled Record Folder",
+            "nodeType":"rma:unfiledRecordFolder"
+          }
+        ]
+        ```
+        If you specify a list as input, then a paginated list rather than an entry is returned in the response body. For example:
+
+        ```JSON
+        {
+          "list": {
+            "pagination": {
+              "count": 2,
+              "hasMoreItems": false,
+              "totalItems": 2,
+              "skipCount": 0,
+              "maxItems": 100
+            },
+            "entries": [
+              {
+                "entry": {
+                  ...
+                }
+              },
+              {
+                "entry": {
+                  ...
+                }
+              }
+            ]
+          }
+        }
+        ```
+      operationId: createUnfiledContainerChildren
+      parameters:
+        - $ref: '#/parameters/unfiledContainerIdParam'
+        - $ref: '#/parameters/autoRenameParam'
+        - $ref: '#/parameters/unfiledContainerEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: nodeBodyCreate
+          description: The node information to create.
+          required: true
+          schema:
+            $ref: '#/definitions/RMNodeBodyCreate'
+      consumes:
+        - application/json
+        - multipart/form-data
+      produces:
+        - application/json
+      responses:
+        '201':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/UnfiledContainerAssociationPaging'
+        '400':
+          description: |
+            Invalid parameter: **unfiledContainerId** is not a valid format or **unfiledContainerId** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to add children to **unfiledContainerId**
+        '404':
+          description: If **unfiledContainerId** does not exist
+        '409':
+          description: If new name clashes with an existing node in the current parent container
+        '422':
+          description: Model integrity exception, including node name with invalid characters
+ ## Unfiled record folders
+  '/unfiled-record-folders/{unfiledRecordFolderId}':
+    get:
+      tags:
+        - unfiled-record-folders
+      summary: Get the unfiled record folder
+      description: |
+        Get information for unfiled record folder id **unfiledRecordFolderId**
+
+        Besides mandatory fields the unfiled record folder's aspects and properties are returned by default.
+      operationId: getUnfiledRecordFolder
+      parameters:
+        - $ref: '#/parameters/unfiledRecordFolderIdParam'
+        - $ref: '#/parameters/unfiledRecordFolderEntryIncludeParam'
+        - $ref: '#/parameters/unfiledRecordFolderRelativePathParam'
+        - $ref: '#/parameters/fieldsParam'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/UnfiledRecordFolderEntry'
+        '400':
+          description: |
+            Invalid parameter: **unfiledRecordFolderId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **unfiledRecordFolderId**
+        '404':
+          description: If **unfiledRecordFolderId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    put:
+      tags:
+        - unfiled-record-folders
+      summary : Update an unfiled record folder
+      description: |
+        Updates unfiled record folder **unfiledRecordFolderId**. For example, you can rename a record folder:
+        ```JSON
+        {
+          "name":"My new name"
+        }
+        ```
+        You can also set or update one or more properties:
+        ```JSON
+        {
+          "properties":
+            {
+               "cm:title":"New title",
+               "cm:description":"New description"
+            }
+        }
+        ```
+        **Note:** if you want to add or remove aspects, then you must use **GET /unfiled-record-folders/{unfiledRecordFolderId}** first to get the complete set of *aspectNames*.
+
+        **Note:** Currently there is no optimistic locking for updates, so they are applied in "last one wins" order.
+      operationId: updateUnfiledRecordFolder
+      parameters:
+        - $ref: '#/parameters/unfiledRecordFolderIdParam'
+        - $ref: '#/parameters/unfiledRecordFolderIncludeSourceParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: unfiledRecordFolderBodyUpdate
+          description: The record folder information to update.
+          required: true
+          schema:
+            $ref: '#/definitions/UnfiledRecordFolderBodyUpdate'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/UnfiledRecordFolderEntry'
+        '400':
+          description: |
+            Invalid parameter: The update request is invalid or **unfiledRecordFolderId** is not a valid format or **unfiledRecordFolderBodyUpdate** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to update **unfiledRecordFolderId**
+        '404':
+          description: If **unfiledRecordFolderId** does not exist
+        '409':
+          description: If the updated name clashes with an existing unfiled record folder in the current parent category
+        '422':
+          description: Model integrity exception, including file name with invalid characters
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    delete:
+      tags:
+        - unfiled-record-folders
+      summary : Delete an unfiled record folder
+      description: |
+        Deletes unfiled record folder **unfiledRecordFolderId**.
+      operationId: deleteUnfiledRecordFolder
+      parameters:
+        - $ref: '#/parameters/unfiledRecordFolderIdParam'
+      produces:
+        - application/json
+      responses:
+        '204':
+          description: Successful response
+        '400':
+          description: |
+            Invalid parameter: **unfiledRecordFolderId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If the current user does not have permission to delete **unfiledRecordFolderId**
+        '404':
+          description: If **unfiledRecordFolderId** does not exist
+        '409':
+          description: If **unfiledRecordFolderId** is locked and cannot be deleted
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  '/unfiled-record-folders/{unfiledRecordFolderId}/children':
+    get:
+      tags:
+        - unfiled-record-folders
+      summary: List unfiled record folder's children
+      description: |
+        Returns a list of records or unfiled record folders.
+
+        Minimal information for each child is returned by default.
+
+        You can use the **include** parameter to return additional information.
+      operationId: listUnfiledRecordFolderChildren
+      produces:
+        - application/json
+      parameters:
+        - $ref: '#/parameters/unfiledRecordFolderIdParam'
+        - $ref: '#/parameters/skipCountParam'
+        - $ref: '#/parameters/maxItemsParam'
+        - $ref: '#/parameters/orderByParam'
+        - $ref: '#/parameters/unfiledRecordFolderAndContainerWhereParam'
+        - $ref: '#/parameters/unfiledRecordFolderEntryIncludeParam'
+        - $ref: '#/parameters/unfiledRecordFolderRelativePathParam'
+        - $ref: '#/parameters/unfiledRecordFolderIncludeSourceParam'
+        - $ref: '#/parameters/fieldsParam'
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/UnfiledRecordFolderAssociationPaging'
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **unfiledRecordFolderId**
+        '404':
+          description: If **unfiledRecordFolderId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    post:
+      tags:
+        - unfiled-record-folders
+      summary: Create a record or an unfiled record folder
+      description: |
+        Create a record or an unfiled record folder as a primary child of **unfiledRecordFolderId**.
+
+        You can set the **autoRename** boolean field to automatically resolve name clashes. If there is a name clash, then
+        the API method tries to create a unique name using an integer suffix.
+
+        This endpoint supports both JSON and multipart/form-data (file upload).
+
+        **Using multipart/form-data**
+
+        Use the **filedata** field to represent the content to upload, for example, the following curl command will
+        create a node with the contents of test.txt in the test user's home folder.
+
+        ```curl -utest:test -X POST host:port/alfresco/api/-default-/public/gs/versions/1/unfiled-record-folders/{unfiledRecordFolderId}/children -F filedata=@test.txt```
+
+        This API method also supports record and unfiled record folder creation using application/json.
+
+        You must specify at least a **name** and **nodeType**.
+
+        You can create a non-electronic record like this:
+        ```JSON
+        {
+          "name":"My Non-electronic Record",
+          "nodeType":"rma:nonElectronicDocument",
+          "properties":
+            {
+              "cm:description":"My Non-electronic Record Description",
+              "cm:title":"My Non-electronic Record Title",
+              "rma:box":"My Non-electronic Record Box",
+              "rma:file":"My Non-electronic Record File",
+              "rma:numberOfCopies":1,
+              "rma:physicalSize":30,
+              "rma:shelf":"My Non-electronic Record Shelf",
+              "rma:storageLocation":"My Non-electronic Record Location"
+            }
+        }
+        ```
+
+        You can create an empty electronic record:
+        ```JSON
+        {
+          "name":"My Electronic Record",
+          "nodeType":"cm:content"
+        }
+        ```
+
+        You can create an unfiled record folder like this:
+        ```JSON
+        {
+          "name": "My Unfiled Record Folder",
+          "nodeType": "rma:unfiledRecordFolder",
+          "properties":
+          {
+            "cm:title": "My Unfiled Record Folder Title"
+          }
+        }
+        ```
+
+        Any missing aspects are applied automatically. You can set aspects explicitly, if needed, using an **aspectNames** field.
+
+        **Note:** You can create more than one child by
+        specifying a list of nodes in the JSON body. For example, the following JSON
+        body creates a record and an unfiled record folder inside the specified **unfiledRecordFolderId**:
+        ```JSON
+        [
+          {
+            "name":"My Record",
+            "nodeType":"cm:content"
+          },
+          {
+            "name":"My Unfiled Record Folder",
+            "nodeType":"rma:unfiledRecordFolder"
+          }
+        ]
+        ```
+        If you specify a list as input, then a paginated list rather than an entry is returned in the response body. For example:
+
+        ```JSON
+        {
+          "list": {
+            "pagination": {
+              "count": 2,
+              "hasMoreItems": false,
+              "totalItems": 2,
+              "skipCount": 0,
+              "maxItems": 100
+            },
+            "entries": [
+              {
+                "entry": {
+                  ...
+                }
+              },
+              {
+                "entry": {
+                  ...
+                }
+              }
+            ]
+          }
+        }
+        ```
+      operationId: createUnfiledRecordFolderChildren
+      parameters:
+        - $ref: '#/parameters/unfiledRecordFolderIdParam'
+        - $ref: '#/parameters/autoRenameParam'
+        - $ref: '#/parameters/unfiledRecordFolderEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: nodeBodyCreate
+          description: The node information to create.
+          required: true
+          schema:
+            $ref: '#/definitions/RMNodeBodyCreateWithRelativePath'
+      consumes:
+        - application/json
+        - multipart/form-data
+      produces:
+        - application/json
+      responses:
+        '201':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/UnfiledRecordFolderAssociationPaging'
+        '400':
+          description: |
+            Invalid parameter: **unfiledRecordFolderId** is not a valid format or **unfiledRecordFolderId** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to add children to **unfiledRecordFolderId**
+        '404':
+          description: If **unfiledRecordFolderId** does not exist
+        '409':
+          description: If new name clashes with an existing node in the current parent container
+        '422':
+          description: Model integrity exception, including node name with invalid characters
+  ## Record categories
+  '/record-categories/{recordCategoryId}':
+    get:
+      tags:
+        - record-categories
+      summary: Get a record category
+      description: |
+        Get information for record category **recordCategoryId**
+
+        Besides mandatory fields the record category's aspects and properties are returned by default.
+      operationId: getRecordCategory
+      parameters:
+        - $ref: '#/parameters/recordCategoryIdParam'
+        - $ref: '#/parameters/recordCategoryEntryIncludeParam'
+        - $ref: '#/parameters/recordCategoryRelativePathParam'
+        - $ref: '#/parameters/fieldsParam'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordCategoryEntry'
+        '400':
+          description: |
+            Invalid parameter: **recordCategoryId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **recordCategoryId**
+        '404':
+          description: If **recordCategoryId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    put:
+      tags:
+        - record-categories
+      summary : Update a record category
+      description: |
+        Updates record category **recordCategoryId**. For example, you can rename a record category:
+        ```JSON
+        {
+          "name":"My new name"
+        }
+        ```
+        You can also set or update one or more properties:
+        ```JSON
+        {
+          "properties":
+            {
+               "rma:vitalRecordIndicator": true,
+               "rma:reviewPeriod":"month|6"
+            }
+        }
+        ```
+        **Note:** If you want to add or remove aspects, then you must use **GET /record-categories/{recordCategoryId}** first to get the complete set of *aspectNames*.
+
+        **Note:** Currently there is no optimistic locking for updates, so they are applied in "last one wins" order.
+      operationId: updateRecordCategory
+      parameters:
+        - $ref: '#/parameters/recordCategoryIdParam'
+        - $ref: '#/parameters/recordCategoryEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: recordCategoryBodyUpdate
+          description: The record category information to update.
+          required: true
+          schema:
+            $ref: '#/definitions/FilePlanComponentBodyUpdate'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordCategoryEntry'
+        '400':
+          description: |
+            Invalid parameter: The update request is invalid or **recordCategoryId** is not a valid format or **recordCategoryBodyUpdate** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to update **recordCategoryId**
+        '404':
+          description: If **recordCategoryId** does not exist
+        '409':
+          description: If the updated name clashes with an existing record category in the current parent category
+        '422':
+          description: Model integrity exception, including file name with invalid characters
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    delete:
+      tags:
+        - record-categories
+      summary : Delete a record category
+      description: |
+        Deletes record category **recordCategoryId**.
+      operationId: deleteRecordCategory
+      parameters:
+        - $ref: '#/parameters/recordCategoryIdParam'
+      produces:
+        - application/json
+      responses:
+        '204':
+          description: Successful response
+        '400':
+          description: |
+            Invalid parameter: **recordCategoryId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If the current user does not have permission to delete **recordCategoryId**
+        '404':
+          description: If **recordCategoryId** does not exist
+        '409':
+          description: If **recordCategoryId** is locked and cannot be deleted
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  '/record-categories/{recordCategoryId}/children':
+    get:
+      tags:
+        - record-categories
+      summary: List record category's children
+      description: |
+        Returns a list of record categories and/or record folders.
+
+        Minimal information for each child is returned by default.
+
+        You can use the **include** parameter to return additional information.
+
+        The list of child nodes includes primary children and secondary children, if there are any.
+
+        You can use the **include** parameter (include=association) to return child association details
+        for each child, including the **assocType** and the **isPrimary** flag.
+      operationId: listRecordCategoryChildren
+      produces:
+        - application/json
+      parameters:
+        - $ref: '#/parameters/recordCategoryIdParam'
+        - $ref: '#/parameters/skipCountParam'
+        - $ref: '#/parameters/maxItemsParam'
+        - $ref: '#/parameters/orderByParam'
+        - $ref: '#/parameters/recordCategoryWhereParam'
+        - $ref: '#/parameters/recordCategoryChildIncludeParam'
+        - $ref: '#/parameters/recordCategoryRelativePathParam'
+        - $ref: '#/parameters/recordCategoryIncludeSourceParam'
+        - $ref: '#/parameters/fieldsParam'
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordCategoryAssociationPaging'
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **recordCategoryId**
+        '404':
+          description: If **recordCategoryId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    post:
+      tags:
+        - record-categories
+      summary: Create a record category or a record folder
+      description: |
+        Create a record category or a record folder as a primary child of **recordCategoryId**.
+
+        You can set the **autoRename** boolean field to automatically resolve name clashes. If there is a name clash, then
+        the API method tries to create
+        a unique name using an integer suffix.
+
+        Any field in the JSON body defined below can also be passed as a form-data field.
+
+        This API method also supports record category or record folder creation using application/json.
+
+        You must specify at least a **name** and **nodeType**.
+
+        You can create a category like this:
+        ```JSON
+        {
+          "name":"My Record Category",
+          "nodeType":"rma:recordCategory"
+        }
+        ```
+
+        You can create a record folder like this:
+        ```JSON
+        {
+          "name":"My Record Folder",
+          "nodeType":"rma:recordFolder"
+        }
+        ```
+
+        You can create a record folder inside a container hierarchy (applies to record categories as well):
+        ```JSON
+        {
+          "name":"My Fileplan Component",
+          "nodeType":"rma:recordFolder",
+          "relativePath":"X/Y/Z"
+        }
+        ```
+        The **relativePath** specifies the container structure to create relative to the node (record category or record folder). Containers in the
+        **relativePath** that do not exist are created before the node is created. The container type is decided considering
+        the type of the parent container and the type of the node to be created.
+
+        You can set properties when creating a record category (applies to record folders as well):
+        ```JSON
+        {
+          "name":"My Record Category",
+          "nodeType":"rma:recordCategory",
+          "properties":
+          {
+            "rma:vitalRecordIndicator":"true",
+            "rma:reviewPeriod":"month|1"
+          }
+        }
+        ```
+
+        Any missing aspects are applied automatically. You can set aspects explicitly, if needed, using an **aspectNames** field.
+
+        **Note:** You can create more than one child by
+        specifying a list of nodes in the JSON body. For example, the following JSON
+        body creates a record category and a record folder inside the specified **categoryId**:
+        ```JSON
+        [
+          {
+            "name":"My Record Category",
+            "nodeType":"rma:recordCategory"
+          },
+          {
+            "name":"My Record Folder",
+            "nodeType":"rma:recordFolder"
+          }
+        ]
+        ```
+        If you specify a list as input, then a paginated list rather than an entry is returned in the response body. For example:
+
+        ```JSON
+        {
+          "list": {
+            "pagination": {
+              "count": 2,
+              "hasMoreItems": false,
+              "totalItems": 2,
+              "skipCount": 0,
+              "maxItems": 100
+            },
+            "entries": [
+              {
+                "entry": {
+                  ...
+                }
+              },
+              {
+                "entry": {
+                  ...
+                }
+              }
+            ]
+          }
+        }
+        ```
+      operationId: createRecordCategoryChild
+      parameters:
+        - $ref: '#/parameters/recordCategoryIdParam'
+        - $ref: '#/parameters/autoRenameParam'
+        - $ref: '#/parameters/recordCategoryChildEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: nodeBodyCreate
+          description: |
+            The node information to create.
+          required: true
+          schema:
+            $ref: '#/definitions/RMNodeBodyCreateWithRelativePath'
+      consumes:
+        - application/json
+        - multipart/form-data
+      produces:
+        - application/json
+      responses:
+        '201':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordCategoryChildEntry'
+        '400':
+          description: |
+            Invalid parameter: **recordCategoryId** is not a valid format or **nodeBodyCreate** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to add children to **recordCategoryId**
+        '404':
+          description: If **recordCategoryId** does not exist
+        '409':
+          description: If new name clashes with an existing node in the current parent container
+        '422':
+          description: Model integrity exception, including node name with invalid characters
+  ## Record folders
+  '/record-folders/{recordFolderId}':
+    get:
+      tags:
+        - record-folders
+      summary: Get a record folder
+      description: |
+        Get information for record folder **recordFolderId**
+
+        Besides mandatory fields the record folder's aspects and properties are returned by default.
+      operationId: getRecordFolder
+      parameters:
+        - $ref: '#/parameters/recordFolderIdParam'
+        - $ref: '#/parameters/recordFolderEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordFolderEntry'
+        '400':
+          description: |
+            Invalid parameter: **recordFolderId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **recordFolderId**
+        '404':
+          description: If **recordFolderId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    put:
+      tags:
+        - record-folders
+      summary : Update a record folder
+      description: |
+        Updates record folder **recordFolderId**. For example, you can rename a record folder:
+        ```JSON
+        {
+          "name":"My new name"
+        }
+        ```
+        You can also set or update one or more properties:
+        ```JSON
+        {
+          "properties":
+            {
+               "rma:vitalRecordIndicator": true,
+               "rma:reviewPeriod":"month|6"
+            }
+        }
+        ```
+        **Note:** if you want to add or remove aspects, then you must use **GET /record-folders/{recordFolderId}** first to get the complete set of *aspectNames*.
+
+        **Note:** Currently there is no optimistic locking for updates, so they are applied in "last one wins" order.
+      operationId: updateRecordFolder
+      parameters:
+        - $ref: '#/parameters/recordFolderIdParam'
+        - $ref: '#/parameters/recordFolderEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: recordFolderBodyUpdate
+          description: The record folder information to update.
+          required: true
+          schema:
+            $ref: '#/definitions/FilePlanComponentBodyUpdate'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordFolderEntry'
+        '400':
+          description: |
+            Invalid parameter: The update request is invalid or **recordFolderId** is not a valid format or **recordFolderBodyUpdate** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to update **recordFolderId**
+        '404':
+          description: If **recordFolderId** does not exist
+        '409':
+          description: If the updated name clashes with an existing record folder in the current parent category
+        '422':
+          description: Model integrity exception, including file name with invalid characters
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    delete:
+      tags:
+        - record-folders
+      summary : Delete a record folder
+      description: |
+        Deletes record folder **recordFolderId**.
+      operationId: deleteRecordFolder
+      parameters:
+        - $ref: '#/parameters/recordFolderIdParam'
+      produces:
+        - application/json
+      responses:
+        '204':
+          description: Successful response
+        '400':
+          description: |
+            Invalid parameter: **recordFolderId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If the current user does not have permission to delete **recordFolderId**
+        '404':
+          description: If **recordFolderId** does not exist
+        '409':
+          description: If **recordFolderId** is locked and cannot be deleted
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  '/record-folders/{recordFolderId}/records':
+    get:
+      tags:
+        - record-folders
+      summary: List records
+      description: |
+        Returns a list of records.
+
+        Minimal information for each record is returned by default.
+
+        You can use the **include** parameter to return additional information.
+
+        The list of records includes primary children and secondary children, if there are any.
+
+        You can use the **include** parameter (include=association) to return child association details
+        for each child, including the **assocType** and the **isPrimary** flag.
+      operationId: listRecordFolderChildren
+      produces:
+        - application/json
+      parameters:
+        - $ref: '#/parameters/recordFolderIdParam'
+        - $ref: '#/parameters/skipCountParam'
+        - $ref: '#/parameters/maxItemsParam'
+        - $ref: '#/parameters/orderByParam'
+        - $ref: '#/parameters/recordFolderWhereParam'
+        - $ref: '#/parameters/recordFolderChildEntryIncludeParam'
+        - $ref: '#/parameters/recordFolderIncludeSourceParam'
+        - $ref: '#/parameters/fieldsParam'
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordFolderAssociationPaging'
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **recordFolderId**
+        '404':
+          description: If **recordFolderId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    post:
+      tags:
+        - record-folders
+      summary: Create a record
+      description: |
+        Create a record as a primary child of **recordFolderId**.
+
+        This endpoint supports both JSON and multipart/form-data (file upload).
+
+        **Using multipart/form-data**
+
+        Use the **filedata** field to represent the content to upload, for example, the following curl command will
+        create a node with the contents of test.txt in the test user's home folder.
+
+        ```curl -utest:test -X POST host:port/alfresco/api/-default-/public/gs/versions/1/record-folders/{recordFolderId}/records -F filedata=@test.txt```
+
+        This API method also supports record creation using application/json.
+
+        You must specify at least a **name** and **nodeType**.
+
+
+        You can create a non-electronic record like this:
+        ```JSON
+        {
+          "name":"My Non-electronic Record",
+          "nodeType":"rma:nonElectronicDocument",
+          "properties":
+            {
+              "cm:description":"My Non-electronic Record Description",
+              "cm:title":"My Non-electronic Record Title",
+              "rma:box":"My Non-electronic Record Box",
+              "rma:file":"My Non-electronic Record File",
+              "rma:numberOfCopies":1,
+              "rma:physicalSize":30,
+              "rma:shelf":"My Non-electronic Record Shelf",
+              "rma:storageLocation":"My Non-electronic Record Location"
+            }
+        }
+        ```
+
+        You can create an empty electronic record:
+        ```JSON
+        {
+          "name":"My Electronic Record",
+          "nodeType":"cm:content"
+        }
+        ```
+
+        Any missing aspects are applied automatically. You can set aspects explicitly, if needed, using an **aspectNames** field.
+
+        **Note:** You can create more than one child by
+        specifying a list of nodes in the JSON body. For example, the following JSON
+        body creates a record category and a record folder inside the specified **categoryId**:
+        ```JSON
+        [
+          {
+            "name":"Record 1",
+            "nodeType":"cm:content"
+          },
+          {
+            "name":"Record 2",
+            "nodeType":"cm:content"
+          }
+        ]
+        ```
+        If you specify a list as input, then a paginated list rather than an entry is returned in the response body. For example:
+
+        ```JSON
+        {
+          "list": {
+            "pagination": {
+              "count": 2,
+              "hasMoreItems": false,
+              "totalItems": 2,
+              "skipCount": 0,
+              "maxItems": 100
+            },
+            "entries": [
+              {
+                "entry": {
+                  ...
+                }
+              },
+              {
+                "entry": {
+                  ...
+                }
+              }
+            ]
+          }
+        }
+        ```
+      operationId: createRecordFolderChild
+      parameters:
+        - $ref: '#/parameters/recordFolderIdParam'
+        - $ref: '#/parameters/recordFolderChildEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: recordBodyCreate
+          description: |
+            The record information to create.
+
+            This field is ignored for multipart/form-data content uploads.
+          required: true
+          schema:
+            $ref: '#/definitions/RMNodeBodyCreate'
+      consumes:
+        - application/json
+        - multipart/form-data
+      produces:
+        - application/json
+      responses:
+        '201':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordEntry'
+        '400':
+          description: |
+            Invalid parameter: **recordFolderId** is not a valid format or **recordBodyCreate** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to add children to **recordFolderId**
+        '404':
+          description: If **recordFolderId** does not exist
+        '422':
+          description: Model integrity exception, including node name with invalid characters
+  ## Records
+  '/records/{recordId}':
+    get:
+      tags:
+        - records
+      summary: Get a record
+      description: |
+        Get information for record **recordId**
+
+        Besides mandatory fields the record's aspects and properties are returned by default.
+      operationId: getRecord
+      parameters:
+        - $ref: '#/parameters/recordIdParam'
+        - $ref: '#/parameters/recordEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordEntry'
+        '400':
+          description: |
+            Invalid parameter: **recordId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **recordId**
+        '404':
+          description: If **recordId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    put:
+      tags:
+        - records
+      summary : Update record
+      description: |
+        Updates the record **recordId**. For example, you can rename a record:
+        ```JSON
+        {
+          "name":"My new name"
+        }
+        ```
+        You can also set or update one or more properties:
+        ```JSON
+        {
+          "properties":
+            {
+               "cm:title":"New title",
+               "cm:description":"New description"
+            }
+        }
+        ```
+        **Note:** if you want to add or remove aspects, then you must use **GET /records/{recordId}** first to get the complete set of *aspectNames*.
+
+        **Note:** Currently there is no optimistic locking for updates, so they are applied in "last one wins" order.
+      operationId: updateRecord
+      parameters:
+        - $ref: '#/parameters/recordIdParam'
+        - $ref: '#/parameters/recordEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: recordBodyUpdate
+          description: The node information to update.
+          required: true
+          schema:
+            $ref: '#/definitions/FilePlanComponentBodyUpdate'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordEntry'
+        '400':
+          description: |
+            Invalid parameter: the update request is invalid or **recordId** is not a valid format or **recordBodyUpdate** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to update **recordId**
+        '404':
+          description: If **recordId** does not exist
+        '409':
+          description: If the updated name clashes with an existing node in the current parent folder
+        '422':
+          description: Model integrity exception, including file name with invalid characters
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    delete:
+      tags:
+        - records
+      summary : Delete a record
+      description: |
+        Deletes the record **recordId**.
+      operationId: deleteRecord
+      parameters:
+        - $ref: '#/parameters/recordIdParam'
+      produces:
+        - application/json
+      responses:
+        '204':
+          description: Successful response
+        '400':
+          description: |
+            Invalid parameter: **recordId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If the current user does not have permission to delete **recordId**
+        '404':
+          description: If **recordId** does not exist
+        '409':
+          description: If **recordId** is locked and cannot be deleted
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  '/records/{recordId}/content':
+    get:
+      tags:
+        - records
+      summary: Get record content
+      description: |
+
+        Gets the content of the record with identifier **recordId**.
+      operationId: getRecordContent
+      parameters:
+        - $ref: '#/parameters/recordIdParam'
+        - $ref: '#/parameters/attachmentParam'
+        - $ref: '#/parameters/ifModifiedSinceHeader'
+      responses:
+        '200':
+          description: Successful response
+        '304':
+          description: Content has not been modified since the date provided in the If-Modified-Since header
+        '400':
+          description: |
+            Invalid parameter: **recordId** is not a valid format, or is not a record
+        '401':
+          description: Authentication failed
+        '404':
+          description: |
+            **recordId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  '/records/{recordId}/file':
+    post:
+      tags:
+        - records
+      summary: File a record
+      description: |
+        Files the record **recordId** in the target record folder.
+
+        You can specify the target record folder by providing its id **targetParentId**
+
+        If the record is already filed, a link to the target record folder is created.
+      operationId: fileRecord
+      parameters:
+        - $ref: '#/parameters/recordIdParam'
+        - $ref: '#/parameters/recordEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: nodeBodyFile
+          description: The target record folder id
+          required: true
+          schema:
+            $ref: '#/definitions/RequestBodyFile'
+      consumes:
+        - application/json
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordEntry'
+        '400':
+          description: |
+            Invalid parameter: **recordIdParam** or **targetParentId** is not a valid format,
+            **recordIdParam** is not a record, **targetParentId** is not a record container or **nodeBodyFile** is invalid
+        '401':
+          description: Authentication failed
+        '403':
+          description: Current user does not have permission to create children of **nodeId**
+        '404':
+          description: |
+            **recordIdParam** or **targetParentId** does not exist
+        '422':
+          description: |
+            Model integrity exception: the action breaks system's integrity restrictions
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  ## Files
+  '/files/{fileId}/declare':
+    post:
+      tags:
+        - files
+      summary: Declare as record
+      description: Declares the file **fileId** in the unfiled record container.
+      operationId: declareRecord
+      parameters:
+        - name: fileId
+          in: path
+          description: The identifier of a non-record file.
+          required: true
+          type: string
+        - name: hideRecord
+          in: query
+          description: Flag to indicate whether the record should be hidden from the current parent folder.
+          type: boolean
+          default: false
+        - $ref: '#/parameters/recordEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+      consumes:
+        - application/json
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/RecordEntry'
+        '400':
+          description: |
+            Invalid parameter: **fileId** is not a valid format
+        '401':
+          description: Authentication failed
+        '403':
+          description: Current user does not have permission to declare a record
+        '404':
+          description: |
+            **fileId** does not exist
+        '422':
+          description: |
+            Model integrity exception: the action breaks system's integrity restrictions
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  ## Transfer Containers
+  '/transfer-containers/{transferContainerId}':
+    get:
+      tags:
+        - transfer-containers
+      summary: Get a transfer container
+      description: |
+        Get information for transfer container  **transferContainerId**
+
+        Besides mandatory fields the transfer container's aspects and properties are returned by default.
+      operationId: getTransferContainer
+      parameters:
+        - $ref: '#/parameters/transferContainerIdWithAliasParam'
+        - $ref: '#/parameters/transferContainerEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/TransferContainerEntry'
+        '400':
+          description: |
+            Invalid parameter: **transferContainerId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **transferContainerId**
+        '404':
+          description: If **transferContainerId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+    put:
+      tags:
+        - transfer-containers
+      summary : Update transfer container
+      description: |
+        Updates the transfer container **transferContainerId**. For example, you can rename transfer container:
+        ```JSON
+        {
+          "name":"My new name"
+        }
+        ```
+        You can also set or update description and title properties:
+        ```JSON
+        {
+          "properties":
+            {
+               "cm:description": "New Description",
+               "cm:title":"New Title"
+            }
+        }
+        ```
+        **Note:** Currently there is no optimistic locking for updates, so they are applied in "last one wins" order.
+      operationId: updateTransferContainer
+      parameters:
+        - $ref: '#/parameters/transferContainerIdWithAliasParam'
+        - $ref: '#/parameters/transferContainerEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+        - in: body
+          name: nodeBodyUpdate
+          description: The node information to update.
+          required: true
+          schema:
+            $ref: '#/definitions/TransferContainerBodyUpdate'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/TransferContainerEntry'
+        '400':
+          description: |
+            Invalid parameter: the update request is invalid or **transferContainerId** is not a valid format or **nodeBody** is invalid
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to update **transferContainerId**
+        '404':
+          description: If **transferContainerId** does not exist
+        '409':
+          description: If the updated name clashes with an existing node in the current parent folder
+        '422':
+          description: Model integrity exception, including transfer container name with invalid characters
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  '/transfer-containers/{transferContainerId}/transfers':
+    get:
+      tags:
+        - transfer-containers
+      summary: List transfer container's children
+      description: |
+        Returns a list of transfers.
+
+        Minimal information for each child is returned by default.
+
+        You can use the **include** parameter to return additional information.
+
+        You can use the **include** parameter (include=association) to return child association details
+        for each child, including the **assocType** and the **isPrimary** flag.
+      operationId: listTransfers
+      produces:
+        - application/json
+      parameters:
+        - $ref: '#/parameters/transferContainerIdWithAliasParam'
+        - $ref: '#/parameters/skipCountParam'
+        - $ref: '#/parameters/maxItemsParam'
+        - $ref: '#/parameters/orderByParam'
+        - $ref: '#/parameters/transferEntryIncludeParam'
+        - $ref: '#/parameters/transferContainerIncludeSourceParam'
+        - $ref: '#/parameters/fieldsParam'
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/TransferContainerAssociationPaging'
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **transferContainerId**
+        '404':
+          description: If **transferContainerId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  ## Transfers
+  '/transfers/{transferId}':
+    get:
+      tags:
+        - transfers
+      summary: Get a transfer
+      description: |
+        Get information for transfer **transferId**
+
+        Besides mandatory fields the transfer's aspects and properties are returned by default.
+      operationId: getTransfer
+      parameters:
+        - $ref: '#/parameters/transferIdParam'
+        - $ref: '#/parameters/transferEntryIncludeParam'
+        - $ref: '#/parameters/fieldsParam'
+      produces:
+        - application/json
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/TransferEntry'
+        '400':
+          description: |
+            Invalid parameter: **transferId** is not a valid format
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **transferId**
+        '404':
+          description: If **transferId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+  '/transfers/{transferId}/children':
+    get:
+      tags:
+        - transfers
+      summary: List transfer's children
+      description: |
+        Returns a list of transfer's children.
+
+        Minimal information for each child is returned by default.
+
+        You can use the **include** parameter to return additional information.
+
+        You can use the **include** parameter (include=association) to return child association details
+        for each child, including the **assocType** and the **isPrimary** flag.
+      operationId: listTransfersChildren
+      produces:
+        - application/json
+      parameters:
+        - $ref: '#/parameters/transferIdParam'
+        - $ref: '#/parameters/skipCountParam'
+        - $ref: '#/parameters/maxItemsParam'
+        - $ref: '#/parameters/orderByParam'
+        - $ref: '#/parameters/transferChildEntryIncludeParam'
+        - $ref: '#/parameters/transferIncludeSourceParam'
+        - $ref: '#/parameters/fieldsParam'
+      responses:
+        '200':
+          description: Successful response
+          schema:
+            $ref: '#/definitions/TransferAssociationPaging'
+        '401':
+          description: If authentication fails
+        '403':
+          description: If current user does not have permission to read **transferId**
+        '404':
+          description: If **transferId** does not exist
+        default:
+          description: Unexpected error
+          schema:
+            $ref: '#/definitions/Error'
+parameters:
+  ## File plans
+  filePlanEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the file plan. Any optional field from the response model can be requested. For example:
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  filePlanIdWithAliasParam:
+    name: filePlanId
+    in: path
+    description: The identifier of a file plan. You can also use the -filePlan- alias.
+    required: true
+    type: string
+  filePlanIncludeSourceParam:
+    name: includeSource
+    in: query
+    description: Also include **source** in addition to **entries** with folder information on the parent node – the specified parent **filePlanId**
+    required: false
+    type: boolean
+  ## Unfiled records containers
+  unfiledContainerIdParam:
+    name: unfiledContainerId
+    in: path
+    description:
+      The identifier of a unfiled records container. You can use the **-unfiled-** alias.
+    required: true
+    type: string
+  unfiledContainerEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the unfiled records container children. Any optional field from the response model can be requested. For example:
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  unfiledContainerIncludeSourceParam:
+    name: includeSource
+    in: query
+    description: Also include **source** in addition to **entries** with folder information on the parent node – the specified parent **unfiledContainerId**
+    required: false
+    type: boolean
+## Unfiled record folders
+  unfiledRecordFolderIdParam:
+    name: unfiledRecordFolderId
+    in: path
+    description:
+      The identifier of a unfiled record folder.
+    required: true
+    type: string
+  unfiledRecordFolderEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the unfiled records container children. Any optional field from the response model can be requested. For example:
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  unfiledRecordFolderRelativePathParam:
+    name: relativePath
+    in: query
+    description: |
+      Return information on children in the unfiled records container resolved by this path. The path is relative to **unfiledRecordFolderId**.
+    required: false
+    type: string
+  unfiledRecordFolderIncludeSourceParam:
+    name: includeSource
+    in: query
+    description: Also include **source** in addition to **entries** with folder information on the parent node – either the specified parent **unfiledRecordFolderId**, or as resolved by **relativePath**.
+    required: false
+    type: boolean
+  unfiledRecordFolderAndContainerWhereParam:
+    name: where
+    in: query
+    description: |
+      Optionally filter the list. Here are some examples:
+
+      *   ```where=(isRecord=true)```
+
+      *   ```where=(isUnfiledRecordFolder=false)```
+
+      *   ```where=(nodeType='cm:content INCLUDESUBTYPES')```
+
+    required: false
+    type: string
+  ## Record categories
+  recordCategoryIdParam:
+    name: recordCategoryId
+    in: path
+    description:
+      The identifier of a record category.
+    required: true
+    type: string
+  recordCategoryEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the record category. Any optional field from the response model can be requested. For example:
+      * hasRetentionSchedule
+      * isRecordCategory
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  recordCategoryChildEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the record category. Any optional field from the response model can be requested. For example:
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  recordCategoryRelativePathParam:
+    name: relativePath
+    in: query
+    description: |
+      Return information on children in the record category resolved by this path. The path is relative to **recordCategoryId**.
+    required: false
+    type: string
+  recordCategoryIncludeSourceParam:
+    name: includeSource
+    in: query
+    description: Also include **source** in addition to **entries** with folder information on the parent node – either the specified parent **recordCategoryId**, or as resolved by **relativePath**.
+    required: false
+    type: boolean
+  recordCategoryChildIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the record category. Any optional field from the response model can be requested. For example:
+      * properties
+      * aspectNames
+      * hasRetentionSchedule
+      * isClosed
+      * isRecordCategory
+      * isRecordFolder
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  recordCategoryWhereParam:
+    name: where
+    in: query
+    description: |
+      Optionally filter the list. Here are some examples:
+
+      *   ```where=(nodeType='rma:recordFolder')```
+
+      *   ```where=(nodeType='rma:recordCategory')```
+
+      *   ```where=(isRecordFolder=true AND isClosed=false)```
+
+    required: false
+    type: string
+  ## Record folders
+  recordFolderIdParam:
+    name: recordFolderId
+    in: path
+    description:
+      The identifier of a record folder.
+    required: true
+    type: string
+  recordFolderEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the record folders. Any optional field from the response model can be requested. For example:
+      * properties
+      * aspectNames
+      * isClosed
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  recordFolderIncludeSourceParam:
+    name: includeSource
+    in: query
+    description: Also include **source** in addition to **entries** with record information on the parent folder – the specified parent **recordFolderId**
+    required: false
+    type: boolean
+  recordFolderChildEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the records. Any optional field from the response model can be requested. For example:
+      * properties
+      * aspectNames
+      * isCompleted
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  recordFolderWhereParam:
+    name: where
+    in: query
+    description: |
+      Optionally filter the list. Here are some examples:
+
+      *   ```where=(nodeType='my:specialNodeType')```
+
+      *   ```where=(nodeType='my:specialNodeType INCLUDESUBTYPES')```
+
+      *   ```where=(isPrimary=true)```
+
+    required: false
+    type: string
+  ## Transfer Containers
+  transferContainerIdWithAliasParam:
+    name: transferContainerId
+    in: path
+    description: The identifier of a transfer container. You can also use the -transfers- alias.
+    required: true
+    type: string
+  transferContainerEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the transfer container. Any optional field from the response model can be requested. For example:
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  transferContainerIncludeSourceParam:
+    name: includeSource
+    in: query
+    description: Also include **source** in addition to **entries** with folder information on the specified parent **transferContainerId**.
+    required: false
+    type: boolean
+  ## Transfers
+  transferEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the transfer folder. Any optional field from the response model can be requested. For example:
+      * transferPDFIndicator
+      * transferLocation
+      * transferAccessionIndicator
+      * properties
+      * aspectNames
+      * allowableOperations
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  transferIdParam:
+    name: transferId
+    in: path
+    description: The identifier of a transfer.
+    required: true
+    type: string
+  transferIncludeSourceParam:
+    name: includeSource
+    in: query
+    description: Also include **source** in addition to **entries** with folder information on the specified parent **transferId**.
+    required: false
+    type: boolean
+  transferChildEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the transfer's child. Any optional field from the response model can be requested. For example:
+      * properties
+      * aspectNames
+      * isCompleted
+      * isRecord
+      * isClosed
+      * isRecordFolder
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  ## Record
+  recordIdParam:
+    name: recordId
+    in: path
+    description: The identifier of a record.
+    required: true
+    type: string
+  recordEntryIncludeParam:
+    name: include
+    in: query
+    description: |
+      Returns additional information about the record. Any optional field from the response model can be requested. For example:
+      * isCompleted
+      * content
+      * allowableOperations
+      * path
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  ## Core definition
+  fieldsParam:
+    name: fields
+    in: query
+    description: |
+      A list of field names.
+
+      You can use this parameter to restrict the fields
+      returned within a response if, for example, you want to save on overall bandwidth.
+
+      The list applies to a returned individual
+      entity or entries within a collection.
+
+      If the API method also supports the **include**
+      parameter, then the fields specified in the **include**
+      parameter are returned in addition to those specified in the **fields** parameter.
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  ## Core definition
+  skipCountParam:
+    name: skipCount
+    in: query
+    description: The number of entities that exist in the collection before those included in this list.
+    required: false
+    type: integer
+    minimum: 0
+  ## Core definition
+  maxItemsParam:
+    name: maxItems
+    in: query
+    description: The maximum number of items to return in the list.
+    required: false
+    type: integer
+    minimum: 1
+  ## Core parameter
+  orderByParam:
+    name: orderBy
+    in: query
+    description: |
+      A string to control the order of the entities returned in a list. You can use the **orderBy** parameter to
+      sort the list by one or more fields.
+
+      Each field has a default sort order, which is normally acending order. Read the API method implementation notes
+      above to check if any fields used in this method have a descending default search order.
+
+      To sort the entities in a specific order, you can use the **ASC** and **DESC** keywords for any field.
+    required: false
+    type: array
+    items:
+      type: string
+    collectionFormat: csv
+  # Core definition
+  attachmentParam:
+    name: attachment
+    in: query
+    description: |
+       **true** enables a web browser to download the file as an attachment.
+       **false** means a web browser may preview the file in a new tab or window, but not
+       download the file.
+
+       You can only set this parameter to **false** if the content type of the file is in the supported list;
+       for example, certain image files and PDF files.
+
+       If the content type is not supported for preview, then a value of **false**  is ignored, and
+       the attachment will be returned in the response.
+    required: false
+    default: true
+    type: boolean
+  # Core definition
+  ifModifiedSinceHeader:
+    name: If-Modified-Since
+    in: header
+    description: |
+      Only returns the content if it has been modified since the date provided.
+      Use the date format defined by HTTP. For example, `Wed, 09 Mar 2016 16:56:34 GMT`.
+    required: false
+    type: string
+    format: date-time
+  # Core definition
+  autoRenameParam:
+    name: autoRename
+    in: query
+    description: |
+      If true, then  a name clash will cause an attempt to auto rename by finding a unique name using an integer suffix.
+    required: false
+    type: boolean
+definitions:
+  FilePlanComponentBodyUpdate:
+    type: object
+    properties:
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+        additionalProperties:
+          type: string
+  ## File plans
+  FilePlan:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+      path:
+        $ref: '#/definitions/PathInfo'
+  FilePlanEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/FilePlan'
+  FilePlanBodyUpdate:
+    type: object
+    properties:
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      properties:
+        type: object
+        additionalProperties:
+          type: string
+  RootCategoryBodyCreate:
+    type: object
+    required:
+      - name
+      - nodeType
+    properties:
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+        additionalProperties:
+          type: string
+  ## Unfiled records containers
+  UnfiledContainer:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+      path:
+        $ref: '#/definitions/PathInfo'
+  UnfiledContainerEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/UnfiledContainer'
+  UnfiledContainerChild:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+      path:
+        $ref: '#/definitions/PathInfo'
+  UnfiledContainerAssociationPaging:
+    type: object
+    properties:
+      list:
+        type: object
+        properties:
+          pagination:
+            $ref: '#/definitions/Pagination'
+          entries:
+            type: array
+            items:
+              $ref: '#/definitions/UnfiledContainerChildAssociationEntry'
+          source:
+            $ref: '#/definitions/UnfiledContainer'
+  UnfiledContainerChildAssociationEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/UnfiledContainerChildAssociation'
+  UnfiledContainerChildAssociation:
+    allOf:
+      - $ref: '#/definitions/UnfiledContainerChild'
+      - type: object
+        properties:
+          association:
+            $ref: '#/definitions/ChildAssociationInfo'
+  UnfiledRecordContainerBodyUpdate:
+    type: object
+    properties:
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      properties:
+        type: object
+        additionalProperties:
+          type: string
+  ## Unfiled record folder
+  UnfiledRecordFolder:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+      path:
+        $ref: '#/definitions/PathInfo'
+  UnfiledRecordFolderEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/UnfiledRecordFolder'
+  UnfiledRecordFolderChild:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+      path:
+        $ref: '#/definitions/PathInfo'
+  UnfiledRecordFolderAssociationPaging:
+    type: object
+    properties:
+      list:
+        type: object
+        properties:
+          pagination:
+            $ref: '#/definitions/Pagination'
+          entries:
+            type: array
+            items:
+              $ref: '#/definitions/UnfiledRecordFolderChildAssociationEntry'
+          source:
+            $ref: '#/definitions/UnfiledRecordFolder'
+  UnfiledRecordFolderChildAssociationEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/UnfiledRecordFolderChildAssociation'
+  UnfiledRecordFolderChildAssociation:
+    allOf:
+      - $ref: '#/definitions/UnfiledRecordFolderChild'
+      - type: object
+        properties:
+          association:
+            $ref: '#/definitions/ChildAssociationInfo'
+  UnfiledRecordFolderBodyUpdate:
+    type: object
+    properties:
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      properties:
+        type: object
+        additionalProperties:
+          type: string
+  ## Record categories
+  RecordCategory:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      hasRetentionSchedule:
+        type: boolean
+        default: false
+        description: Indicates if the record category has a retention schedule defined
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+      path:
+        $ref: '#/definitions/PathInfo'
+  RecordCategoryChild:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      hasRetentionSchedule:
+        type: boolean
+        default: false
+        description: Indicates if the record category has a retention schedule defined
+      isClosed:
+        type: boolean
+        default: false
+        description: Indicates if the record folder is closed
+      isRecordCategory:
+        type: boolean
+      isRecordFolder:
+        type: boolean
+        default: false
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+      path:
+        $ref: '#/definitions/PathInfo'
+  RecordCategoryEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/RecordCategory'
+  RecordCategoryChildEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/RecordCategoryChild'
+  RecordCategoryAssociationPaging:
+    type: object
+    properties:
+      list:
+        type: object
+        properties:
+          pagination:
+            $ref: '#/definitions/Pagination'
+          entries:
+            type: array
+            items:
+              $ref: '#/definitions/RecordCategoryChildAssociationEntry'
+          source:
+            $ref: '#/definitions/RecordCategory'
+  RecordCategoryChildAssociationEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/RecordCategoryChildAssociation'
+  RecordCategoryChildAssociation:
+    allOf:
+      - $ref: '#/definitions/RecordCategoryChild'
+      - type: object
+        properties:
+          association:
+            $ref: '#/definitions/ChildAssociationInfo'
+  ## Record folder
+  RecordFolder:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      isClosed:
+        type: boolean
+        default: false
+        description: Indicates if the record folder is closed
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+      path:
+        $ref: '#/definitions/PathInfo'
+  RecordFolderEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/RecordFolder'
+  RecordFolderAssociationPaging:
+    type: object
+    properties:
+      list:
+        type: object
+        properties:
+          pagination:
+            $ref: '#/definitions/Pagination'
+          entries:
+            type: array
+            items:
+              $ref: '#/definitions/RecordFolderChildAssociationEntry'
+          source:
+            $ref: '#/definitions/RecordFolder'
+  RecordFolderChildAssociationEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/RecordFolderChildAssociation'
+  RecordFolderChildAssociation:
+    allOf:
+      - $ref: '#/definitions/Record'
+      - type: object
+        properties:
+          association:
+            $ref: '#/definitions/ChildAssociationInfo'
+  ## Record
+  Record:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      isCompleted:
+        type: boolean
+        default: false
+        description: Present only for record nodes. Indicates if the record is completed
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+      content:
+        $ref: '#/definitions/ContentInfo'
+      path:
+        $ref: '#/definitions/PathInfo'
+  RecordEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/Record'
+  ## Transfer Containers
+  TransferContainer:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+  TransferContainerEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/TransferContainer'
+  TransferContainerChild:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      transferPDFIndicator:
+        type: boolean
+        default: false
+        description: Present only for transfer nodes.
+      transferLocation:
+        type: string
+        description: Present only for transfer nodes.
+      transferAccessionIndicator:
+        type: boolean
+        default: false
+        description: Present only for transfer nodes.
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+  TransferContainerAssociationPaging:
+    type: object
+    properties:
+      list:
+        type: object
+        properties:
+          pagination:
+            $ref: '#/definitions/Pagination'
+          entries:
+            type: array
+            items:
+              $ref: '#/definitions/TransferContainerChildAssociationEntry'
+          source:
+            $ref: '#/definitions/TransferContainer'
+  TransferContainerChildAssociationEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/TransferContainerChildAssociation'
+  TransferContainerChildAssociation:
+    allOf:
+      - $ref: '#/definitions/TransferContainerChild'
+      - type: object
+        properties:
+          association:
+            $ref: '#/definitions/ChildAssociationInfo'
+  TransferContainerBodyUpdate:
+    type: object
+    properties:
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      properties:
+        type: object
+        additionalProperties:
+          type: string
+  ## Transfers
+  Transfer:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      transferPDFIndicator:
+        type: boolean
+        default: false
+        description: Present only for transfer nodes.
+      transferLocation:
+        type: string
+        description: Present only for transfer nodes.
+      transferAccessionIndicator:
+        type: boolean
+        default: false
+        description: Present only for transfer nodes.
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+  TransferEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/Transfer'
+  TransferChild:
+    type: object
+    required:
+    - id
+    - name
+    - nodeType
+    - createdAt
+    - createdByUser
+    - modifiedAt
+    - modifiedByUser
+    - parentId
+    properties:
+      id:
+        type: string
+      parentId:
+        type: string
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      isRecordFolder:
+        type: boolean
+        default: false
+      isRecord:
+        type: boolean
+        default: false
+      isClosed:
+        type: boolean
+        default: false
+        description: Indicates if the record folder is closed
+      isCompleted:
+        type: boolean
+        default: false
+        description: Present only for record nodes. Indicates if the record has a retention schedule defined
+      modifiedAt:
+        type: string
+        format: date-time
+      modifiedByUser:
+        $ref: '#/definitions/UserInfo'
+      createdAt:
+        type: string
+        format: date-time
+      createdByUser:
+        $ref: '#/definitions/UserInfo'
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+      allowableOperations:
+        type: array
+        items:
+          type: string
+      path:
+        $ref: '#/definitions/PathInfo'
+  TransferAssociationPaging:
+    type: object
+    properties:
+      list:
+        type: object
+        properties:
+          pagination:
+            $ref: '#/definitions/Pagination'
+          entries:
+            type: array
+            items:
+              $ref: '#/definitions/TransferChildAssociationEntry'
+          source:
+            $ref: '#/definitions/Transfer'
+  TransferChildAssociationEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/TransferChildAssociation'
+  TransferChildAssociation:
+    allOf:
+      - $ref: '#/definitions/TransferChild'
+      - type: object
+        properties:
+          association:
+            $ref: '#/definitions/ChildAssociationInfo'
+  ##
+  RequestBodyFile:
+    type: object
+    required:
+      - targetParentId
+    properties:
+      targetParentId:
+        type: string
+  ## Core definition
+  ChildAssociationInfo:
+    type: object
+    required:
+      - assocType
+      - isPrimary
+    properties:
+      assocType:
+        type: string
+      isPrimary:
+        type: boolean
+  ## Core definition
+  Pagination:
+    type: object
+    required:
+      - count
+      - hasMoreItems
+      - skipCount
+      - maxItems
+    properties:
+      count:
+        type: integer
+        format: int64
+        description: |
+          The number of objects in the entries array.
+      hasMoreItems:
+        type: boolean
+        description: |
+          A boolean value which is **true** if there are more entities in the collection
+          beyond those in this response. A true value means a request with a larger value
+          for the **skipCount** or the **maxItems** parameter will return more entities.
+      totalItems:
+        type: integer
+        format: int64
+        description: |
+          An integer describing the total number of entities in the collection.
+          The API might not be able to determine this value,
+          in which case this property will not be present.
+      skipCount:
+        type: integer
+        format: int64
+        description: |
+          An integer describing how many entities exist in the collection before
+          those included in this list.
+      maxItems:
+        type: integer
+        format: int64
+        description: |
+          The value of the **maxItems** parameter used to generate this list,
+          or if there was no **maxItems** parameter the default value is 100
+  ## Core definition
+  Error:
+    type: object
+    required:
+      - error
+    properties:
+      error:
+        type: object
+        required:
+          - statusCode
+          - briefSummary
+          - stackTrace
+          - descriptionURL
+        properties:
+          errorKey:
+            type: string
+          statusCode:
+            type: integer
+            format: int32
+          briefSummary:
+            type: string
+          stackTrace:
+            type: string
+          descriptionURL:
+            type: string
+          logId:
+            type: string
+  ## Core definition
+  UserInfo:
+    type: object
+    required:
+      - displayName
+      - id
+    properties:
+      displayName:
+        type: string
+      id:
+        type: string
+  ## Core definition
+  ContentInfo:
+    type: object
+    required:
+      - mimeType
+      - mimeTypeName
+      - sizeInBytes
+      - encoding
+    properties:
+      mimeType:
+        type: string
+      mimeTypeName:
+        type: string
+      sizeInBytes:
+        type: integer
+      encoding:
+        type: string
+  ## Core definition
+  PathElement:
+    type: object
+    properties:
+      id:
+        type: string
+      name:
+        type: string
+  ## Core definition
+  PathInfo:
+    type: object
+    properties:
+      elements:
+        type: array
+        items:
+          $ref: '#/definitions/PathElement'
+      name:
+        type: string
+      isCompleted:
+        type: boolean
+  RMNodeBodyCreate:
+    type: object
+    required:
+      - name
+      - nodeType
+    properties:
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+        additionalProperties:
+          type: string
+  RMNodeBodyCreateWithRelativePath:
+    type: object
+    required:
+      - name
+      - nodeType
+    properties:
+      name:
+        type: string
+        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
+        description: |
+          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
+          The character . must not be used at the end of the name.
+      nodeType:
+        type: string
+      aspectNames:
+        type: array
+        items:
+          type: string
+      properties:
+        type: object
+        additionalProperties:
+          type: string
+      relativePath:
+        type: string
+  RMSiteBodyCreate:
+    type: object
+    required:
+      - title
+    properties:
+      title:
+        type: string
+      description:
+        type: string
+      compliance:
+        type: string
+        default: STANDARD
+        enum:
+          - STANDARD
+          - DOD5015
+  RMSiteBodyUpdate:
+    type: object
+    properties:
+      title:
+        type: string
+      description:
+        type: string
+  RMSiteEntry:
+    type: object
+    required:
+      - entry
+    properties:
+      entry:
+        $ref: '#/definitions/RMSite'
+  RMSite:
+    type: object
+    required:
+      - id
+      - guid
+      - title
+      - visibility
+      - compliance
+    properties:
+      id:
+        type: string
+      guid:
+        type: string
+      title:
+        type: string
+      description:
+        type: string
+      visibility:
+        type: string
+        enum:
+          - PRIVATE
+          - MODERATED
+          - PUBLIC
+      compliance:
+        type: string
+        enum:
+          - STANDARD
+          - DOD5015
+      role:
+        type: string
+        enum:
+          - SiteConsumer
+          - SiteCollaborator
+          - SiteContributor
+          - SiteManager
\ No newline at end of file
diff --git a/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/ig-core-api.yaml b/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/ig-core-api.yaml
deleted file mode 100644
index 5cba27939b..0000000000
--- a/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/ig-core-api.yaml
+++ /dev/null
@@ -1,1198 +0,0 @@
-swagger: '2.0'
-info:
-  description: Provides access to Information Governance specific features
-  version: '1'
-  title: IG Core REST API
-basePath: /alfresco/api/-default-/public/ig/versions/1
-securityDefinitions:
-  basicAuth:
-    type: basic
-    description: HTTP Basic Authentication
-security:
-  - basicAuth: []
-consumes:
-  - application/json
-produces:
-  - application/json
-tags:
-  - name: fileplan-components
-    description: Retrieve and manage fileplan components
-  - name: ig-sites
-    description: Retrieve and manage the RM site
-  - name: records
-    description: Perform record specific operations
-  - name: files
-    description: Perform operations on non-record files
-paths:
-  '/fileplan-components/{fileplanComponentId}':
-    get:
-      tags:
-        - fileplan-components
-      summary: Get a fileplan component
-      description: |
-        Get information for fileplan component **fileplanComponentId**
-
-        Besides mandatory fields the node's aspects and properties are returned by default.
-      operationId: getFileplanComponent
-      parameters:
-        - $ref: '#/parameters/fileplanComponentIdWithAliasParam'
-        - $ref: '#/parameters/FilePlanComponentEntryIncludeParam'
-        - $ref: '#/parameters/relativePathParam'
-        - $ref: '#/parameters/fieldsParam'
-      produces:
-        - application/json
-      responses:
-        '200':
-          description: Successful response
-          schema:
-            $ref: '#/definitions/FilePlanComponentEntry'
-        '400':
-          description: |
-            Invalid parameter: **fileplanComponentId** is not a valid format
-        '401':
-          description: If authentication fails
-        '403':
-          description: If current user does not have permission to read **fileplanComponentId**
-        '404':
-          description: If **fileplanComponentId** does not exist
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-    put:
-      tags:
-        - fileplan-components
-      summary : Update fileplan component
-      description: |
-        Updates the fileplan component node **fileplanComponentId**. For example, you can rename a file or folder:
-        ```JSON
-        {
-          "name":"My new name"
-        }
-        ```
-        You can also set or update one or more properties:
-        ```JSON
-        {
-          "properties":
-            {
-               "rma:vitalRecordIndicator": true,
-               "rma:reviewPeriod":"month|6"
-            }
-        }
-        ```
-        **Note:** if you want to add or remove aspects, then you must use **GET /fileplan-components/{fileplanComponentId}** first to get the complete set of *aspectNames*.
-
-        **Note:** Currently there is no optimistic locking for updates, so they are applied in "last one wins" order.
-      operationId: updateFileplanComponent
-      parameters:
-        - $ref: '#/parameters/fileplanComponentIdWithAliasParam'
-        - $ref: '#/parameters/FilePlanComponentEntryIncludeParam'
-        - $ref: '#/parameters/fieldsParam'
-        - in: body
-          name: nodeBodyUpdate
-          description: The node information to update.
-          required: true
-          schema:
-            $ref: '#/definitions/FilePlanComponentBodyUpdate'
-      produces:
-        - application/json
-      responses:
-        '200':
-          description: Successful response
-          schema:
-            $ref: '#/definitions/FilePlanComponentEntry'
-        '400':
-          description: |
-            Invalid parameter: the update request is invalid or **fileplanComponentId** is not a valid format or **nodeBody** is invalid
-        '401':
-          description: If authentication fails
-        '403':
-          description: If current user does not have permission to update **fileplanComponentId**
-        '404':
-          description: If **fileplanComponentId** does not exist
-        '409':
-          description: If the updated name clashes with an existing node in the current parent folder
-        '422':
-          description: Model integrity exception, including file name with invalid characters
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-    delete:
-      tags:
-        - fileplan-components
-      summary : Delete fileplan component
-      description: |
-        Deletes the fileplan component node **fileplanComponentId**.
-      operationId: deleteFileplanComponent
-      parameters:
-        - $ref: '#/parameters/fileplanComponentIdParam'
-      produces:
-        - application/json
-      responses:
-        '204':
-          description: Successful response
-        '400':
-          description: |
-            Invalid parameter: **fileplanComponentId** is not a valid format
-        '401':
-          description: If authentication fails
-        '403':
-          description: If the current user does not have permission to delete **fileplanComponentId**
-        '404':
-          description: If **fileplanComponentId** does not exist
-        '409':
-          description: If **fileplanComponentId** is locked and cannot be deleted
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-
-  '/fileplan-components/{fileplanComponentId}/children':
-    get:
-      tags:
-        - fileplan-components
-      summary: List fileplan component's children
-      description: |
-        Returns a list of records and record containers.
-
-        **Note:** Special containers and retention schedules will not be retrieved.
-
-        Minimal information for each child is returned by default.
-
-        You can use the **include** parameter to return addtional information.
-
-        The list of child nodes includes primary children and secondary children, if there are any.
-
-        You can use the **include** parameter (include=association) to return child association details
-        for each child, including the **assocTyp**e and the **isPrimary** flag.
-      operationId: listFileplanComponentChildren
-      produces:
-        - application/json
-      parameters:
-        - $ref: '#/parameters/fileplanComponentIdWithAliasParam'
-        - $ref: '#/parameters/skipCountParam'
-        - $ref: '#/parameters/maxItemsParam'
-        - $ref: '#/parameters/orderByParam'
-        - $ref: '#/parameters/whereParam'
-        - $ref: '#/parameters/FilePlanComponentEntryIncludeParam'
-        - $ref: '#/parameters/relativePathParam'
-        - $ref: '#/parameters/includeSourceParam'
-        - $ref: '#/parameters/fieldsParam'
-      responses:
-        '200':
-          description: Successful response
-          schema:
-            $ref: '#/definitions/FilePlanComponentAssociationPaging'
-        '401':
-          description: If authentication fails
-        '403':
-          description: If current user does not have permission to read **fileplanComponentId**
-        '404':
-          description: If **fileplanComponentId** does not exist
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-    post:
-      tags:
-        - fileplan-components
-      summary: Create an IG node
-      description: |
-        Create a fileplan component as a primary child of node **fileplanComponentId**.
-
-        This API method supports file upload using multipart/form-data.
-        Electronic records are the only nodes that have content.
-
-        Use the **filedata** field to represent the content to upload.
-        You can use a **name** field to give an alternative name for the new electronic record.
-
-        For multipart/form-data upload you can use the **renditions** field to create renditions (e.g. doclib) asynchronously upon upload.
-        Note that currently only one rendition can be requested. Also, as requesting rendition is a background process,
-        any rendition failure (e.g. No transformer is currently available) will not fail the whole upload and has the potential to silently fail.
-
-        Use **overwrite** to overwrite an existing file, matched by name. If the file is versionable,
-        the existing content is replaced.
-
-        When you overwrite existing content, you can set the **majorVersion** boolean field to **true** to indicate a major version
-        should be created. The default for **majorVersion** is **false**.
-        Setting  **majorVersion** enables versioning of the node, if it is not already versioned.
-
-        When you overwrite existing content, you can use the **comment** field to add a version comment that appears in the
-        version history. This also enables versioning of this node, if it is not already versioned.
-
-        You can set the **autoRename** boolean field to automatically resolve name clashes. If there is a name clash, then
-        the API method tries to create
-        a unique name using an integer suffix.
-
-        Any field in the JSON body defined below can also be passed as a form-data field.
-
-        This API method also supports node creation using application/json.
-
-        You must specify at least a **name** and **nodeType**.
-
-        You can create a category like this:
-        ```JSON
-        {
-          "name":"My Category",
-          "nodeType":"rma:recordCategory"
-        }
-        ```
-
-        You can create a record folder like this:
-        ```JSON
-        {
-          "name":"My Record Folder",
-          "nodeType":"rma:recordFolder"
-        }
-        ```
-
-        You can create a hold folder like this:
-        ```JSON
-        {
-          "name": "My Hold Folder",
-          "nodeType": "rma:hold",
-          "properties":
-          {
-            "rma:holdReason": "My Hold Reason"
-          }
-        }
-        ```
-        You can create an unfiled record folder like this:
-        ```JSON
-        {
-          "name": "My Unfiled Record Folder",
-          "nodeType": "rma:unfiledRecordFolder",
-          "properties":
-          {
-            "cm:title": "My Unfiled Record Folder Title"
-          }
-        }
-        ```
-
-        You can create a non-electronic record like this:
-        ```JSON
-        {
-          "name":"My Non-electronic Record",
-          "nodeType":"rma:nonElectronicDocument",
-          "properties":
-            {
-              "cm:description":"My Non-electronic Record Description",
-              "cm:title":"My Non-electronic Record Title",
-              "rma:box":"My Non-electronic Record Box",
-              "rma:file":"My Non-electronic Record File",
-              "rma:numberOfCopies":1,
-              "rma:physicalSize":30,
-              "rma:shelf":"My Non-electronic Record Shelf",
-              "rma:storageLocation":"My Non-electronic Record Location"
-            }
-        }
-        ```
-
-        You can create an empty electronic record:
-        ```JSON
-        {
-          "name":"My Electronic Record",
-          "nodeType":"cm:content"
-        }
-        ```
-
-        You can create a fileplan component inside a container hierarchy:
-        ```JSON
-        {
-          "name":"My Fileplan Component",
-          "nodeType":"rma:recordFolder",
-          "relativePath":"X/Y/Z"
-        }
-        ```
-        The **relativePath** specifies the container structure to create relative to the node **nodeId**. Containers in the
-        **relativePath** that do not exist are created before the node is created. The container type is decided considering
-        the type of the parent container and the type of the node to be created.
-
-        You can set properties when creating a new fileplan component:
-        ```JSON
-        {
-          "name":"My Record Category",
-          "nodeType":"rma:recordCategory",
-          "properties":
-          {
-            "rma:vitalRecordIndicator":"true",
-            "rma:reviewPeriod":"month|1"
-          }
-        }
-        ```
-
-        Any missing aspects are applied automatically. You can set aspects explicitly, if needed, using an **aspectNames** field.
-
-        **Note:** You can create more than one child by
-        specifying a list of nodes in the JSON body. For example, the following JSON
-        body creates two folders inside the specified **nodeId**, if the **nodeId** identifies
-        a folder:
-        ```JSON
-        [
-          {
-            "name":"My Record Category",
-            "nodeType":"rma:recordCategory"
-          },
-          {
-            "name":"My Record Folder",
-            "nodeType":"rma:recordFolder"
-          }
-        ]
-        ```
-        If you specify a list as input, then a paginated list rather than an entry is returned in the response body. For example:
-
-        ```JSON
-        {
-          "list": {
-            "pagination": {
-              "count": 2,
-              "hasMoreItems": false,
-              "totalItems": 2,
-              "skipCount": 0,
-              "maxItems": 100
-            },
-            "entries": [
-              {
-                "entry": {
-                  ...
-                }
-              },
-              {
-                "entry": {
-                  ...
-                }
-              }
-            ]
-          }
-        }
-        ```
-      operationId: createFileplanComponent
-      parameters:
-        - $ref: '#/parameters/fileplanComponentIdWithAliasParam'
-        - name: autoRename
-          in: query
-          description: If true, then  a name clash will cause an attempt to auto rename by finding a unique name using an integer suffix.
-          required: false
-          type: boolean
-        - $ref: '#/parameters/FilePlanComponentEntryIncludeParam'
-        - $ref: '#/parameters/fieldsParam'
-        - in: body
-          name: nodeBodyCreate
-          description: |
-            The node information to create.
-
-            This field is ignored for multipart/form-data content uploads.
-          required: true
-          schema:
-            $ref: '#/definitions/RMNodeBodyCreate'
-      consumes:
-        - application/json
-        - multipart/form-data
-      produces:
-        - application/json
-      responses:
-        '201':
-          description: Successful response
-          schema:
-            $ref: '#/definitions/FilePlanComponentEntry'
-        '400':
-          description: |
-            Invalid parameter: **fileplanComponentId** is not a valid format or **nodeBodyCreate** is invalid
-        '401':
-          description: If authentication fails
-        '403':
-          description: If current user does not have permission to add children to **fileplanComponentId**
-        '404':
-          description: If **fileplanComponentId** does not exist
-        '409':
-          description: If new name clashes with an existing node in the current parent container
-        '422':
-          description: Model integrity exception, including node name with invalid characters
-  /ig-sites:
-    post:
-      tags:
-        - ig-sites
-      summary: Create the RM site
-      description: |
-        **Note:** this endpoint is available in RM 2.6 and newer versions.
-
-        Creates the RM site with the given details.
-
-        **Note:** the id of a site cannot be updated once the site has been created.
-
-        For example, to create the RM site named "Records Management Title" with "Records Management Description" as description, the following body could be used:
-        ```JSON
-        {
-          "title": "Records Management Title",
-          "description": "Records Management Description"
-        }
-        ```
-
-        The creator will be added as a member with Site Manager role.
-
-        When you create the RM site, the **filePlan** structure is also created.
-
-      operationId: createRMSite
-      produces:
-        - application/json
-      parameters:
-        - name: skipAddToFavorites
-          in: query
-          description: Flag to indicate whether the RM site should not be added to the user's site favorites.
-          type: boolean
-          default: false
-        - in: body
-          name: siteBodyCreate
-          description: The site details
-          required: true
-          schema:
-            $ref: '#/definitions/RMSiteBodyCreate'
-      responses:
-        '201':
-          description: Successful response
-          schema:
-            $ref: '#/definitions/RMSiteEntry'
-        '400':
-          description: |
-            Invalid parameter: **title**, or **description** exceed the maximum length;
-            or **siteBodyCreate** invalid
-        '401':
-          description: Authentication failed
-        '409':
-          description: Site with the given identifier already exists
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-  '/ig-sites/rm':
-    get:
-      tags:
-        - ig-sites
-      summary: Get the RM site
-      description: |
-        **Note:** this endpoint is available in RM 2.6 and newer versions.
-
-        Gets information for RM site.
-
-      operationId: getRMSite
-      produces:
-        - application/json
-      parameters:
-        - $ref: '#/parameters/fieldsParam'
-      responses:
-        '200':
-          description: Successful response
-          schema:
-            $ref: '#/definitions/RMSiteEntry'
-        '400':
-          description: |
-            Invalid parameter: GET request is suported only for the RM site
-        '401':
-          description: Authentication failed
-        '404':
-          description: |
-            RM site does not exist
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-    delete:
-      tags:
-        - ig-sites
-      summary: Delete the RM site
-      description: |
-        **Note:** this endpoint is available in RM 2.6 and newer versions.
-
-        Deletes the RM site.
-      operationId: deleteRMSite
-      produces:
-        - application/json
-      responses:
-        '204':
-          description: Successful response
-        '400':
-          description: |
-            Invalid parameter: DELETE request is suported only for the RM site
-        '401':
-          description: Authentication failed
-        '403':
-          description: Current user does not have permission to delete the site that is visible to them.
-        '404':
-          description: |
-            RM site does not exist
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-    put:
-      tags:
-        - ig-sites
-      summary: Update the RM site
-      description: |
-        **Note:** this endpoint is available in RM 2.6 and newer versions.
-
-        Update the details for the RM site. Site Manager or otherwise a
-        (site) admin can update title or description.
-
-        **Note**: the id, site visibility or compliance of the RM site cannot be updated once the RM site has been created.
-
-      operationId: updateRMSite
-      produces:
-        - application/json
-      parameters:
-        - $ref: '#/parameters/fieldsParam'
-        - in: body
-          name: siteBodyUpdate
-          description: The RM site information to update.
-          required: true
-          schema:
-            $ref: '#/definitions/RMSiteBodyUpdate'
-      responses:
-        '200':
-          description: Successful response
-          schema:
-            $ref: '#/definitions/RMSiteEntry'
-        '400':
-          description: |
-            Invalid parameter: PUT request is suported only for the RM site, or **siteBodyUpdate** invalid
-        '401':
-          description: Authentication failed
-        '403':
-          description: Current user does not have permission to update the RM site that is visible to them.
-        '404':
-          description: |
-            RM site does not exist
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-  '/records/{recordId}/content':
-    get:
-      tags:
-        - records
-      summary: Get record content
-      description: |
-
-        Gets the content of the record with identifier **recordId**.
-      operationId: getRecordContent
-      parameters:
-        - $ref: '#/parameters/recordIdParam'
-        - $ref: '#/parameters/attachmentParam'
-        - $ref: '#/parameters/ifModifiedSinceHeader'
-      responses:
-        '200':
-          description: Successful response
-        '304':
-          description: Content has not been modified since the date provided in the If-Modified-Since header
-        '400':
-          description: |
-            Invalid parameter: **recordId** is not a valid format, or is not a record
-        '401':
-          description: Authentication failed
-        '404':
-          description: |
-            **recordId** does not exist
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-  '/records/{recordId}/file':
-    post:
-      tags:
-        - records
-      summary: File a record
-      description: |
-        Files the record **recordId** in the target record folder.
-
-        You can specify the target record folder by providing its id **targetParentId**
-        or by providing the id of a parent container **targetParentId** and a relative path **relativePath**.
-
-        The **relativePath** specifies the container structure relative to the node **targetParentId**.
-        If targetParentId is missing the path will be relative to the fileplan.
-        The relativePath is made of record containers and a record folder as the last element.
-        Containers that are missing from relativePath will be created before filing.
-
-        If the record is already filed, a link to the target record folder is created.
-      operationId: fileRecord
-      parameters:
-        - $ref: '#/parameters/recordIdParam'
-        - $ref: '#/parameters/FilePlanComponentEntryIncludeParam'
-        - $ref: '#/parameters/fieldsParam'
-        - in: body
-          name: nodeBodyFile
-          description: The target record folder id
-          required: true
-          schema:
-            $ref: '#/definitions/RequestBodyFile'
-      consumes:
-        - application/json
-      produces:
-        - application/json
-      responses:
-        '200':
-          description: Successful response
-          schema:
-            $ref: '#/definitions/FilePlanComponentEntry'
-        '400':
-          description: |
-            Invalid parameter: **recordIdParam** or **targetParentId** is not a valid format,
-            **recordIdParam** is not a record, **targetParentId** is not a record container or **nodeBodyFile** is invalid
-        '401':
-          description: Authentication failed
-        '403':
-          description: Current user does not have permission to create children of **nodeId**
-        '404':
-          description: |
-            **recordIdParam** or **targetParentId** does not exist
-        '422':
-          description: |
-            Model integrity exception: the action breaks system's integrity restrictions
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-  '/files/{fileId}/declare':
-    post:
-      tags:
-        - files
-      summary: Declare as record
-      description: Declares the file **fileId** in the unfiled record container.
-      operationId: declareRecord
-      parameters:
-        - name: fileId
-          in: path
-          description: The identifier of a non-record file.
-          required: true
-          type: string
-        - name: hideRecord
-          in: query
-          description: Flag to indicate whether the record should be hidden from the current parent folder.
-          type: boolean
-          default: false
-        - $ref: '#/parameters/FilePlanComponentEntryIncludeParam'
-        - $ref: '#/parameters/fieldsParam'
-      consumes:
-        - application/json
-      produces:
-        - application/json
-      responses:
-        '200':
-          description: Successful response
-          schema:
-            $ref: '#/definitions/FilePlanComponentEntry'
-        '400':
-          description: |
-            Invalid parameter: **fileId** is not a valid format
-        '401':
-          description: Authentication failed
-        '403':
-          description: Current user does not have permission to declare a record
-        '404':
-          description: |
-            **fileId** does not exist
-        '422':
-          description: |
-            Model integrity exception: the action breaks system's integrity restrictions
-        default:
-          description: Unexpected error
-          schema:
-            $ref: '#/definitions/Error'
-parameters:
-  fileplanComponentIdWithAliasParam:
-    name: fileplanComponentId
-    in: path
-    description: |
-      The identifier of a node. You can also use one of these well-known aliases:
-      * -filePlan-
-      * -transfers-
-      * -unfiled-
-      * -holds-
-    required: true
-    type: string
-  whereParam:
-    name: where
-    in: query
-    description: |
-      Optionally filter the list. Here are some examples:
-
-      *   ```where=(isFile=true)```
-
-      *   ```where=(nodeType='my:specialNodeType')```
-
-      *   ```where=(nodeType='my:specialNodeType' INCLUDESUBTYPES)```
-
-      *   ```where=(isPrimary=true)```
-
-      *   ```where=(assocType='my:specialAssocType')```
-
-    required: false
-    type: string
-  includeSourceParam:
-    name: includeSource
-    in: query
-    description: Also include **source** in addition to **entries** with folder information on the parent node – either the specified parent **fileplanComponentId**, or as resolved by **relativePath**.
-    required: false
-    type: boolean
-  relativePathParam:
-    name: relativePath
-    in: query
-    description: |
-      A path relative to the **fileplanComponentId**. If you set this,
-      information is returned on the node resolved by this path.
-    required: false
-    type: string
-  FilePlanComponentEntryIncludeParam:
-    name: include
-    in: query
-    description: |
-      Returns additional information about the fileplan component. Any optional field from the response model can be requested. For example:
-      * properties
-      * aspectNames
-      * hasRetentionSchedule
-      * isClosed
-      * isCompleted
-      * content
-      * allowableOperations
-      * path
-    required: false
-    type: array
-    items:
-      type: string
-    collectionFormat: csv
-  fileplanComponentIdParam:
-    name: fileplanComponentId
-    in: path
-    description: The identifier of a fileplan compoment.
-    required: true
-    type: string
-  recordIdParam:
-    name: recordId
-    in: path
-    description: The identifier of a record.
-    required: true
-    type: string
-  ## Core definition
-  fieldsParam:
-    name: fields
-    in: query
-    description: |
-      A list of field names.
-
-      You can use this parameter to restrict the fields
-      returned within a response if, for example, you want to save on overall bandwidth.
-
-      The list applies to a returned individual
-      entity or entries within a collection.
-
-      If the API method also supports the **include**
-      parameter, then the fields specified in the **include**
-      parameter are returned in addition to those specified in the **fields** parameter.
-    required: false
-    type: array
-    items:
-      type: string
-    collectionFormat: csv
-  ## Core definition
-  skipCountParam:
-    name: skipCount
-    in: query
-    description: The number of entities that exist in the collection before those included in this list.
-    required: false
-    type: integer
-    minimum: 0
-  ## Core definition
-  maxItemsParam:
-    name: maxItems
-    in: query
-    description: The maximum number of items to return in the list.
-    required: false
-    type: integer
-    minimum: 1
-  ## Core parameter
-  orderByParam:
-    name: orderBy
-    in: query
-    description: |
-      A string to control the order of the entities returned in a list. You can use the **orderBy** parameter to
-      sort the list by one or more fields.
-
-      Each field has a default sort order, which is normally acending order. Read the API method implementation notes
-      above to check if any fields used in this method have a descending default search order.
-
-      To sort the entities in a specific order, you can use the **ASC** and **DESC** keywords for any field.
-    required: false
-    type: array
-    items:
-      type: string
-    collectionFormat: csv
-  # Core definition
-  attachmentParam:
-    name: attachment
-    in: query
-    description: |
-       **true** enables a web browser to download the file as an attachment.
-       **false** means a web browser may preview the file in a new tab or window, but not
-       download the file.
-
-       You can only set this parameter to **false** if the content type of the file is in the supported list;
-       for example, certain image files and PDF files.
-
-       If the content type is not supported for preview, then a value of **false**  is ignored, and
-       the attachment will be returned in the response.
-    required: false
-    default: true
-    type: boolean
-  # Core definition
-  ifModifiedSinceHeader:
-    name: If-Modified-Since
-    in: header
-    description: |
-      Only returns the content if it has been modified since the date provided.
-      Use the date format defined by HTTP. For example, `Wed, 09 Mar 2016 16:56:34 GMT`.
-    required: false
-    type: string
-    format: date-time
-definitions:
-  FilePlanComponentEntry:
-    type: object
-    required:
-      - entry
-    properties:
-      entry:
-        $ref: '#/definitions/FilePlanComponent'
-  FilePlanComponent:
-    type: object
-    required:
-    - id
-    - name
-    - nodeType
-    - isCategory
-    - isRecordFolder
-    - isFile
-    - createdAt
-    - createdByUser
-    - modifiedAt
-    - modifiedByUser
-    - parentId
-    properties:
-      id:
-        type: string
-      parentId:
-        type: string
-      name:
-        type: string
-        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
-        description: |
-          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
-          The character . must not be used at the end of the name.
-      nodeType:
-        type: string
-      isCategory:
-        type: boolean
-      isRecordFolder:
-        type: boolean
-        default: false
-      isFile:
-        type: boolean
-        default: false
-      hasRetentionSchedule:
-        type: boolean
-        default: false
-        description: Present only for category nodes. Indicates if the category has a retention schedule defined
-      isClosed:
-        type: boolean
-        default: false
-        description: Present only for record folder nodes. Indicates if the record folder is closed
-      isCompleted:
-        type: boolean
-        default: false
-        description: Present only for record nodes. Indicates if the record has a retention schedule defined
-      modifiedAt:
-        type: string
-        format: date-time
-      modifiedByUser:
-        $ref: '#/definitions/UserInfo'
-      createdAt:
-        type: string
-        format: date-time
-      createdByUser:
-        $ref: '#/definitions/UserInfo'
-      content:
-        $ref: '#/definitions/ContentInfo'
-      aspectNames:
-        type: array
-        items:
-          type: string
-      properties:
-        type: object
-      allowableOperations:
-        type: array
-        items:
-          type: string
-      path:
-        $ref: '#/definitions/PathInfo'
-  FilePlanComponentAssociationPaging:
-    type: object
-    properties:
-      list:
-        type: object
-        properties:
-          pagination:
-            $ref: '#/definitions/Pagination'
-          entries:
-            type: array
-            items:
-              $ref: '#/definitions/FilePlanComponentChildAssociationEntry'
-          source:
-            $ref: '#/definitions/FilePlanComponent'
-  FilePlanComponentChildAssociationEntry:
-    type: object
-    required:
-      - entry
-    properties:
-      entry:
-        $ref: '#/definitions/FilePlanComponentChildAssociation'
-  FilePlanComponentChildAssociation:
-    allOf:
-      - $ref: '#/definitions/FilePlanComponent'
-      - type: object
-        properties:
-          association:
-            $ref: '#/definitions/ChildAssociationInfo'
-  FilePlanComponentBodyUpdate:
-    type: object
-    properties:
-      name:
-        type: string
-        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
-        description: |
-          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
-          The character . must not be used at the end of the name.
-      aspectNames:
-        type: array
-        items:
-          type: string
-      properties:
-        type: object
-        additionalProperties:
-          type: string
-  RequestBodyFile:
-    type: object
-    properties:
-      targetParentId:
-        type: string
-      relativePath:
-        type: string
-  ## Core definition
-  ChildAssociationInfo:
-    type: object
-    required:
-      - assocType
-      - isPrimary
-    properties:
-      assocType:
-        type: string
-      isPrimary:
-        type: boolean
-  ## Core definition
-  Pagination:
-    type: object
-    required:
-      - count
-      - hasMoreItems
-      - skipCount
-      - maxItems
-    properties:
-      count:
-        type: integer
-        format: int64
-        description: |
-          The number of objects in the entries array.
-      hasMoreItems:
-        type: boolean
-        description: |
-          A boolean value which is **true** if there are more entities in the collection
-          beyond those in this response. A true value means a request with a larger value
-          for the **skipCount** or the **maxItems** parameter will return more entities.
-      totalItems:
-        type: integer
-        format: int64
-        description: |
-          An integer describing the total number of entities in the collection.
-          The API might not be able to determine this value,
-          in which case this property will not be present.
-      skipCount:
-        type: integer
-        format: int64
-        description: |
-          An integer describing how many entities exist in the collection before
-          those included in this list.
-      maxItems:
-        type: integer
-        format: int64
-        description: |
-          The value of the **maxItems** parameter used to generate this list,
-          or if there was no **maxItems** parameter the default value is 100
-  ## Core definition
-  Error:
-    type: object
-    required:
-      - error
-    properties:
-      error:
-        type: object
-        required:
-          - statusCode
-          - briefSummary
-          - stackTrace
-          - descriptionURL
-        properties:
-          errorKey:
-            type: string
-          statusCode:
-            type: integer
-            format: int32
-          briefSummary:
-            type: string
-          stackTrace:
-            type: string
-          descriptionURL:
-            type: string
-          logId:
-            type: string
-  ## Core definition
-  UserInfo:
-    type: object
-    required:
-      - displayName
-      - id
-    properties:
-      displayName:
-        type: string
-      id:
-        type: string
-  ## Core definition
-  ContentInfo:
-    type: object
-    required:
-      - mimeType
-      - mimeTypeName
-      - sizeInBytes
-    properties:
-      mimeType:
-        type: string
-      mimeTypeName:
-        type: string
-      sizeInBytes:
-        type: integer
-      encoding:
-        type: string
-  ## Core definition
-  PathElement:
-    type: object
-    properties:
-      id:
-        type: string
-      name:
-        type: string
-  ## Core definition
-  PathInfo:
-    type: object
-    properties:
-      elements:
-        type: array
-        items:
-          $ref: '#/definitions/PathElement'
-      name:
-        type: string
-      isComplete:
-        type: boolean
-  RMNodeBodyCreate:
-    type: object
-    required:
-      - name
-      - nodeType
-    properties:
-      name:
-        type: string
-        pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
-        description: |
-          The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
-          The character . must not be used at the end of the name.
-      nodeType:
-        type: string
-      aspectNames:
-        type: array
-        items:
-          type: string
-      properties:
-        type: object
-        additionalProperties:
-          type: string
-      relativePath:
-        type: string
-  RMSiteBodyCreate:
-    type: object
-    required:
-      - title
-    properties:
-      title:
-        type: string
-      description:
-        type: string
-      compliance:
-        type: string
-        default: STANDARD
-        enum:
-          - STANDARD
-          - DOD5015
-  RMSiteBodyUpdate:
-    type: object
-    properties:
-      title:
-        type: string
-      description:
-        type: string
-  RMSiteEntry:
-    type: object
-    required:
-      - entry
-    properties:
-      entry:
-        $ref: '#/definitions/RMSite'
-  RMSite:
-    type: object
-    required:
-      - id
-      - guid
-      - title
-      - visibility
-      - compliance
-    properties:
-      id:
-        type: string
-      guid:
-        type: string
-      title:
-        type: string
-      description:
-        type: string
-      visibility:
-        type: string
-        enum:
-          - PRIVATE
-          - MODERATED
-          - PUBLIC
-      compliance:
-        type: string
-        enum:
-          - STANDARD
-          - DOD5015
-      role:
-        type: string
-        enum:
-          - SiteConsumer
-          - SiteCollaborator
-          - SiteContributor
-          - SiteManager
\ No newline at end of file
diff --git a/rm-community/rm-community-rest-api-explorer/src/main/webapp/index.html b/rm-community/rm-community-rest-api-explorer/src/main/webapp/index.html
index f20f567268..9739da746c 100644
--- a/rm-community/rm-community-rest-api-explorer/src/main/webapp/index.html
+++ b/rm-community/rm-community-rest-api-explorer/src/main/webapp/index.html
@@ -3,7 +3,7 @@
 
 
   
-  Alfresco API Explorer
+  Alfresco Governance Services REST API Explorer
   
   
   
@@ -11,6 +11,15 @@
   
   
   
+  
+  
+  
   
   
   
@@ -32,7 +41,7 @@