From cd851b0bae7967382a96e00010283004e95da80f Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Fri, 17 Mar 2023 15:11:01 -0400 Subject: [PATCH 01/60] Decouples Federated Persistence layer from controller and service layers. Introduces a unified persistence layer interface with a federated implementation. Added msosa, view, and oauth modules --- .../ArtifactsElementsHookSubscriber.java | 6 +- .../service/DefaultArtifactService.java | 64 ++-- authenticator/authenticator.gradle | 13 +- .../security/JwtTokenGenerator.java | 5 +- build.gradle | 3 + cameo/cameo.gradle | 1 + .../openmbee/mms/cameo/CameoConstants.java | 4 + .../cameo/services/CameoCommitService.java | 6 +- .../mms/cameo/services/CameoHelper.java | 4 +- .../mms/cameo/services/CameoNodeService.java | 81 ++-- .../cameo/services/CameoProjectService.java | 5 +- .../mms/cameo/services/CameoViewService.java | 145 +++++--- .../controllers/DocumentsResponse.java | 2 +- .../controllers/GroupsResponse.java | 2 +- .../controllers/MountsResponse.java | 2 +- .../controllers/VeController.java | 29 +- core/core.gradle | 14 +- .../PermissionUpdateResponseBuilder.java | 98 +---- .../openmbee/mms/core/config/Constants.java | 23 +- .../mms/core/config/ContextHolder.java | 2 +- .../org/openmbee/mms/core/config/Formats.java | 9 +- .../mms/core/dao/BranchPersistence.java | 21 ++ .../mms/core/dao/CommitPersistence.java | 32 ++ .../mms/core/dao/GroupPersistence.java | 14 + .../mms/core/dao/NodePersistence.java | 35 ++ .../openmbee/mms/core/dao/OrgPersistence.java | 19 + .../mms/core/dao/ProjectPersistence.java | 31 ++ .../mms/core/dao/UserGroupsPersistence.java | 15 + .../mms/core/dao/UserPersistence.java | 14 + .../core/delegation/PermissionsDelegate.java | 2 + .../PermissionsDelegateFactory.java | 12 +- .../mms/core/objects/PermissionResponse.java | 14 + .../core/security/MethodSecurityService.java | 49 +-- .../mms/core/services/BranchService.java | 2 + .../services/DefaultPermissionService.java | 265 ++++++------- .../core/services/GenericServiceFactory.java | 61 +++ .../services/HierarchicalNodeService.java | 11 + .../mms/core/services/NodeChangeInfo.java | 73 +--- .../mms/core/services/NodeChangeInfoImpl.java | 75 ++++ .../mms/core/services/NodeGetInfo.java | 95 +---- .../mms/core/services/NodeGetInfoImpl.java | 87 +++++ .../mms/core/services/NodeService.java | 8 - .../openmbee/mms/core/utils/ElementUtils.java | 7 + .../core/utils/PermissionsDelegateUtil.java | 70 ++++ .../org/openmbee/mms/crud/CrudConstants.java | 15 + .../mms/crud/controllers/BaseController.java | 29 +- .../branches/BranchesController.java | 13 +- .../elements/ElementsController.java | 5 +- .../crud/controllers/orgs/OrgsController.java | 62 ++-- .../projects/ProjectsController.java | 181 +++++---- .../mms/crud/domain/CommitDomain.java | 22 ++ .../mms/crud/domain/ElementDomain.java | 12 + .../openmbee/mms/crud/domain/JsonDomain.java | 28 ++ .../mms/crud/domain/NodeChangeDomain.java | 145 ++++++++ .../mms/crud/domain/NodeGetDomain.java | 58 +++ .../mms/crud/domain/NodeGetInfoFactory.java | 7 + .../crud/services/DefaultBranchService.java | 183 ++++----- .../crud/services/DefaultCommitService.java | 89 ++--- .../mms/crud/services/DefaultNodeService.java | 338 ++++++----------- .../crud/services/DefaultProjectService.java | 153 +++----- .../crud/services/ElementUtilsFactory.java | 27 ++ .../mms/crud/services/NodeDeleteHelper.java | 43 --- .../mms/crud/services/NodeGetHelper.java | 209 ----------- .../mms/crud/services/NodeOperation.java | 289 --------------- .../mms/crud/services/NodePostHelper.java | 121 ------ .../crud/services/ProjectDeleteService.java | 62 ++++ .../branches/BranchesControllerTest.java | 1 - .../services/DefaultCommitServiceTest.java | 350 ++++++++++++++++++ data/data.gradle | 3 + .../org/openmbee/mms/data}/dao/BranchDAO.java | 2 +- .../openmbee/mms/data}/dao/BranchGDAO.java | 2 +- .../mms/data}/dao/BranchIndexDAO.java | 2 +- .../org/openmbee/mms/data}/dao/CommitDAO.java | 2 +- .../mms/data}/dao/CommitIndexDAO.java | 2 +- .../org/openmbee/mms/data}/dao/NodeDAO.java | 3 +- .../openmbee/mms/data}/dao/NodeIndexDAO.java | 3 +- .../org/openmbee/mms/data}/dao/OrgDAO.java | 2 +- .../openmbee/mms/data}/dao/ProjectDAO.java | 12 +- .../openmbee/mms/data}/dao/ProjectIndex.java | 2 +- .../openmbee/mms/data}/dao/WebhookDAO.java | 2 +- .../mms/data/domains/global/Group.java | 1 + .../mms/data/domains/global/User.java | 20 - elastic/elastic.gradle | 7 + .../mms/elastic/BranchElasticDAOImpl.java | 2 +- .../mms/elastic/CommitElasticDAOImpl.java | 3 +- .../mms/elastic/NodeElasticDAOImpl.java | 3 +- .../mms/elastic/ProjectElasticImpl.java | 2 +- .../services/ElasticSearchService.java | 5 +- .../elastic_mappings/msosa_node.json | 110 ++++++ example/example.gradle | 3 + federatedpersistence/README.md | 3 + .../federatedpersistence.gradle | 11 + .../DefaultPermissionsDelegateConfig.java | 6 +- .../config/PermissionInit.java | 2 +- .../dao/FederatedBranchPersistence.java | 165 +++++++++ .../dao/FederatedCommitPersistence.java | 188 ++++++++++ .../dao/FederatedGroupPersistence.java | 64 ++++ .../dao/FederatedNodeChangeInfo.java | 17 + .../dao/FederatedNodeChangeInfoImpl.java | 60 +++ .../dao/FederatedNodeGetInfo.java | 18 + .../dao/FederatedNodeGetInfoImpl.java | 36 ++ .../dao/FederatedNodePersistence.java | 264 +++++++++++++ .../dao/FederatedOrgPersistence.java | 83 +++++ .../dao/FederatedProjectPersistence.java | 223 +++++++++++ .../dao/FederatedUserGroupsPersistence.java | 109 ++++++ .../dao/FederatedUserPersistence.java | 73 ++++ .../dao/FederatedWebhookPersistence.java | 129 +++++++ .../domain/FederatedElementDomain.java | 64 ++++ .../domain/FederatedNodeChangeDomain.java | 285 ++++++++++++++ .../domain/FederatedNodeGetDomain.java | 202 ++++++++++ .../AbstractDefaultPermissionsDelegate.java | 4 +- .../DefaultBranchPermissionsDelegate.java | 39 +- ...ltFederatedPermissionsDelegateFactory.java | 83 +++++ .../DefaultOrgPermissionsDelegate.java | 33 +- .../DefaultProjectPermissionsDelegate.java | 49 ++- ...ratedPermissionUpdatesResponseBuilder.java | 76 ++++ ...ratedPermissionsUpdateResponseBuilder.java | 99 +++++ .../exceptions/PermissionException.java | 2 +- .../utils/FederatedJsonUtils.java | 23 ++ gradle.properties | 6 +- groups/groups.gradle | 1 - .../mms/groups/constants/GroupConstants.java | 3 +- .../controllers/LocalGroupsController.java | 89 ++--- .../mms/groups/objects/GroupResponse.java | 16 +- .../services/GroupValidationService.java | 18 +- .../java/org/openmbee/mms/json/BaseJson.java | 13 + .../java/org/openmbee/mms/json/GroupJson.java | 11 + .../org/openmbee/mms/json/ProjectJson.java | 1 + .../java/org/openmbee/mms/json/UserJson.java | 82 ++++ .../mms/jupyter/services/JupyterHelper.java | 6 +- .../jupyter/services/JupyterNodeService.java | 17 +- ldap/ldap.gradle | 6 + .../openmbee/mms/ldap/LdapSecurityConfig.java | 69 ++-- localuser/localuser.gradle | 8 +- .../localuser/config/AuthProviderConfig.java | 2 +- .../config/UserPasswordRulesConfig.java | 16 + .../controllers/LocalUserController.java | 12 +- .../localuser/security/UserDetailsImpl.java | 20 +- .../security/UserDetailsServiceImpl.java | 57 +-- .../mms/localuser/security/UsersResponse.java | 10 +- msosa/README.md | 1 + msosa/msosa.gradle | 6 + .../openmbee/mms/msosa/MsosaConstants.java | 136 +++++++ .../org/openmbee/mms/msosa/MsosaNodeType.java | 47 +++ .../openmbee/mms/msosa/MsosaSchemaConfig.java | 15 + .../msosa/services/MsosaCommitService.java | 17 + .../mms/msosa/services/MsosaHelper.java | 161 ++++++++ .../mms/msosa/services/MsosaNodeService.java | 128 +++++++ .../msosa/services/MsosaProjectService.java | 67 ++++ .../mms/msosa/services/MsosaViewService.java | 258 +++++++++++++ oauth/oauth.gradle | 18 + .../mms/oauth/config/OAuthSecurityConfig.java | 32 ++ .../mms/oauth/constants/OAuthConstants.java | 22 ++ .../oauth/constants/OAuthErrorConstants.java | 15 + .../oauth/security/OAuth2Authentication.java | 55 +++ .../security/OAuthAuthenticationFilter.java | 53 +++ .../mms/oauth/security/OAuthProcessor.java | 210 +++++++++++ .../mms/oauth/security/OAuthUserDetails.java | 72 ++++ .../security/OAuthUserDetailsService.java | 53 +++ .../mms/oauth/util/OAuthTokenUtil.java | 82 ++++ permissions/permissions.gradle | 4 +- .../DefaultPermissionsDelegateFactory.java | 36 -- rdb/rdb.gradle | 1 + .../rdb/config/DatabaseDefinitionService.java | 30 +- .../mms/rdb/config/PersistenceJPAConfig.java | 4 +- .../mms/rdb/repositories/BaseDAOImpl.java | 2 +- .../mms/rdb/repositories/BranchGDAOImpl.java | 3 +- .../mms/rdb/repositories/OrgDAOImpl.java | 3 +- .../mms/rdb/repositories/ProjectDAOImpl.java | 16 +- .../rdb/repositories/ProjectRepository.java | 2 - .../mms/rdb/repositories/WebhookDAOImpl.java | 2 +- .../repositories/branch/BranchDAOImpl.java | 35 +- .../repositories/commit/CommitDAOImpl.java | 6 +- .../rdb/repositories/node/NodeDAOImpl.java | 10 +- storage/storage.gradle | 6 + .../org/openmbee/mms/twc/TeamworkCloud.java | 13 +- .../mms/twc/TeamworkCloudEndpoints.java | 2 +- .../openmbee/mms/twc/config/TwcConfig.java | 3 +- .../mms/twc/config/TwcPermissionsConfig.java | 2 +- .../maintenance/TWCMaintenanceController.java | 24 +- .../mms/twc/metadata/TwcMetadataService.java | 40 +- .../TwcBranchPermissionsDelegate.java | 22 +- .../TwcPermissionsDelegateFactory.java | 34 +- .../TwcProjectPermissionsDelegate.java | 22 +- .../security/TwcAuthenticationProvider.java | 13 +- .../mms/twc/security/TwcUserDetails.java | 19 +- .../twc/security/TwcUserDetailsService.java | 36 +- .../TwcRevisionMmsCommitMapService.java | 31 +- .../mms/twc/utilities/TwcPermissionUtils.java | 30 +- .../TwcPermissionsDelegateFactoryTest.java | 70 ++-- .../security/TwcAuthenticationFilterTest.java | 2 +- .../twc/utilities/TwcPermissionUtilsTest.java | 21 +- twc/twc.gradle | 6 +- view/README.md | 1 + .../view/controllers/DocumentsResponse.java | 24 ++ .../mms/view/controllers/GroupsResponse.java | 24 ++ .../mms/view/controllers/MountsResponse.java | 24 ++ .../mms/view/controllers/VeController.java | 124 +++++++ .../mms/view}/services/PropertyData.java | 30 +- .../mms/view/services/ViewService.java | 19 + view/view.gradle | 5 + .../webhooks/components/EventListener.java | 12 +- .../controllers/WebhooksController.java | 76 ++-- .../persistence/WebhookPersistence.java | 19 + webhooks/webhooks.gradle | 6 + 205 files changed, 6953 insertions(+), 2447 deletions(-) rename cameo/src/main/java/org/openmbee/mms/{cameo => view}/controllers/DocumentsResponse.java (92%) rename cameo/src/main/java/org/openmbee/mms/{cameo => view}/controllers/GroupsResponse.java (92%) rename cameo/src/main/java/org/openmbee/mms/{cameo => view}/controllers/MountsResponse.java (92%) rename cameo/src/main/java/org/openmbee/mms/{cameo => view}/controllers/VeController.java (73%) create mode 100644 core/src/main/java/org/openmbee/mms/core/dao/BranchPersistence.java create mode 100644 core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java create mode 100644 core/src/main/java/org/openmbee/mms/core/dao/GroupPersistence.java create mode 100644 core/src/main/java/org/openmbee/mms/core/dao/NodePersistence.java create mode 100644 core/src/main/java/org/openmbee/mms/core/dao/OrgPersistence.java create mode 100644 core/src/main/java/org/openmbee/mms/core/dao/ProjectPersistence.java create mode 100644 core/src/main/java/org/openmbee/mms/core/dao/UserGroupsPersistence.java create mode 100644 core/src/main/java/org/openmbee/mms/core/dao/UserPersistence.java create mode 100644 core/src/main/java/org/openmbee/mms/core/services/GenericServiceFactory.java create mode 100644 core/src/main/java/org/openmbee/mms/core/services/HierarchicalNodeService.java create mode 100644 core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java create mode 100644 core/src/main/java/org/openmbee/mms/core/services/NodeGetInfoImpl.java create mode 100644 core/src/main/java/org/openmbee/mms/core/utils/ElementUtils.java create mode 100644 core/src/main/java/org/openmbee/mms/core/utils/PermissionsDelegateUtil.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/CrudConstants.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/domain/CommitDomain.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/domain/ElementDomain.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetInfoFactory.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/services/ElementUtilsFactory.java delete mode 100644 crud/src/main/java/org/openmbee/mms/crud/services/NodeDeleteHelper.java delete mode 100644 crud/src/main/java/org/openmbee/mms/crud/services/NodeGetHelper.java delete mode 100644 crud/src/main/java/org/openmbee/mms/crud/services/NodeOperation.java delete mode 100644 crud/src/main/java/org/openmbee/mms/crud/services/NodePostHelper.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/services/ProjectDeleteService.java create mode 100644 crud/src/test/java/org/openmbee/mms/crud/services/DefaultCommitServiceTest.java rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/BranchDAO.java (89%) rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/BranchGDAO.java (87%) rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/BranchIndexDAO.java (94%) rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/CommitDAO.java (94%) rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/CommitIndexDAO.java (95%) rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/NodeDAO.java (89%) rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/NodeIndexDAO.java (95%) rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/OrgDAO.java (91%) rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/ProjectDAO.java (52%) rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/ProjectIndex.java (92%) rename {core/src/main/java/org/openmbee/mms/core => data/src/main/java/org/openmbee/mms/data}/dao/WebhookDAO.java (92%) create mode 100644 elastic/src/main/resources/elastic_mappings/msosa_node.json create mode 100644 federatedpersistence/README.md create mode 100644 federatedpersistence/federatedpersistence.gradle rename {permissions/src/main/java/org/openmbee/mms/permissions => federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence}/config/DefaultPermissionsDelegateConfig.java (64%) rename {permissions/src/main/java/org/openmbee/mms/permissions => federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence}/config/PermissionInit.java (98%) create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedBranchPersistence.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedGroupPersistence.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeChangeInfo.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeChangeInfoImpl.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeGetInfo.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeGetInfoImpl.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedOrgPersistence.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserGroupsPersistence.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserPersistence.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedWebhookPersistence.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedElementDomain.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java rename {permissions/src/main/java/org/openmbee/mms/permissions/delegation => federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions}/AbstractDefaultPermissionsDelegate.java (95%) rename {permissions/src/main/java/org/openmbee/mms/permissions/delegation => federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions}/DefaultBranchPermissionsDelegate.java (92%) create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultFederatedPermissionsDelegateFactory.java rename {permissions/src/main/java/org/openmbee/mms/permissions/delegation => federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions}/DefaultOrgPermissionsDelegate.java (91%) rename {permissions/src/main/java/org/openmbee/mms/permissions/delegation => federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions}/DefaultProjectPermissionsDelegate.java (92%) create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionUpdatesResponseBuilder.java create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionsUpdateResponseBuilder.java rename {permissions/src/main/java/org/openmbee/mms => federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence}/permissions/exceptions/PermissionException.java (78%) create mode 100644 federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/utils/FederatedJsonUtils.java create mode 100644 json/src/main/java/org/openmbee/mms/json/GroupJson.java create mode 100644 json/src/main/java/org/openmbee/mms/json/UserJson.java create mode 100644 localuser/src/main/java/org/openmbee/mms/localuser/config/UserPasswordRulesConfig.java create mode 100644 msosa/README.md create mode 100644 msosa/msosa.gradle create mode 100644 msosa/src/main/java/org/openmbee/mms/msosa/MsosaConstants.java create mode 100644 msosa/src/main/java/org/openmbee/mms/msosa/MsosaNodeType.java create mode 100644 msosa/src/main/java/org/openmbee/mms/msosa/MsosaSchemaConfig.java create mode 100644 msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaCommitService.java create mode 100644 msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaHelper.java create mode 100644 msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaNodeService.java create mode 100644 msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaProjectService.java create mode 100644 msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaViewService.java create mode 100644 oauth/oauth.gradle create mode 100644 oauth/src/main/java/org/openmbee/mms/oauth/config/OAuthSecurityConfig.java create mode 100644 oauth/src/main/java/org/openmbee/mms/oauth/constants/OAuthConstants.java create mode 100644 oauth/src/main/java/org/openmbee/mms/oauth/constants/OAuthErrorConstants.java create mode 100644 oauth/src/main/java/org/openmbee/mms/oauth/security/OAuth2Authentication.java create mode 100644 oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthAuthenticationFilter.java create mode 100644 oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthProcessor.java create mode 100644 oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetails.java create mode 100644 oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetailsService.java create mode 100644 oauth/src/main/java/org/openmbee/mms/oauth/util/OAuthTokenUtil.java delete mode 100644 permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultPermissionsDelegateFactory.java create mode 100644 view/README.md create mode 100644 view/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java create mode 100644 view/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java create mode 100644 view/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java create mode 100644 view/src/main/java/org/openmbee/mms/view/controllers/VeController.java rename {cameo/src/main/java/org/openmbee/mms/cameo => view/src/main/java/org/openmbee/mms/view}/services/PropertyData.java (61%) create mode 100644 view/src/main/java/org/openmbee/mms/view/services/ViewService.java create mode 100644 view/view.gradle create mode 100644 webhooks/src/main/java/org/openmbee/mms/webhooks/persistence/WebhookPersistence.java diff --git a/artifacts/src/main/java/org/openmbee/mms/artifacts/pubsub/ArtifactsElementsHookSubscriber.java b/artifacts/src/main/java/org/openmbee/mms/artifacts/pubsub/ArtifactsElementsHookSubscriber.java index a606360ac..e9c7da19c 100644 --- a/artifacts/src/main/java/org/openmbee/mms/artifacts/pubsub/ArtifactsElementsHookSubscriber.java +++ b/artifacts/src/main/java/org/openmbee/mms/artifacts/pubsub/ArtifactsElementsHookSubscriber.java @@ -22,9 +22,9 @@ public void acceptHook(Object payload) { ElementUpdateHook elementUpdateHook = (ElementUpdateHook) payload; if(elementUpdateHook.getElements() != null) { //Ignore any artifact changes coming in outside of the Artifacts Controller - elementUpdateHook.getElements().parallelStream().forEach(v -> { - v.remove(ArtifactJson.ARTIFACTS); - }); + elementUpdateHook.getElements().parallelStream().forEach(v -> + v.remove(ArtifactJson.ARTIFACTS) + ); } } } diff --git a/artifacts/src/main/java/org/openmbee/mms/artifacts/service/DefaultArtifactService.java b/artifacts/src/main/java/org/openmbee/mms/artifacts/service/DefaultArtifactService.java index 5c4aae384..8cffaaf18 100644 --- a/artifacts/src/main/java/org/openmbee/mms/artifacts/service/DefaultArtifactService.java +++ b/artifacts/src/main/java/org/openmbee/mms/artifacts/service/DefaultArtifactService.java @@ -3,7 +3,7 @@ import org.openmbee.mms.artifacts.storage.ArtifactStorage; import org.openmbee.mms.artifacts.ArtifactConstants; import org.openmbee.mms.artifacts.objects.ArtifactResponse; -import org.openmbee.mms.core.dao.ProjectDAO; +import org.openmbee.mms.core.dao.ProjectPersistence; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.exceptions.ConflictException; import org.openmbee.mms.core.exceptions.NotFoundException; @@ -11,9 +11,9 @@ import org.openmbee.mms.core.objects.ElementsResponse; import org.openmbee.mms.core.services.NodeService; import org.openmbee.mms.crud.services.ServiceFactory; -import org.openmbee.mms.data.domains.global.Project; import org.openmbee.mms.artifacts.json.ArtifactJson; import org.openmbee.mms.json.ElementJson; +import org.openmbee.mms.json.ProjectJson; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; @@ -30,7 +30,7 @@ public class DefaultArtifactService implements ArtifactService { private ArtifactStorage artifactStorage; private ServiceFactory serviceFactory; - private ProjectDAO projectRepository; + private ProjectPersistence projectPersistence; @Autowired public void setArtifactStorage(ArtifactStorage artifactStorage) { @@ -43,16 +43,19 @@ public void setServiceFactory(ServiceFactory serviceFactory) { } @Autowired - public void setProjectRepository(ProjectDAO projectRepository) { - this.projectRepository = projectRepository; + public void setProjectPersistence(ProjectPersistence projectPersistence) { + this.projectPersistence = projectPersistence; } @Override public ArtifactResponse get(String projectId, String refId, String id, Map params) { NodeService nodeService = getNodeService(projectId); ElementJson elementJson = getElement(nodeService, projectId, refId, id, params); - - ArtifactJson artifact = getExistingArtifact(ArtifactJson.getArtifacts(elementJson), params, elementJson); + List artifacts = ArtifactJson.getArtifacts(elementJson); + ArtifactJson artifact = new ArtifactJson(); + if (artifacts != null) { + artifact = getExistingArtifact(artifacts, params); + } byte[] data = artifactStorage.get(artifact.getLocation(), elementJson, artifact.getMimeType()); ArtifactResponse response = new ArtifactResponse(); response.setData(data); @@ -99,7 +102,10 @@ public ElementsResponse disassociate(String projectId, String refId, String id, ElementJson elementJson = getElement(nodeService, projectId, refId, id, params); List artifacts = ArtifactJson.getArtifacts(elementJson); - ArtifactJson artifact = getExistingArtifact(artifacts, params, elementJson); + if (artifacts == null) { + throw new NotFoundException("Artifacts not found"); + } + ArtifactJson artifact = getExistingArtifact(artifacts, params); artifacts.remove(artifact); ArtifactJson.setArtifacts(elementJson, artifacts); ElementsRequest elementsRequest = new ElementsRequest(); @@ -110,9 +116,9 @@ public ElementsResponse disassociate(String projectId, String refId, String id, private ElementJson getElement(NodeService nodeService, String projectId, String refId, String id, Map params) { ElementsResponse elementsResponse = nodeService.read(projectId, refId, id, params); - if(elementsResponse.getElements() == null || elementsResponse.getElements().isEmpty()) { + if (elementsResponse.getElements() == null || elementsResponse.getElements().isEmpty()) { throw new NotFoundException("Element not found"); - } else if(elementsResponse.getElements().size() > 1) { + } else if (elementsResponse.getElements().size() > 1) { throw new ConflictException("Multiple elements found with id " + id); } else { return elementsResponse.getElements().get(0); @@ -124,10 +130,18 @@ private ElementJson attachOrUpdateArtifact(ElementJson elementJson, String artif List artifacts = ArtifactJson.getArtifacts(elementJson); ArtifactJson artifact; try { - artifact = getExistingArtifact(artifacts, mimeType, null, elementJson); + //nested try/nullcheck OK? + if (artifacts != null) { + artifact = getExistingArtifact(artifacts, mimeType, null); + } else { + throw new NotFoundException("Null artifact exception"); + } + } catch(NotFoundException ex) { artifact = new ArtifactJson(); - artifacts.add(artifact); + if (artifacts != null) { + artifacts.add(artifact); + } } artifact.setLocation(artifactLocation); @@ -140,29 +154,29 @@ private ElementJson attachOrUpdateArtifact(ElementJson elementJson, String artif return elementJson; } - private ArtifactJson getExistingArtifact(List artifacts, Map params, ElementJson element) { - return getExistingArtifact(artifacts, params.get(ArtifactConstants.MIMETYPE_PARAM), params.get(ArtifactConstants.EXTENSION_PARAM), element); + private ArtifactJson getExistingArtifact(List artifacts, Map params) { + return getExistingArtifact(artifacts, params.get(ArtifactConstants.MIMETYPE_PARAM), params.get(ArtifactConstants.EXTENSION_PARAM)); } - private ArtifactJson getExistingArtifact(List artifacts, String mimeType, String extension, ElementJson element) { - if(mimeType == null && extension == null) { + private ArtifactJson getExistingArtifact(List artifacts, String mimeType, String extension) { + if (mimeType == null && extension == null) { throw new BadRequestException("Missing mimetype or extension"); } //Element representation is unique by mimeType and extension - Optional existing = artifacts.stream().filter(v -> { - return (mimeType != null && mimeType.equals(v.getMimeType())) || (extension != null && extension.equals(v.getExtension())); - }).findFirst(); - if(existing.isPresent()) { + Optional existing = artifacts.stream().filter(v -> + (mimeType != null && mimeType.equals(v.getMimeType())) || (extension != null && extension.equals(v.getExtension())) + ).findFirst(); + if (existing.isPresent()) { return existing.get(); } - throw new NotFoundException(element); + throw new NotFoundException("Artifact not found"); } private String getFileExtension(MultipartFile file) { String originalFilename = file.getOriginalFilename(); - if(originalFilename != null) { + if (originalFilename != null) { int inx = originalFilename.lastIndexOf('.'); - if(inx > 0) { + if (inx > 0) { return originalFilename.substring(inx + 1); } } @@ -191,8 +205,8 @@ private String getProjectType(String projectId) { return getProject(projectId).getProjectType(); } - private Project getProject(String projectId) { - Optional p = projectRepository.findByProjectId(projectId); + private ProjectJson getProject(String projectId) { + Optional p = projectPersistence.findById(projectId); if (p.isPresent()) { return p.get(); } diff --git a/authenticator/authenticator.gradle b/authenticator/authenticator.gradle index 82e02c61d..ca8f17887 100644 --- a/authenticator/authenticator.gradle +++ b/authenticator/authenticator.gradle @@ -1,14 +1,19 @@ dependencies { - implementation project(':rdb') api project(':core') api commonDependencies.'spring-security-web' implementation commonDependencies.'servlet-api' - implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.10.5' + implementation commonDependencies.'jjwt-api' - runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.10.5' - runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.10.5' + runtimeOnly commonDependencies.'jjwt-impl' + runtimeOnly commonDependencies.'jjwt-jackson' testImplementation commonDependencies.'spring-boot-starter-test' } + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } +} \ No newline at end of file diff --git a/authenticator/src/main/java/org/openmbee/mms/authenticator/security/JwtTokenGenerator.java b/authenticator/src/main/java/org/openmbee/mms/authenticator/security/JwtTokenGenerator.java index 2c7fbce00..a667211b1 100644 --- a/authenticator/src/main/java/org/openmbee/mms/authenticator/security/JwtTokenGenerator.java +++ b/authenticator/src/main/java/org/openmbee/mms/authenticator/security/JwtTokenGenerator.java @@ -101,7 +101,10 @@ private Date generateExpirationDate() { private boolean isTokenExpired(String token) { final Date expirationDate = getExpirationDateFromToken(token); - return expirationDate.before(new Date()); + if (expirationDate != null) { + return expirationDate.before(new Date()); + } + return true; } @Override diff --git a/build.gradle b/build.gradle index 6dc76ae86..529b26a1e 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,9 @@ ext { 'jackson-databind' : "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion", 'jackson-datatype-hibernate5' : "com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonVersion", 'jackson-datatype-jsr310' : "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion", + 'jjwt-api' : "io.jsonwebtoken:jjwt-api:$jwtTokenVersion", + 'jjwt-impl' : "io.jsonwebtoken:jjwt-impl:$jwtTokenVersion", + 'jjwt-jackson' : "io.jsonwebtoken:jjwt-jackson:$jwtTokenVersion", 'spring-boot-starter-test' : 'org.springframework.boot:spring-boot-starter-test:2.2.4.RELEASE' ] } diff --git a/cameo/cameo.gradle b/cameo/cameo.gradle index 21102b288..8884015e7 100644 --- a/cameo/cameo.gradle +++ b/cameo/cameo.gradle @@ -1,5 +1,6 @@ dependencies { implementation project(':crud') + implementation project(':view') testImplementation commonDependencies.'spring-boot-starter-test' } diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/CameoConstants.java b/cameo/src/main/java/org/openmbee/mms/cameo/CameoConstants.java index 43ce9110f..b49db39d7 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/CameoConstants.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/CameoConstants.java @@ -14,6 +14,7 @@ public class CameoConstants { public static final String CLASSIFIERIDS = "classifierIds"; public static final String CLIENTDEPENDENCYIDS = "clientDependencyIds"; public static final String COLLABORATIONUSEIDS = "collaborationUseIds"; + public static final String COMMITID = "commitId"; public static final String DATATYPEID = "datatypeId"; public static final String DEFAULTVALUE = "defaultValue"; public static final String DEFININGFEATUREID = "definingFeatureId"; @@ -46,12 +47,15 @@ public class CameoConstants { public static final String NAME = "name"; public static final String NAMEEXPRESSION = "nameExpression"; public static final String NAVIGABLEOWNEDENDIDS = "navigableOwnedEndIds"; + public static final String NONE = "none"; public static final String OWNEDATTRIBUTEIDS = "ownedAttributeIds"; public static final String OWNEDENDIDS = "ownedEndIds"; public static final String OWNERID = "ownerId"; public static final String PACKAGEIMPORTIDS = "packageImportIds"; public static final String PACKAGEMERGEIDS = "packageMergeIds"; + public static final String PARENTID = "_parentId"; public static final String POWERTYPEEXTENTIDS = "powertypeExtentIds"; + public static final String PROPERTY = "Property"; public static final String PROPERTYID = "propertyId"; public static final String PROPERTYTYPE = "propertyType"; public static final String PROFILEAPPLICATIONIDS = "profileApplicationIds"; diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoCommitService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoCommitService.java index 4994e106b..35c326ecc 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoCommitService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoCommitService.java @@ -1,9 +1,8 @@ package org.openmbee.mms.cameo.services; -import org.openmbee.mms.core.config.ContextHolder; import org.openmbee.mms.core.services.CommitService; import org.openmbee.mms.crud.services.DefaultCommitService; -import org.openmbee.mms.data.domains.scoped.Commit; +import org.openmbee.mms.json.CommitJson; import org.springframework.stereotype.Service; import java.util.List; @@ -12,8 +11,7 @@ public class CameoCommitService extends DefaultCommitService implements CommitService { @Override public boolean isProjectNew(String projectId) { - ContextHolder.setContext(projectId); - List commits = commitRepository.findAll(); + List commits = commitPersistence.findAllByProjectId(projectId); return commits == null || commits.size() <= 1; // a cameo project gets 1 auto commit, so its still "new" } } diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoHelper.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoHelper.java index 337ae09e3..10dbe1ce3 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoHelper.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoHelper.java @@ -6,12 +6,14 @@ import java.util.Set; import org.openmbee.mms.cameo.CameoConstants; import org.openmbee.mms.cameo.CameoNodeType; +import org.openmbee.mms.core.utils.ElementUtils; import org.openmbee.mms.json.ElementJson; import org.springframework.stereotype.Component; @Component -public class CameoHelper { +public class CameoHelper implements ElementUtils { + @Override public CameoNodeType getNodeType(ElementJson e) { if (isDocument(e)) { return CameoNodeType.DOCUMENT; diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java index 0e68cf134..53a04e220 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java @@ -1,24 +1,18 @@ package org.openmbee.mms.cameo.services; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.openmbee.mms.cameo.CameoNodeType; import org.openmbee.mms.cameo.CameoConstants; -import org.openmbee.mms.core.config.ContextHolder; +import org.openmbee.mms.cameo.CameoNodeType; import org.openmbee.mms.core.config.Privileges; +import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.core.objects.ElementsRequest; +import org.openmbee.mms.core.objects.ElementsResponse; import org.openmbee.mms.core.security.MethodSecurityService; +import org.openmbee.mms.core.services.HierarchicalNodeService; import org.openmbee.mms.core.services.NodeChangeInfo; import org.openmbee.mms.core.services.NodeGetInfo; -import org.openmbee.mms.json.ElementJson; -import org.openmbee.mms.core.objects.ElementsResponse; import org.openmbee.mms.crud.services.DefaultNodeService; -import org.openmbee.mms.core.services.NodeService; -import org.openmbee.mms.data.domains.scoped.Node; +import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.ElementJson; import org.openmbee.mms.json.MountJson; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.util.Pair; @@ -26,8 +20,10 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; +import java.util.*; + @Service("cameoNodeService") -public class CameoNodeService extends DefaultNodeService implements NodeService { +public class CameoNodeService extends DefaultNodeService implements HierarchicalNodeService { protected CameoHelper cameoHelper; private MethodSecurityService mss; @@ -46,22 +42,28 @@ public void setMss(MethodSecurityService mss) { public ElementsResponse read(String projectId, String refId, ElementsRequest req, Map params) { - String commitId = params.getOrDefault("commitId", null); - ContextHolder.setContext(projectId, refId); - NodeGetInfo info = nodeGetHelper.processGetJson(req.getElements(), commitId, this); + String commitId = params.getOrDefault(CameoConstants.COMMITID, null); + if (commitId == null) { + Optional commitJson = commitPersistence.findLatestByProjectAndRef(projectId, refId); + if (!commitJson.isPresent()) { + throw new InternalErrorException("Could not find latest commit for project and ref"); + } + commitId = commitJson.get().getId(); + } + NodeGetInfo info = getNodePersistence().findAll(projectId, refId, commitId, req.getElements()); + if (!info.getRejected().isEmpty()) { //continue looking in visible mounted projects for elements if not all found NodeGetInfo curInfo = info; List> usages = new ArrayList<>(); - getProjectUsages(projectId, refId, commitId, usages); + getProjectUsages(projectId, refId, commitId, usages, true); int i = 1; //0 is entry project, already gotten while (!curInfo.getRejected().isEmpty() && i < usages.size()) { ElementsRequest reqNext = buildRequest(curInfo.getRejected().keySet()); - ContextHolder.setContext(usages.get(i).getFirst(), usages.get(i).getSecond()); - //TODO use the right commitId in child if commitId is present in params - curInfo = nodeGetHelper.processGetJson(reqNext.getElements(), "", this); + //TODO use the right commitId in child if commitId is present in params :: same commit Id is not working for child + curInfo = getNodePersistence().findAll(usages.get(i).getFirst(), usages.get(i).getSecond(), "", reqNext.getElements()); info.getActiveElementMap().putAll(curInfo.getActiveElementMap()); curInfo.getActiveElementMap().forEach((id, json) -> info.getRejected().remove(id)); curInfo.getRejected().forEach((id, rejection) -> { @@ -80,38 +82,36 @@ public ElementsResponse read(String projectId, String refId, ElementsRequest req } @Override - public void extraProcessPostedElement(ElementJson element, Node node, NodeChangeInfo info) { - node.setNodeType(cameoHelper.getNodeType(element).getValue()); + public void extraProcessPostedElement(NodeChangeInfo info, ElementJson element) { //remove _childViews if posted element.remove(CameoConstants.CHILDVIEWS); } - @Override - public void extraProcessGotElement(ElementJson element, Node node, NodeGetInfo info) { - //TODO extended info? (qualified name/id) - } - - public MountJson getProjectUsages(String projectId, String refId, String commitId, List> saw) { - ContextHolder.setContext(projectId, refId); + public MountJson getProjectUsages(String projectId, String refId, String commitId, List> saw, + boolean restrictOnPermissions) { saw.add(Pair.of(projectId, refId)); - List mountNodes = nodeRepository.findAllByNodeType(CameoNodeType.PROJECTUSAGE.getValue()); - Set mountIds = new HashSet<>(); - mountNodes.forEach(n -> mountIds.add(n.getNodeId())); - Map params = new HashMap<>(); - params.put("commitId", commitId); - ElementsResponse mountsJson = super.read(projectId, refId, buildRequest(mountIds), params); + List mounts = getNodePersistence().findAllByNodeType(projectId, refId, commitId, + CameoNodeType.PROJECTUSAGE.getValue()); Authentication auth = SecurityContextHolder.getContext().getAuthentication(); List mountValues = new ArrayList<>(); - for (ElementJson mount: mountsJson.getElements()) { + for (ElementJson mount : mounts) { String mountedProjectId = (String)mount.get(CameoConstants.MOUNTEDELEMENTPROJECTID); String mountedRefId = (String)mount.get(CameoConstants.MOUNTEDREFID); + if (mountedProjectId == null) { + logger.error("Could not find Mounted Project Id"); + continue; + } + if (mountedRefId == null) { + logger.error("Could not find Mounted Ref Id for Project ID: {}" , mountedProjectId); + continue; + } if (saw.contains(Pair.of(mountedProjectId, mountedRefId))) { //prevent circular dependencies or dups - should it be by project or by project and ref? continue; } try { - if (!mss.hasBranchPrivilege(auth, mountedProjectId, mountedRefId, + if (restrictOnPermissions && !mss.hasBranchPrivilege(auth, mountedProjectId, mountedRefId, Privileges.BRANCH_READ.name(), true)) { //should permission be considered here? continue; @@ -120,7 +120,12 @@ public MountJson getProjectUsages(String projectId, String refId, String commitI continue; } //doing a depth first traversal TODO get appropriate commitId - mountValues.add(getProjectUsages(mountedProjectId, mountedRefId, "", saw)); + try { + mountValues.add(getProjectUsages(mountedProjectId, mountedRefId, "", saw, restrictOnPermissions)); + } catch (Exception e) { + //log the error and move on + logger.debug(String.format("Could not get project usages from nested project %s" , mountedProjectId), e); + } } MountJson res = new MountJson(); res.setId(projectId); diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoProjectService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoProjectService.java index f11da5116..a080b71ea 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoProjectService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoProjectService.java @@ -47,11 +47,10 @@ public ProjectJson create(ProjectJson project) { } @Override - public RefJson createRefJson(ProjectJson project, String docId){ - RefJson branchJson = super.createRefJson(project, docId); + public RefJson createMasterRefJson(ProjectJson project){ + RefJson branchJson = super.createMasterRefJson(project); branchJson.put("twcId",Constants.MASTER_BRANCH); return branchJson; - } private static ElementJson createNode(String id, String name, ProjectJson projectJson) { diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java index c878ee5cb..ff85d0a3a 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java @@ -10,44 +10,51 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; + import org.openmbee.mms.cameo.CameoConstants; import org.openmbee.mms.cameo.CameoNodeType; import org.openmbee.mms.core.config.ContextHolder; +import org.openmbee.mms.core.dao.NodePersistence; import org.openmbee.mms.core.objects.ElementsRequest; import org.openmbee.mms.core.objects.ElementsResponse; import org.openmbee.mms.core.services.NodeChangeInfo; import org.openmbee.mms.core.services.NodeGetInfo; -import org.openmbee.mms.data.domains.scoped.Node; import org.openmbee.mms.json.ElementJson; +import org.openmbee.mms.view.services.PropertyData; +import org.openmbee.mms.view.services.ViewService; import org.springframework.stereotype.Service; @Service("cameoViewService") -public class CameoViewService extends CameoNodeService { +public class CameoViewService extends CameoNodeService implements ViewService { + @Override public ElementsResponse getDocuments(String projectId, String refId, Map params) { - ContextHolder.setContext(projectId, refId); - List documents = this.nodeRepository.findAllByNodeType(CameoNodeType.DOCUMENT.getValue()); - ElementsResponse res = this.getViews(projectId, refId, buildRequest(nodeGetHelper.convertNodesToMap(documents).keySet()), params); + String commitId = params.getOrDefault(CameoConstants.COMMITID, null); + List documents = getNodePersistence().findAllByNodeType(projectId, refId, + commitId, CameoNodeType.DOCUMENT.getValue()); + ElementsResponse res = this.getViews(projectId, refId, buildRequestFromJsons(documents), params); for (ElementJson e: res.getElements()) { - Optional parent = nodeGetHelper.getFirstRelationshipOfType(e, - Arrays.asList(CameoNodeType.GROUP.getValue()), CameoConstants.OWNERID); - if (parent.isPresent()) { - e.put("_groupId", parent.get().getId()); - } + Optional parent = getFirstRelationshipOfType(projectId, refId, commitId, e, + List.of(CameoNodeType.GROUP.getValue()), CameoConstants.OWNERID); + parent.ifPresent(elementJson -> e.put(CameoConstants.SITECHARACTERIZATIONID, elementJson.getId())); + } return res; } + @Override public ElementsResponse getView(String projectId, String refId, String elementId, Map params) { return this.getViews(projectId, refId, buildRequest(elementId), params); } + @Override public ElementsResponse getViews(String projectId, String refId, ElementsRequest req, Map params) { ElementsResponse res = this.read(projectId, refId, req, params); addChildViews(res, params); return res; } + @Override public void addChildViews(ElementsResponse res, Map params) { for (ElementJson element: res.getElements()) { if (cameoHelper.isView(element)) { @@ -57,11 +64,11 @@ public void addChildViews(ElementsResponse res, Map params) { } ElementsResponse ownedAttributes = this.read(element.getProjectId(), element.getRefId(), buildRequest(ownedAttributeIds), params); - List sorted = nodeGetHelper.sort(ownedAttributeIds, ownedAttributes.getElements()); + List filtered = filter(ownedAttributeIds, ownedAttributes.getElements()); List childViews = new ArrayList<>(); - for (ElementJson attr : sorted) { + for (ElementJson attr : filtered) { String childId = (String) attr.get(CameoConstants.TYPEID); - if ("Property".equals(attr.getType()) && childId != null && !childId.isEmpty()) { + if (CameoConstants.PROPERTY.equals(attr.getType()) && childId != null && !childId.isEmpty()) { Map child = new HashMap<>(); child.put(ElementJson.ID, childId); child.put(CameoConstants.AGGREGATION, (String) attr.get(CameoConstants.AGGREGATION)); @@ -75,42 +82,47 @@ public void addChildViews(ElementsResponse res, Map params) { } public ElementsResponse getGroups(String projectId, String refId, Map params) { - ContextHolder.setContext(projectId, refId); - List groups = this.nodeRepository.findAllByNodeType(CameoNodeType.GROUP.getValue()); - ElementsResponse res = this.read(projectId, refId, buildRequest(nodeGetHelper.convertNodesToMap(groups).keySet()), params); + String commitId = params.getOrDefault(CameoConstants.COMMITID, null); + List groups = getNodePersistence().findAllByNodeType(projectId, refId, commitId, + CameoNodeType.GROUP.getValue()); + + ElementsResponse res = this.read(projectId, refId, buildRequestFromJsons(groups), params); for (ElementJson e: res.getElements()) { - Optional parent = nodeGetHelper.getFirstRelationshipOfType(e, - Arrays.asList(CameoNodeType.GROUP.getValue()), CameoConstants.OWNERID); - if (parent.isPresent()) { - e.put("_parentId", parent.get().getId()); - } + Optional parent = getFirstRelationshipOfType(projectId, refId, commitId, e, + List.of(CameoNodeType.GROUP.getValue()), CameoConstants.OWNERID); + parent.ifPresent(elementJson -> e.put(CameoConstants.PARENTID, elementJson.getId())); } return res; } @Override - public void extraProcessPostedElement(ElementJson element, Node node, NodeChangeInfo info) { + public void extraProcessPostedElement(NodeChangeInfo info, ElementJson element) { //handle _childViews List> newChildViews = (List)element.remove(CameoConstants.CHILDVIEWS); if (newChildViews == null) { - super.extraProcessPostedElement(element, node, info); + super.extraProcessPostedElement(info, element); return; } //gather data on "old" attributes List oldOwnedAttributeIds = (List)element.get(CameoConstants.OWNEDATTRIBUTEIDS); //use helper to get access to Nodes - NodeGetInfo oldInfo = nodeGetHelper.processGetJson(buildRequest(oldOwnedAttributeIds).getElements(), null); + String projectId = info.getCommitJson().getProjectId(); + String refId = info.getCommitJson().getRefId(); + NodeGetInfo oldInfo = getNodePersistence().findAll(projectId, refId, null, + buildRequest(oldOwnedAttributeIds).getElements()); List oldProperties = new ArrayList<>(); Map oldPropertiesTypeMapping = new HashMap<>(); //typeId to PropertyData for (String oldOwnedAttributeId: oldOwnedAttributeIds) { if (!oldInfo.getActiveElementMap().containsKey(oldOwnedAttributeId)) { continue; //property doesn't exist anymore? indicates existing model inconsistency } - Node oldNode = oldInfo.getExistingNodeMap().get(oldOwnedAttributeId); + //TODO This probably breaks view editor. move to federated domain somehow + //Node oldNode = oldInfo.getExistingNodeMap().get(oldOwnedAttributeId); ElementJson oldJson = oldInfo.getActiveElementMap().get(oldOwnedAttributeId); PropertyData oldData = new PropertyData(); oldData.setPropertyJson(oldJson); - oldData.setPropertyNode(oldNode); + //TODO This probably breaks view editor. move to federated domain somehow + //oldData.setPropertyNode(oldNode); oldProperties.add(oldData); String typeId = (String)oldJson.get(CameoConstants.TYPEID); if (typeId == null || typeId.isEmpty()) { @@ -120,7 +132,7 @@ public void extraProcessPostedElement(ElementJson element, Node node, NodeChange } //include project usages when finding types ElementsResponse oldTypeJsons = this.read(element.getProjectId(), element.getRefId(), - buildRequest(oldPropertiesTypeMapping.keySet()), Collections.EMPTY_MAP); + buildRequest(oldPropertiesTypeMapping.keySet()), Collections.emptyMap()); for (ElementJson oldType: oldTypeJsons.getElements()) { oldPropertiesTypeMapping.get(oldType.getId()).setTypeJson(oldType); oldPropertiesTypeMapping.get(oldType.getId()).setView(cameoHelper.isView(oldType)); @@ -132,7 +144,7 @@ public void extraProcessPostedElement(ElementJson element, Node node, NodeChange //go through requested _childView changes //get the first package element that's in the owner chain of parent class // cameo/sysml1 requires associations to be placed in the first owning package, is this rule still valid? - Optional p = nodePostHelper.getFirstRelationshipOfType(element, + Optional p = getFirstRelationshipOfType(projectId, refId, null, element, Arrays.asList(CameoNodeType.PACKAGE.getValue(), CameoNodeType.GROUP.getValue()), CameoConstants.OWNERID); String packageId = p.isPresent() ? p.get().getId() : CameoConstants.HOLDING_BIN_PREFIX + element.getProjectId(); List newProperties = new ArrayList<>(); @@ -143,21 +155,21 @@ public void extraProcessPostedElement(ElementJson element, Node node, NodeChange //existing property and type, reuse PropertyData data = oldPropertiesTypeMapping.get(typeId); newProperties.add(data); - newAttributeIds.add(data.getPropertyNode().getNodeId()); + newAttributeIds.add(data.getPropertyJson().getId()); continue; } //create new properties and association - PropertyData newProperty = createElementsForView(newChildView.get(CameoConstants.AGGREGATION), - typeId, element.getId(), packageId, info); + PropertyData newProperty = createElementsForView(info, newChildView.get(CameoConstants.AGGREGATION), + typeId, element.getId(), packageId); newProperties.add(newProperty); - newAttributeIds.add(newProperty.getPropertyNode().getNodeId()); + newAttributeIds.add(newProperty.getPropertyJson().getId()); } //go through old attributes and add back any that wasn't to a view and delete ones that's to a view but not in newProperties List toDelete = new ArrayList<>(); for (PropertyData oldProperty: oldProperties) { if (!oldProperty.isView()) { newProperties.add(oldProperty); - newAttributeIds.add(oldProperty.getPropertyNode().getNodeId()); + newAttributeIds.add(oldProperty.getPropertyJson().getId()); continue; } if (newProperties.contains(oldProperty)) { @@ -165,17 +177,14 @@ public void extraProcessPostedElement(ElementJson element, Node node, NodeChange } toDelete.add(oldProperty); } - deletePropertyElements(toDelete, info); + deletePropertyElements(projectId, refId, toDelete, info); //new derived ownedAttributeIds based on changes element.put(CameoConstants.OWNEDATTRIBUTEIDS, newAttributeIds); - super.extraProcessPostedElement(element, node, info); + super.extraProcessPostedElement(info, element); } - private PropertyData createElementsForView(String aggregation, String typeId, String parentId, String packageId, NodeChangeInfo info) { + private PropertyData createElementsForView(NodeChangeInfo info, String aggregation, String typeId, String parentId, String packageId) { //create new properties and association - Node newPropertyNode = new Node(); - Node newAssocNode = new Node(); - Node newAssocPropertyNode = new Node(); String newPropertyId = UUID.randomUUID().toString(); String newAssocId = UUID.randomUUID().toString(); String newAssocPropertyId = UUID.randomUUID().toString(); @@ -184,30 +193,26 @@ private PropertyData createElementsForView(String aggregation, String typeId, St ElementJson newAssocJson = cameoHelper.createAssociation(newAssocId, packageId, newAssocPropertyId, newPropertyId); ElementJson newAssocPropertyJson = cameoHelper.createProperty(newAssocPropertyId, "", - newAssocId, "none", parentId, newAssocId); - nodePostHelper.processElementAdded(newPropertyJson, newPropertyNode, info); - nodePostHelper.processElementAdded(newAssocJson, newAssocNode, info); - nodePostHelper.processElementAdded(newAssocPropertyJson, newAssocPropertyNode, info); - super.extraProcessPostedElement(newPropertyJson, newPropertyNode, info); - super.extraProcessPostedElement(newAssocJson, newAssocNode, info); - super.extraProcessPostedElement(newAssocPropertyJson, newAssocPropertyNode, info); + newAssocId, CameoConstants.NONE, parentId, newAssocId); + + NodePersistence nodeChangeDomain = getNodePersistence(); + nodeChangeDomain.prepareAddsUpdates(info, List.of(newPropertyJson, newAssocJson, newAssocPropertyJson)); + super.extraProcessPostedElement(info, newPropertyJson); + super.extraProcessPostedElement(info, newAssocJson); + super.extraProcessPostedElement(info, newAssocPropertyJson); PropertyData newProperty = new PropertyData(); newProperty.setAssocJson(newAssocJson); - newProperty.setAssocNode(newAssocNode); newProperty.setPropertyJson(newPropertyJson); - newProperty.setPropertyNode(newPropertyNode); - newProperty.setAssocPropertyNode(newAssocPropertyNode); newProperty.setAssocPropertyJson(newAssocPropertyJson); newProperty.setView(true); return newProperty; } - private void deletePropertyElements(List properties, NodeChangeInfo info) { + private void deletePropertyElements(String projectId, String refId, List properties, NodeChangeInfo info) { Set assocToDelete = new HashSet<>(); for (PropertyData oldProperty: properties) { - Node oldPropertyNode = oldProperty.getPropertyNode(); ElementJson oldPropertyJson = oldProperty.getPropertyJson(); - nodePostHelper.processElementDeleted(oldPropertyJson, oldPropertyNode, info); + getNodePersistence().prepareDeletes(info, List.of(oldPropertyJson)); String assocId = (String)oldPropertyJson.get(CameoConstants.ASSOCIATIONID); if (assocId == null) { continue; @@ -215,20 +220,42 @@ private void deletePropertyElements(List properties, NodeChangeInf assocToDelete.add(assocId); } Set assocPropToDelete = new HashSet<>(); - NodeGetInfo assocInfo = nodeGetHelper.processGetJson(buildRequest(assocToDelete).getElements(), null); + NodeGetInfo assocInfo = getNodePersistence().findAll(projectId, refId, null, buildRequest(assocToDelete).getElements()); for (ElementJson assocJson: assocInfo.getActiveElementMap().values()) { - Node assocNode = assocInfo.getExistingNodeMap().get(assocJson.getId()); - nodePostHelper.processElementDeleted(assocJson, assocNode, info); + getNodePersistence().prepareDeletes(info, List.of(assocJson)); List ownedEndIds = (List)assocJson.get(CameoConstants.OWNEDENDIDS); if (ownedEndIds == null) { continue; } assocPropToDelete.addAll(ownedEndIds); } - NodeGetInfo assocPropInfo = nodeGetHelper.processGetJson(buildRequest(assocPropToDelete).getElements(), null); - for (ElementJson assocPropJson: assocPropInfo.getActiveElementMap().values()) { - Node assocPropNode = assocPropInfo.getExistingNodeMap().get(assocPropJson.getId()); - nodePostHelper.processElementDeleted(assocPropJson, assocPropNode, info); + NodeGetInfo assocPropInfo = getNodePersistence().findAll(projectId, refId, null, buildRequest(assocPropToDelete).getElements()); + getNodePersistence().prepareDeletes(info, assocPropInfo.getActiveElementMap().values()); + } + + //find first element of type in types following e's relkey (assuming relkey's value is an element id) + private Optional getFirstRelationshipOfType(String projectId, String refId, String commitId, + ElementJson e, List types, String relkey) { + //only for latest graph + String nextId = (String)e.get(relkey); + if (nextId == null || nextId.isEmpty()) { + return Optional.empty(); + } + + NodeGetInfo getInfo = nodePersistence.findById(projectId, refId, commitId, nextId); + Optional next = Optional.of(getInfo.getActiveElementMap().get(nextId)); + + while (next.isPresent()) { + if (types.contains(cameoHelper.getNodeType(next.get()).getValue())) { + return next; + } + nextId = (String)next.get().get(relkey); + if (nextId == null || nextId.isEmpty()) { + return Optional.empty(); + } + getInfo = nodePersistence.findById(projectId, refId, commitId, nextId); + next = Optional.of(getInfo.getActiveElementMap().get(nextId)); } + return Optional.empty(); } } diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/controllers/DocumentsResponse.java b/cameo/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java similarity index 92% rename from cameo/src/main/java/org/openmbee/mms/cameo/controllers/DocumentsResponse.java rename to cameo/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java index ad46b479f..b9a089862 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/controllers/DocumentsResponse.java +++ b/cameo/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.cameo.controllers; +package org.openmbee.mms.view.controllers; import java.util.ArrayList; import java.util.List; diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/controllers/GroupsResponse.java b/cameo/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java similarity index 92% rename from cameo/src/main/java/org/openmbee/mms/cameo/controllers/GroupsResponse.java rename to cameo/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java index a7c90d175..12202f559 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/controllers/GroupsResponse.java +++ b/cameo/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.cameo.controllers; +package org.openmbee.mms.view.controllers; import java.util.ArrayList; import java.util.List; diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/controllers/MountsResponse.java b/cameo/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java similarity index 92% rename from cameo/src/main/java/org/openmbee/mms/cameo/controllers/MountsResponse.java rename to cameo/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java index d6de649e0..756ab1122 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/controllers/MountsResponse.java +++ b/cameo/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.cameo.controllers; +package org.openmbee.mms.view.controllers; import java.util.ArrayList; import java.util.List; diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/controllers/VeController.java b/cameo/src/main/java/org/openmbee/mms/view/controllers/VeController.java similarity index 73% rename from cameo/src/main/java/org/openmbee/mms/cameo/controllers/VeController.java rename to cameo/src/main/java/org/openmbee/mms/view/controllers/VeController.java index 914cd54bb..786232c97 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/controllers/VeController.java +++ b/cameo/src/main/java/org/openmbee/mms/view/controllers/VeController.java @@ -1,12 +1,13 @@ -package org.openmbee.mms.cameo.controllers; +package org.openmbee.mms.view.controllers; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import java.util.ArrayList; import java.util.Map; -import org.openmbee.mms.cameo.services.CameoViewService; +import org.openmbee.mms.view.services.ViewService; import org.openmbee.mms.core.objects.ElementsRequest; import org.openmbee.mms.core.objects.ElementsResponse; +import org.openmbee.mms.core.services.GenericServiceFactory; import org.openmbee.mms.crud.controllers.BaseController; import org.openmbee.mms.json.MountJson; import org.springframework.beans.factory.annotation.Autowired; @@ -21,16 +22,17 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; + @RestController @RequestMapping("/projects/{projectId}/refs/{refId}") @Tag(name = "Views") public class VeController extends BaseController { - private CameoViewService cameoViewService; + private GenericServiceFactory serviceFactory; @Autowired - public VeController(CameoViewService cameoViewService) { - this.cameoViewService = cameoViewService; + public void setGenericServiceFactory( GenericServiceFactory serviceFactory){ + this.serviceFactory = serviceFactory; } @GetMapping("/mounts") @@ -40,9 +42,7 @@ public MountsResponse getMounts( @PathVariable String refId, @RequestParam(required = false) String commitId, @RequestParam(required = false) Map params) { - - MountJson json = cameoViewService - .getProjectUsages(projectId, refId, params.get("commitId"), new ArrayList<>()); + MountJson json = serviceFactory.getServiceForSchema( ViewService.class ,getProjectType(projectId)).getProjectUsages(projectId, refId, params.get("commitId"), new ArrayList<>(), true); MountsResponse res = new MountsResponse(); res.getProjects().add(json); return res; @@ -56,7 +56,7 @@ public DocumentsResponse getDocuments( @RequestParam(required = false) String commitId, @RequestParam(required = false) Map params) { - ElementsResponse docs = cameoViewService.getDocuments(projectId, refId, params); + ElementsResponse docs = serviceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getDocuments(projectId, refId, params); return (new DocumentsResponse()).setDocuments(docs.getElements()); } @@ -69,7 +69,7 @@ public ElementsResponse getView( @RequestParam(required = false) String commitId, @RequestParam(required = false) Map params) { - ElementsResponse res = cameoViewService.getView(projectId, refId, viewId, params); + ElementsResponse res = serviceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getView(projectId, refId, viewId, params); handleSingleResponse(res); return res; } @@ -83,7 +83,7 @@ public ElementsResponse getViews( @RequestParam(required = false) String commitId, @RequestParam(required = false) Map params) { - return cameoViewService.getViews(projectId, refId, req, params); + return serviceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getViews(projectId, refId, req, params); } @PostMapping("/views") @@ -96,8 +96,9 @@ public ElementsResponse createOrUpdateViews( @RequestParam(required = false) Map params, @Parameter(hidden = true) Authentication auth) { - ElementsResponse res = cameoViewService.createOrUpdate(projectId, refId, req, params, auth.getName()); - cameoViewService.addChildViews(res, params); + ViewService viewService = serviceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)); + ElementsResponse res = viewService.createOrUpdate(projectId, refId, req, params, auth.getName()); + viewService.addChildViews(res, params); return res; } @@ -108,7 +109,7 @@ public GroupsResponse getGroups( @PathVariable String refId, @RequestParam(required = false) Map params) { - ElementsResponse groups = cameoViewService.getGroups(projectId, refId, params); + ElementsResponse groups = serviceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getGroups(projectId, refId, params); return (new GroupsResponse()).setGroups(groups.getElements()); } } diff --git a/core/core.gradle b/core/core.gradle index 868a6ebb9..dc672e804 100644 --- a/core/core.gradle +++ b/core/core.gradle @@ -1,7 +1,13 @@ dependencies { - api project(':data') + api project(':json') + api commonDependencies.'jackson-annotations' + api commonDependencies.'jackson-databind' + api commonDependencies.'jackson-datatype-hibernate5' + api commonDependencies.'jackson-datatype-jsr310' + + api commonDependencies.'spring-data-commons' api commonDependencies.'spring-security-config' api commonDependencies.'spring-webmvc' api commonDependencies.'spring-tx' @@ -10,4 +16,10 @@ dependencies { implementation commonDependencies.'servlet-api' testImplementation commonDependencies.'spring-boot-starter-test' +} + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } } \ No newline at end of file diff --git a/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdateResponseBuilder.java b/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdateResponseBuilder.java index f9043b7b9..6abf03a23 100644 --- a/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdateResponseBuilder.java +++ b/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdateResponseBuilder.java @@ -1,18 +1,12 @@ package org.openmbee.mms.core.builders; import org.openmbee.mms.core.objects.PermissionUpdateResponse; -import org.openmbee.mms.data.domains.global.BranchGroupPerm; -import org.openmbee.mms.data.domains.global.BranchUserPerm; -import org.openmbee.mms.data.domains.global.OrgGroupPerm; -import org.openmbee.mms.data.domains.global.OrgUserPerm; -import org.openmbee.mms.data.domains.global.ProjectGroupPerm; -import org.openmbee.mms.data.domains.global.ProjectUserPerm; import java.util.*; public class PermissionUpdateResponseBuilder { - private class PermissionUpdateWrapper { + protected class PermissionUpdateWrapper { private PermissionUpdateResponse.PermissionUpdate permissionUpdate; @@ -50,93 +44,6 @@ public PermissionUpdateResponseBuilder insert(PermissionUpdateResponse updateUse return this; } - public void insertPermissionUpdates_OrgUserPerm(PermissionUpdateResponse.Action action, Collection perms) { - perms.forEach(v -> insertPermissionUpdate(action, v)); - } - - public void insertPermissionUpdate(PermissionUpdateResponse.Action action, OrgUserPerm v) { - if(v == null) - return; - - PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( - action, v.getUser().getUsername(), v.getRole().getName(), v.getOrganization().getOrganizationId(), v.getOrganization().getOrganizationName(), - null, null, null, false); - doInsert(update); - } - - public void insertPermissionUpdates_OrgGroupPerm(PermissionUpdateResponse.Action action, Collection perms) { - perms.forEach(v -> insertPermissionUpdate(action, v)); - } - - public void insertPermissionUpdate(PermissionUpdateResponse.Action action, OrgGroupPerm v) { - if(v == null) - return; - - PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( - action, v.getGroup().getName(), v.getRole().getName(), v.getOrganization().getOrganizationId(), v.getOrganization().getOrganizationName(), - null, null, null, false); - doInsert(update); - } - - public void insertPermissionUpdates_ProjectUserPerm(PermissionUpdateResponse.Action action, Collection perms) { - perms.forEach(v -> insertPermissionUpdate(action, v)); - } - - public void insertPermissionUpdate(PermissionUpdateResponse.Action action, ProjectUserPerm v) { - if(v == null) - return; - - PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( - action, v.getUser().getUsername(), v.getRole().getName(), v.getProject().getOrganization().getOrganizationId(), - v.getProject().getOrganization().getOrganizationName(), v.getProject().getProjectId(), v.getProject().getProjectName(), - null, v.isInherited()); - doInsert(update); - } - - public void insertPermissionUpdates_ProjectGroupPerm(PermissionUpdateResponse.Action action, Collection perms) { - perms.forEach(v -> insertPermissionUpdate(action, v)); - } - - public void insertPermissionUpdate(PermissionUpdateResponse.Action action, ProjectGroupPerm v) { - if(v == null) - return; - - PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( - action, v.getGroup().getName(), v.getRole().getName(), v.getProject().getOrganization().getOrganizationId(), - v.getProject().getOrganization().getOrganizationName(), v.getProject().getProjectId(), v.getProject().getProjectName(), - null, v.isInherited()); - doInsert(update); - } - - public void insertPermissionUpdates_BranchUserPerm(PermissionUpdateResponse.Action action, Collection perms) { - perms.forEach(v -> insertPermissionUpdate(action, v)); - } - - public void insertPermissionUpdate(PermissionUpdateResponse.Action action, BranchUserPerm v) { - if(v == null) - return; - - PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( - action, v.getUser().getUsername(), v.getRole().getName(), v.getBranch().getProject().getOrganization().getOrganizationId(), - v.getBranch().getProject().getOrganization().getOrganizationName(), v.getBranch().getProject().getProjectId(), - v.getBranch().getProject().getProjectName(), v.getBranch().getBranchId(), v.isInherited()); - doInsert(update); - } - - public void insertPermissionUpdates_BranchGroupPerm(PermissionUpdateResponse.Action action, Collection perms) { - perms.forEach(v -> insertPermissionUpdate(action, v)); - } - - public void insertPermissionUpdate(PermissionUpdateResponse.Action action, BranchGroupPerm v) { - if(v == null) - return; - - PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( - action, v.getGroup().getName(), v.getRole().getName(), v.getBranch().getProject().getOrganization().getOrganizationId(), - v.getBranch().getProject().getOrganization().getOrganizationName(), v.getBranch().getProject().getProjectId(), - v.getBranch().getProject().getProjectName(),v.getBranch().getBranchId(), v.isInherited()); - doInsert(update); - } public PermissionUpdateResponse getPermissionUpdateResponse() { PermissionUpdateResponse response = new PermissionUpdateResponse(); @@ -147,7 +54,7 @@ public PermissionUpdateResponse getPermissionUpdateResponse() { return response; } - private void doInsert(PermissionUpdateResponse.PermissionUpdate update) { + protected void doInsert(PermissionUpdateResponse.PermissionUpdate update) { PermissionUpdateWrapper wrapped = new PermissionUpdateWrapper(update); if(update.getAction() == PermissionUpdateResponse.Action.ADD) { if(! removed.remove(wrapped)) { @@ -156,5 +63,6 @@ private void doInsert(PermissionUpdateResponse.PermissionUpdate update) { } else if(! added.remove(wrapped)){ removed.add(wrapped); } + } } diff --git a/core/src/main/java/org/openmbee/mms/core/config/Constants.java b/core/src/main/java/org/openmbee/mms/core/config/Constants.java index ee6b30275..c70762353 100644 --- a/core/src/main/java/org/openmbee/mms/core/config/Constants.java +++ b/core/src/main/java/org/openmbee/mms/core/config/Constants.java @@ -11,25 +11,40 @@ public class Constants { public static final String ELEMENT_KEY = "elements"; public static final String COMMIT_KEY = "commits"; public static final String WEBHOOK_KEY = "webhooks"; + public static final String BRANCH_TYPE = "Branch"; + public static final String ELEMENT_TYPE = "element"; + public static final String REF_ID = "refId"; + public static final String ID_KEY = "id"; public static final String REJECTED = "rejected"; public static final String MESSAGES = "messages"; public static final String CODE = "code"; + public static final String TRUE= "true"; + public static final String FALSE = "false"; + public static final String LIMIT = "limit"; public static final String MASTER_BRANCH = "master"; public static final Pattern BRANCH_ID_VALID_PATTERN = Pattern.compile("^[\\w-]+$"); - public static final Map RPmap = new LinkedHashMap<>(); + public static final Map> RPmap = new LinkedHashMap<>(); public static final List aPriv; public static final List rPriv; public static final List wPriv; + + public static final String ADMIN = "ADMIN"; + public static final String READER = "READER"; + public static final String WRITER = "WRITER"; + + public static final String NOT_FOUND = "Not Found"; + public static final String ELEMENT_DELETE = "Element Already Deleted"; + static { aPriv = Arrays.asList("ORG_READ", "ORG_EDIT", "ORG_UPDATE_PERMISSIONS", "ORG_READ_PERMISSIONS", "ORG_CREATE_PROJECT", "ORG_DELETE", "PROJECT_READ", "PROJECT_EDIT", "PROJECT_READ_COMMITS", "PROJECT_CREATE_BRANCH", "PROJECT_DELETE", "PROJECT_UPDATE_PERMISSIONS", "PROJECT_READ_PERMISSIONS", "PROJECT_CREATE_WEBHOOKS", "BRANCH_READ", "BRANCH_EDIT_CONTENT", "BRANCH_DELETE", "BRANCH_UPDATE_PERMISSIONS", "BRANCH_READ_PERMISSIONS"); rPriv = Arrays.asList("ORG_READ", "ORG_READ_PERMISSIONS", "PROJECT_READ", "PROJECT_READ_COMMITS", "PROJECT_READ_PERMISSIONS", "BRANCH_READ", "BRANCH_READ_PERMISSIONS"); wPriv = Arrays.asList("ORG_READ", "ORG_EDIT", "ORG_READ_PERMISSIONS", "ORG_CREATE_PROJECT", "PROJECT_READ", "PROJECT_EDIT", "PROJECT_READ_COMMITS", "PROJECT_CREATE_BRANCH", "PROJECT_READ_PERMISSIONS", "PROJECT_CREATE_WEBHOOKS", "BRANCH_READ", "BRANCH_EDIT_CONTENT", "BRANCH_READ_PERMISSIONS"); - RPmap.put("ADMIN", aPriv); - RPmap.put("READER", rPriv); - RPmap.put("WRITER", wPriv); + RPmap.put(ADMIN, aPriv); + RPmap.put(READER, rPriv); + RPmap.put(WRITER, wPriv); } } diff --git a/core/src/main/java/org/openmbee/mms/core/config/ContextHolder.java b/core/src/main/java/org/openmbee/mms/core/config/ContextHolder.java index 2dd8c62b0..a08a93a9e 100644 --- a/core/src/main/java/org/openmbee/mms/core/config/ContextHolder.java +++ b/core/src/main/java/org/openmbee/mms/core/config/ContextHolder.java @@ -12,7 +12,7 @@ public static ContextObject getContext() { } public static void setContext(String projectId) { - contextHolder.set(new ContextObject(projectId)); + setContext(projectId, getContext().getProjectId().equals(projectId) ? getContext().getBranchId() : Constants.MASTER_BRANCH); } public static void setContext(String projectId, String refId) { diff --git a/core/src/main/java/org/openmbee/mms/core/config/Formats.java b/core/src/main/java/org/openmbee/mms/core/config/Formats.java index 4212b96a1..5c2c3d1c8 100644 --- a/core/src/main/java/org/openmbee/mms/core/config/Formats.java +++ b/core/src/main/java/org/openmbee/mms/core/config/Formats.java @@ -6,9 +6,12 @@ public class Formats { - public static String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; - public static SimpleDateFormat SDF = new SimpleDateFormat(DATE_FORMAT); - public static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT).withZone( + public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; + public static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(DATE_FORMAT); + public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT).withZone( ZoneId.systemDefault()); + private Formats() { + throw new IllegalStateException("Formats"); + } } diff --git a/core/src/main/java/org/openmbee/mms/core/dao/BranchPersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/BranchPersistence.java new file mode 100644 index 000000000..351381438 --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/dao/BranchPersistence.java @@ -0,0 +1,21 @@ +package org.openmbee.mms.core.dao; + +import org.openmbee.mms.json.RefJson; + +import java.util.List; +import java.util.Optional; + +public interface BranchPersistence { + + RefJson save(RefJson refJson); + + RefJson update(RefJson refJson); + + List findAll(String projectId); + + Optional findById(String projectId, String refId); + + Optional deleteById(String projectId, String refId); + + boolean inheritsPermissions(String projectId, String branchId); +} diff --git a/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java new file mode 100644 index 000000000..7ff3ffb25 --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java @@ -0,0 +1,32 @@ +package org.openmbee.mms.core.dao; + +import org.openmbee.mms.json.CommitJson; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public interface CommitPersistence { + + CommitJson save(CommitJson commitJson, Instant now); + + CommitJson update(CommitJson commitJson); + + Optional findById(String projectId, String commitId); + + List findAllById(String projectId, Set commitIds); + + List findAllByProjectId(String projectId); + + Optional findLatestByProjectAndRef(String projectId, String refId); + + Optional findByProjectAndRefAndTimestamp(String projectId, String refId, Instant timestamp); + + List findByProjectAndRefAndTimestampAndLimit(String projectId, String refId, Instant timestamp, int limit); + + List elementHistory(String projectId, String elementId, Set commitDocIds); + + Optional deleteById(String projectId, String commitId); + +} diff --git a/core/src/main/java/org/openmbee/mms/core/dao/GroupPersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/GroupPersistence.java new file mode 100644 index 000000000..b7622f251 --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/dao/GroupPersistence.java @@ -0,0 +1,14 @@ +package org.openmbee.mms.core.dao; + +import org.openmbee.mms.json.GroupJson; + +import java.util.Collection; +import java.util.Optional; + +public interface GroupPersistence { + + GroupJson save(GroupJson groupJson); + void delete(GroupJson groupJson); + Optional findByName(String name); + Collection findAll(); +} diff --git a/core/src/main/java/org/openmbee/mms/core/dao/NodePersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/NodePersistence.java new file mode 100644 index 000000000..be20c50d8 --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/dao/NodePersistence.java @@ -0,0 +1,35 @@ +package org.openmbee.mms.core.dao; + +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.ElementJson; +import org.openmbee.mms.json.RefJson; + +import java.io.OutputStream; +import java.util.Collection; +import java.util.List; + +public interface NodePersistence { + + + NodeChangeInfo prepareChange(CommitJson commitJson, boolean overwrite, boolean preserveTimestamps); + + NodeChangeInfo prepareAddsUpdates(NodeChangeInfo nodeChangeInfo, Collection elements); + + NodeChangeInfo prepareDeletes(NodeChangeInfo nodeChangeInfo, Collection jsons); + + NodeChangeInfo commitChanges(NodeChangeInfo nodeChangeInfo); + + NodeGetInfo findById(String elementId, String refId, String commitId, String id); + + List findAllByNodeType(String projectId, String refId, String commitId, int nodeType); + + NodeGetInfo findAll(String projectId, String refId, String commitId, List elements); + + List findAll(String projectId, String refId, String commitId); + + void streamAllAtCommit(String projectId, String refId, String commitId, OutputStream outputStream, String separator); + + void branchElements(RefJson parentBranch, CommitJson parentCommit, RefJson targetBranch); +} diff --git a/core/src/main/java/org/openmbee/mms/core/dao/OrgPersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/OrgPersistence.java new file mode 100644 index 000000000..c9dd84799 --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/dao/OrgPersistence.java @@ -0,0 +1,19 @@ +package org.openmbee.mms.core.dao; + +import org.openmbee.mms.json.OrgJson; + +import java.util.Collection; +import java.util.Optional; + +public interface OrgPersistence { + + OrgJson save(OrgJson orgJson); + + Optional findById(String orgId); + + Collection findAll(); + + OrgJson deleteById(String orgId); + + boolean hasPublicPermissions(String orgId); +} diff --git a/core/src/main/java/org/openmbee/mms/core/dao/ProjectPersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/ProjectPersistence.java new file mode 100644 index 000000000..3bf83cc6e --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/dao/ProjectPersistence.java @@ -0,0 +1,31 @@ +package org.openmbee.mms.core.dao; + +import org.openmbee.mms.json.ProjectJson; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public interface ProjectPersistence { + + ProjectJson save(ProjectJson projectJson); + + ProjectJson update(ProjectJson projectJson); + + Optional findById(String projectId); + + List findAllById(Set projectIds); + + List findAll(); + + Collection findAllByOrgId(String orgId); + + void softDelete(String projectId); + + void hardDelete(String projectId); + + boolean inheritsPermissions(String projectId); + + boolean hasPublicPermissions(String projectId); +} diff --git a/core/src/main/java/org/openmbee/mms/core/dao/UserGroupsPersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/UserGroupsPersistence.java new file mode 100644 index 000000000..f8cf58b16 --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/dao/UserGroupsPersistence.java @@ -0,0 +1,15 @@ +package org.openmbee.mms.core.dao; + +import org.openmbee.mms.json.GroupJson; +import org.openmbee.mms.json.UserJson; + +import java.util.Collection; + +public interface UserGroupsPersistence { + + boolean addUserToGroup(String groupName, String username); + boolean removeUserFromGroup(String groupName, String username); + Collection findUsersInGroup(String groupName); + Collection findGroupsAssignedToUser(String username); + +} diff --git a/core/src/main/java/org/openmbee/mms/core/dao/UserPersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/UserPersistence.java new file mode 100644 index 000000000..9876f5438 --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/dao/UserPersistence.java @@ -0,0 +1,14 @@ +package org.openmbee.mms.core.dao; + +import org.openmbee.mms.json.UserJson; + +import java.util.Collection; +import java.util.Optional; + +public interface UserPersistence { + + UserJson save(UserJson user); + Optional findByUsername(String username); + Collection findAll(); + +} diff --git a/core/src/main/java/org/openmbee/mms/core/delegation/PermissionsDelegate.java b/core/src/main/java/org/openmbee/mms/core/delegation/PermissionsDelegate.java index e6dd91f2c..88fb35a37 100644 --- a/core/src/main/java/org/openmbee/mms/core/delegation/PermissionsDelegate.java +++ b/core/src/main/java/org/openmbee/mms/core/delegation/PermissionsDelegate.java @@ -9,9 +9,11 @@ public interface PermissionsDelegate { boolean hasPermission(String user, Set groups, String privilege); + boolean hasGroupPermissions(String group, String privilege); void initializePermissions(String creator); void initializePermissions(String creator, boolean inherit); boolean setInherit(boolean isInherit); + PermissionResponse getInherit(); void setPublic(boolean isPublic); PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest req); PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest req); diff --git a/core/src/main/java/org/openmbee/mms/core/delegation/PermissionsDelegateFactory.java b/core/src/main/java/org/openmbee/mms/core/delegation/PermissionsDelegateFactory.java index 794f61efc..99b53daea 100644 --- a/core/src/main/java/org/openmbee/mms/core/delegation/PermissionsDelegateFactory.java +++ b/core/src/main/java/org/openmbee/mms/core/delegation/PermissionsDelegateFactory.java @@ -1,14 +1,14 @@ package org.openmbee.mms.core.delegation; -import org.openmbee.mms.data.domains.global.Branch; -import org.openmbee.mms.data.domains.global.Organization; -import org.openmbee.mms.data.domains.global.Project; +import org.openmbee.mms.json.OrgJson; +import org.openmbee.mms.json.ProjectJson; +import org.openmbee.mms.json.RefJson; public interface PermissionsDelegateFactory { - PermissionsDelegate getPermissionsDelegate(Project project); + PermissionsDelegate getPermissionsDelegate(ProjectJson project); - PermissionsDelegate getPermissionsDelegate(Organization organization); + PermissionsDelegate getPermissionsDelegate(OrgJson organization); - PermissionsDelegate getPermissionsDelegate(Branch branch); + PermissionsDelegate getPermissionsDelegate(RefJson branch); } diff --git a/core/src/main/java/org/openmbee/mms/core/objects/PermissionResponse.java b/core/src/main/java/org/openmbee/mms/core/objects/PermissionResponse.java index 341e26be7..becf438e8 100644 --- a/core/src/main/java/org/openmbee/mms/core/objects/PermissionResponse.java +++ b/core/src/main/java/org/openmbee/mms/core/objects/PermissionResponse.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class PermissionResponse { @@ -44,6 +45,19 @@ public boolean isInherited() { public void setInherited(boolean inherited) { this.inherited = inherited; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Permission that = (Permission) o; + return inherited == that.inherited && name.equals(that.name) && role.equals(that.role); + } + + @Override + public int hashCode() { + return Objects.hash(name, role, inherited); + } } public List getPermissions() { diff --git a/core/src/main/java/org/openmbee/mms/core/security/MethodSecurityService.java b/core/src/main/java/org/openmbee/mms/core/security/MethodSecurityService.java index 5266f9d58..bbab05f2a 100644 --- a/core/src/main/java/org/openmbee/mms/core/security/MethodSecurityService.java +++ b/core/src/main/java/org/openmbee/mms/core/security/MethodSecurityService.java @@ -4,16 +4,16 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import org.openmbee.mms.core.dao.BranchPersistence; +import org.openmbee.mms.core.dao.ProjectPersistence; import org.openmbee.mms.core.exceptions.NotFoundException; import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.dao.BranchDAO; -import org.openmbee.mms.core.dao.OrgDAO; -import org.openmbee.mms.core.dao.ProjectDAO; +import org.openmbee.mms.core.dao.OrgPersistence; import org.openmbee.mms.core.services.PermissionService; import org.openmbee.mms.core.utils.AuthenticationUtils; -import org.openmbee.mms.data.domains.global.Organization; -import org.openmbee.mms.data.domains.global.Project; -import org.openmbee.mms.data.domains.scoped.Branch; +import org.openmbee.mms.json.OrgJson; +import org.openmbee.mms.json.ProjectJson; +import org.openmbee.mms.json.RefJson; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; @@ -23,9 +23,9 @@ public class MethodSecurityService { private PermissionService permissionService; - private ProjectDAO projectRepository; - private BranchDAO branchRepository; - private OrgDAO orgRepository; + private ProjectPersistence projectPersistence; + private BranchPersistence branchPersistence; + private OrgPersistence orgPersistence; @Autowired public void setPermissionService(PermissionService permissionService) { @@ -33,21 +33,20 @@ public void setPermissionService(PermissionService permissionService) { } @Autowired - public void setOrgRepository(OrgDAO orgRepository) { - this.orgRepository = orgRepository; + public void setOrgPersistence(OrgPersistence orgPersistence) { + this.orgPersistence = orgPersistence; } @Autowired - public void setProjectRepository(ProjectDAO projectRepository) { - this.projectRepository = projectRepository; + public void setProjectPersistence(ProjectPersistence projectPersistence) { + this.projectPersistence = projectPersistence; } @Autowired - public void setBranchRepository(BranchDAO branchRepository) { - this.branchRepository = branchRepository; + public void setBranchPersistence(BranchPersistence branchPersistence) { + this.branchPersistence = branchPersistence; } - public boolean hasOrgPrivilege(Authentication authentication, String orgId, String privilege, boolean allowAnonIfPublic) { CompletableFuture permissionsFuture = CompletableFuture.supplyAsync(() -> { @@ -106,13 +105,13 @@ public boolean hasBranchPrivilege(Authentication authentication, String projectI } private boolean orgExists(String orgId) { - Optional o = orgRepository.findByOrganizationId(orgId); + Optional o = orgPersistence.findById(orgId); return o.isPresent(); } private boolean projectExists(String projectId) { ContextHolder.getContext().setProjectId(projectId); - Optional p = projectRepository.findByProjectId(projectId); + Optional p = projectPersistence.findById(projectId); return p.isPresent(); } @@ -120,16 +119,16 @@ private boolean branchExists(String projectId, String branchId){ if(! projectExists(projectId)) { return false; } - Optional branchesOption = branchRepository.findByBranchId(branchId); + Optional branchesOption = branchPersistence.findById(projectId, branchId); return branchesOption.isPresent(); } private boolean completeFutures(CompletableFuture permissionsFuture, CompletableFuture existsFuture, String context) { try { - if (!permissionsFuture.join()) { + if (!isBooleanFutureTrue(permissionsFuture)){ return false; } - if (!existsFuture.join()) { + if (!isBooleanFutureTrue(existsFuture)){ throw new NotFoundException(context + " not found"); } return true; @@ -140,4 +139,12 @@ private boolean completeFutures(CompletableFuture permissionsFuture, Co return false; } } + + private boolean isBooleanFutureTrue(CompletableFuture future) { + if(future != null) { + Boolean joinResult = future.join(); + return joinResult != null && joinResult; + } + return false; + } } diff --git a/core/src/main/java/org/openmbee/mms/core/services/BranchService.java b/core/src/main/java/org/openmbee/mms/core/services/BranchService.java index 83831dad8..6348ec6f1 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/BranchService.java +++ b/core/src/main/java/org/openmbee/mms/core/services/BranchService.java @@ -11,5 +11,7 @@ public interface BranchService { RefJson createBranch(String projectId, RefJson branch); + RefJson updateBranch(String projectId, RefJson branch); + RefsResponse deleteBranch(String projectId, String id); } diff --git a/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java b/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java index 30bdabd5a..999101146 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java +++ b/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java @@ -1,27 +1,26 @@ package org.openmbee.mms.core.services; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; +import java.util.*; import org.openmbee.mms.core.builders.PermissionUpdatesResponseBuilder; import org.openmbee.mms.core.config.AuthorizationConstants; import org.openmbee.mms.core.config.Constants; -import org.openmbee.mms.core.dao.BranchGDAO; -import org.openmbee.mms.core.dao.OrgDAO; -import org.openmbee.mms.core.dao.ProjectDAO; +import org.openmbee.mms.core.dao.BranchPersistence; +import org.openmbee.mms.core.dao.OrgPersistence; +import org.openmbee.mms.core.dao.ProjectPersistence; import org.openmbee.mms.core.delegation.PermissionsDelegate; -import org.openmbee.mms.core.delegation.PermissionsDelegateFactory; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.core.exceptions.NotFoundException; import org.openmbee.mms.core.objects.PermissionResponse; import org.openmbee.mms.core.objects.PermissionUpdateRequest; import org.openmbee.mms.core.objects.PermissionUpdateResponse; import org.openmbee.mms.core.objects.PermissionUpdatesResponse; -import org.openmbee.mms.data.domains.global.Branch; -import org.openmbee.mms.data.domains.global.Organization; -import org.openmbee.mms.data.domains.global.Project; +import org.openmbee.mms.core.utils.PermissionsDelegateUtil; +import org.openmbee.mms.json.OrgJson; +import org.openmbee.mms.json.ProjectJson; +import org.openmbee.mms.json.RefJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -29,44 +28,46 @@ @Service("defaultPermissionService") public class DefaultPermissionService implements PermissionService { - private BranchGDAO branchRepo; - private ProjectDAO projectRepo; - private OrgDAO orgRepo; - private List permissionsDelegateFactories; + private static final Logger logger = LoggerFactory.getLogger(DefaultPermissionService.class); + + private BranchPersistence branchPersistence; + private ProjectPersistence projectPersistence; + private OrgPersistence orgPersistence; + private PermissionsDelegateUtil permissionsDelegateUtil; @Autowired - public void setPermissionsDelegateFactories(List permissionsDelegateFactories) { - this.permissionsDelegateFactories = permissionsDelegateFactories; + public void setPermissionsDelegateUtil(PermissionsDelegateUtil permissionsDelegateUtil) { + this.permissionsDelegateUtil = permissionsDelegateUtil; } @Autowired - public void setBranchRepo(BranchGDAO branchRepo) { - this.branchRepo = branchRepo; + public void setBranchPersistence(BranchPersistence branchPersistence) { + this.branchPersistence = branchPersistence; } @Autowired - public void setProjectRepo(ProjectDAO projectRepo) { - this.projectRepo = projectRepo; + public void setProjectPersistence(ProjectPersistence projectPersistence) { + this.projectPersistence = projectPersistence; } @Autowired - public void setOrgRepo(OrgDAO orgRepo) { - this.orgRepo = orgRepo; + public void setOrgPersistence(OrgPersistence orgPersistence) { + this.orgPersistence = orgPersistence; } @Override @Transactional public void initOrgPerms(String orgId, String creator) { - Organization organization = getOrganization(orgId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(organization); + OrgJson organization = getOrganization(orgId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); permissionsDelegate.initializePermissions(creator); } @Override @Transactional public void initProjectPerms(String projectId, boolean inherit, String creator) { - Project project = getProject(projectId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(project); + ProjectJson project = getProject(projectId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); permissionsDelegate.initializePermissions(creator, inherit); recalculateInheritedPerms(project); @@ -76,8 +77,12 @@ public void initProjectPerms(String projectId, boolean inherit, String creator) @Override @Transactional public void initBranchPerms(String projectId, String branchId, boolean inherit, String creator) { - Branch branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.CREATE); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(branch); + RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.CREATE); + if(branch == null) { + logger.error("Error initiating branch permissions " + projectId + " / " + branchId); + throw new InternalErrorException("Could not initiate branch permissions"); + } + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(branch); permissionsDelegate.initializePermissions(creator, inherit); recalculateInheritedPerms(branch); @@ -86,13 +91,15 @@ public void initBranchPerms(String projectId, String branchId, boolean inherit, @Override @Transactional public PermissionUpdatesResponse updateOrgUserPerms(PermissionUpdateRequest req, String orgId) { - Organization organization = getOrganization(orgId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(organization); + OrgJson organization = getOrganization(orgId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); responseBuilder.getUsers().insert(permissionsDelegate.updateUserPermissions(req)); - for (Project proj: organization.getProjects()) { - responseBuilder.insert(recalculateInheritedPerms(proj)); + Collection projects = projectPersistence.findAllByOrgId(orgId); + + for (ProjectJson project : projects) { + responseBuilder.insert(recalculateInheritedPerms(project)); } return responseBuilder.getPermissionUpdatesReponse(); @@ -101,13 +108,14 @@ public PermissionUpdatesResponse updateOrgUserPerms(PermissionUpdateRequest req, @Override @Transactional public PermissionUpdatesResponse updateOrgGroupPerms(PermissionUpdateRequest req, String orgId) { - Organization organization = getOrganization(orgId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(organization); + OrgJson organization = getOrganization(orgId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); responseBuilder.getGroups().insert(permissionsDelegate.updateGroupPermissions(req)); - for (Project proj : organization.getProjects()) { - responseBuilder.insert(recalculateInheritedPerms(proj)); + Collection projects = projectPersistence.findAllByOrgId(orgId); + for (ProjectJson project : projects) { + responseBuilder.insert(recalculateInheritedPerms(project)); } return responseBuilder.getPermissionUpdatesReponse(); @@ -116,12 +124,13 @@ public PermissionUpdatesResponse updateOrgGroupPerms(PermissionUpdateRequest req @Override @Transactional public PermissionUpdatesResponse updateProjectUserPerms(PermissionUpdateRequest req, String projectId) { - Project project = getProject(projectId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(project); + ProjectJson project = getProject(projectId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); responseBuilder.getUsers().insert(permissionsDelegate.updateUserPermissions(req)); - for (Branch b : project.getBranches()) { + Collection branches = branchPersistence.findAll(projectId); + for (RefJson b : branches) { responseBuilder.insert(recalculateInheritedPerms(b)); } @@ -131,12 +140,13 @@ public PermissionUpdatesResponse updateProjectUserPerms(PermissionUpdateRequest @Override @Transactional public PermissionUpdatesResponse updateProjectGroupPerms(PermissionUpdateRequest req, String projectId) { - Project project = getProject(projectId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(project); + ProjectJson project = getProject(projectId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); responseBuilder.getGroups().insert(permissionsDelegate.updateGroupPermissions(req)); - for (Branch b : project.getBranches()) { + Collection branches = branchPersistence.findAll(projectId); + for (RefJson b : branches) { responseBuilder.insert(recalculateInheritedPerms(b)); } @@ -146,16 +156,19 @@ public PermissionUpdatesResponse updateProjectGroupPerms(PermissionUpdateRequest @Override @Transactional public PermissionUpdateResponse updateBranchUserPerms(PermissionUpdateRequest req, String projectId, String branchId) { - Branch branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(branch); + RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); + if(branch == null) { + throw new NotFoundException("Branch not found"); + } + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(branch); return permissionsDelegate.updateUserPermissions(req); } @Override @Transactional public PermissionUpdateResponse updateBranchGroupPerms(PermissionUpdateRequest req, String projectId, String branchId) { - Branch branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(branch); + RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(branch); return permissionsDelegate.updateGroupPermissions(req); } @@ -164,8 +177,8 @@ public PermissionUpdateResponse updateBranchGroupPerms(PermissionUpdateRequest r public PermissionUpdatesResponse setProjectInherit(boolean isInherit, String projectId) { PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); responseBuilder.setInherit(true); - Project project = getProject(projectId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(project); + ProjectJson project = getProject(projectId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); if (permissionsDelegate.setInherit(isInherit)) { responseBuilder.insert(recalculateInheritedPerms(project)); } @@ -176,9 +189,9 @@ public PermissionUpdatesResponse setProjectInherit(boolean isInherit, String pro @Transactional public PermissionUpdatesResponse setBranchInherit(boolean isInherit, String projectId, String branchId) { PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); - responseBuilder.setInherit(true); - Branch branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(branch); + responseBuilder.setInherit(isInherit); + RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(branch); if (permissionsDelegate.setInherit(isInherit)) { responseBuilder.insert(recalculateInheritedPerms(branch)); } @@ -188,8 +201,8 @@ public PermissionUpdatesResponse setBranchInherit(boolean isInherit, String proj @Override @Transactional public boolean setOrgPublic(boolean isPublic, String orgId) { - Organization organization = getOrganization(orgId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(organization); + OrgJson organization = getOrganization(orgId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); permissionsDelegate.setPublic(isPublic); return true; } @@ -197,8 +210,8 @@ public boolean setOrgPublic(boolean isPublic, String orgId) { @Override @Transactional public boolean setProjectPublic(boolean isPublic, String projectId) { - Project project = getProject(projectId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(project); + ProjectJson project = getProject(projectId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); permissionsDelegate.setPublic(isPublic); return true; } @@ -208,8 +221,8 @@ public boolean setProjectPublic(boolean isPublic, String projectId) { public boolean hasOrgPrivilege(String privilege, String user, Set groups, String orgId) { if (groups.contains(AuthorizationConstants.MMSADMIN)) return true; - Organization organization = getOrganization(orgId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(organization); + OrgJson organization = getOrganization(orgId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); return permissionsDelegate.hasPermission(user, groups, privilege); } @@ -218,8 +231,8 @@ public boolean hasOrgPrivilege(String privilege, String user, Set groups public boolean hasProjectPrivilege(String privilege, String user, Set groups, String projectId) { if (groups.contains(AuthorizationConstants.MMSADMIN)) return true; - Project project = getProject(projectId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(project); + ProjectJson project = getProject(projectId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); return permissionsDelegate.hasPermission(user, groups, privilege); } @@ -228,127 +241,114 @@ public boolean hasProjectPrivilege(String privilege, String user, Set gr public boolean hasBranchPrivilege(String privilege, String user, Set groups, String projectId, String branchId) { if (groups.contains(AuthorizationConstants.MMSADMIN)) return true; - Branch branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(branch); + RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(branch); return permissionsDelegate.hasPermission(user, groups, privilege); } @Override @Transactional public boolean isProjectInherit(String projectId) { - Optional project = projectRepo.findByProjectId(projectId); - if (!project.isPresent()) { - throw new NotFoundException("project " + projectId + " not found"); - } - return project.get().isInherit(); + return projectPersistence.inheritsPermissions(projectId); } @Override @Transactional public boolean isBranchInherit(String projectId, String branchId) { - Optional branch = branchRepo.findByProject_ProjectIdAndBranchId(projectId, branchId); - return branch.map(Branch::isInherit).orElse(false); + return branchPersistence.inheritsPermissions(projectId, branchId); } @Override @Transactional public boolean isOrgPublic(String orgId) { - Optional organization = orgRepo.findByOrganizationId(orgId); - if (!organization.isPresent()) { - throw new NotFoundException("org " + orgId + " not found"); - } - return organization.get().isPublic(); + return orgPersistence.hasPublicPermissions(orgId); } @Override @Transactional public boolean isProjectPublic(String projectId) { - Optional project = projectRepo.findByProjectId(projectId); - if (!project.isPresent()) { - throw new NotFoundException("project " + projectId + " not found"); - } - return project.get().isPublic(); + return projectPersistence.hasPublicPermissions(projectId); } @Override @Transactional public PermissionResponse getOrgGroupRoles(String orgId) { - Organization organization = getOrganization(orgId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(organization); + OrgJson organization = getOrganization(orgId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); return permissionsDelegate.getGroupRoles(); } @Override @Transactional public PermissionResponse getOrgUserRoles(String orgId) { - Organization organization = getOrganization(orgId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(organization); + OrgJson organization = getOrganization(orgId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); return permissionsDelegate.getUserRoles(); } @Override @Transactional public PermissionResponse getProjectGroupRoles(String projectId) { - Project project = getProject(projectId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(project); + ProjectJson project = getProject(projectId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); return permissionsDelegate.getGroupRoles(); } @Override @Transactional public PermissionResponse getProjectUserRoles(String projectId) { - Project project = getProject(projectId); - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(project); + ProjectJson project = getProject(projectId); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); return permissionsDelegate.getUserRoles(); } @Override @Transactional public PermissionResponse getBranchGroupRoles(String projectId, String branchId) { - Branch branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.IGNORE); + RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.IGNORE); if (branch == null) { return PermissionResponse.getDefaultResponse(); } - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(branch); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(branch); return permissionsDelegate.getGroupRoles(); } @Override @Transactional public PermissionResponse getBranchUserRoles(String projectId, String branchId) { - Branch branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.IGNORE); + RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.IGNORE); if (branch == null) { return PermissionResponse.getDefaultResponse(); } - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(branch); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(branch); return permissionsDelegate.getUserRoles(); } - private PermissionUpdatesResponse recalculateInheritedPerms(Project project) { + private PermissionUpdatesResponse recalculateInheritedPerms(ProjectJson project) { - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(project); + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); responseBuilder.insert(permissionsDelegate.recalculateInheritedPerms()); - if (project.getBranches() != null) { - for (Branch branch : project.getBranches()) { - responseBuilder.insert(recalculateInheritedPerms(branch)); - } + Collection branches = branchPersistence.findAll(project.getProjectId()); + for (RefJson branch : branches) { + responseBuilder.insert(recalculateInheritedPerms(branch)); } + return responseBuilder.getPermissionUpdatesReponse(); } - private PermissionUpdatesResponse recalculateInheritedPerms(Branch branch) { - PermissionsDelegate permissionsDelegate = getPermissionsDelegate(branch); + private PermissionUpdatesResponse recalculateInheritedPerms(RefJson branch) { + PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(branch); return permissionsDelegate.recalculateInheritedPerms(); } - private Organization getOrganization(String orgId) { - Optional org = orgRepo.findByOrganizationId(orgId); + private OrgJson getOrganization(String orgId) { + Optional org = orgPersistence.findById(orgId); if (!org.isPresent()) { throw new NotFoundException("Organization " + orgId + " not found"); @@ -357,10 +357,10 @@ private Organization getOrganization(String orgId) { return org.get(); } - private Project getProject(String projectId) { - Optional proj = projectRepo.findByProjectId(projectId); + private ProjectJson getProject(String projectId) { + Optional proj = projectPersistence.findById(projectId); - if (!proj.isPresent()) { + if (proj.isEmpty()) { throw new NotFoundException("Project " + projectId + " not found"); } return proj.get(); @@ -368,16 +368,17 @@ private Project getProject(String projectId) { private enum BRANCH_NOTFOUND_BEHAVIOR {THROW, CREATE, IGNORE} - private Branch getBranch(String projectId, String branchId, BRANCH_NOTFOUND_BEHAVIOR mode) { - Optional branch = branchRepo.findByProject_ProjectIdAndBranchId(projectId, branchId); - if(!branch.isPresent()) { - switch(mode){ + private RefJson getBranch(String projectId, String branchId, BRANCH_NOTFOUND_BEHAVIOR mode) { + Optional branch = branchPersistence.findById(projectId, branchId); + if (branch.isEmpty()) { + switch (mode) { case THROW: - throw new NotFoundException("Branch " + projectId + " " + branchId + " not found"); + throw new NotFoundException("Branch " + projectId + " " + branchId + " not found"); case CREATE: - Branch b = new Branch(getProject(projectId), branchId, false); - branchRepo.save(b); - return b; + RefJson b = new RefJson(); + b.setProjectId(projectId); + b.setRefId(branchId); + return branchPersistence.save(b); default: //do nothing break; @@ -385,44 +386,4 @@ private Branch getBranch(String projectId, String branchId, BRANCH_NOTFOUND_BEHA } return branch.orElse(null); } - - private PermissionsDelegate getPermissionsDelegate(final Organization organization) { - Optional permissionsDelegate = permissionsDelegateFactories.stream() - .map(v -> v.getPermissionsDelegate(organization)).filter(Objects::nonNull).findFirst(); - - if (permissionsDelegate.isPresent()) { - return permissionsDelegate.get(); - } - - throw new InternalErrorException( - "No valid permissions scheme found for organization " + organization.getOrganizationId() - + " (" + organization.getOrganizationName() + ")"); - } - - private PermissionsDelegate getPermissionsDelegate(final Project project) { - Optional permissionsDelegate = permissionsDelegateFactories.stream() - .map(v -> v.getPermissionsDelegate(project)).filter(Objects::nonNull).findFirst(); - - if(permissionsDelegate.isPresent()) { - return permissionsDelegate.get(); - } - - throw new InternalErrorException( - "No valid permissions scheme found for project " + project.getProjectId() - + " (" + project.getProjectName() + ")"); - } - - private PermissionsDelegate getPermissionsDelegate(final Branch branch) { - Optional permissionsDelegate = permissionsDelegateFactories.stream() - .map(v -> v.getPermissionsDelegate(branch)).filter(Objects::nonNull).findFirst(); - - if(permissionsDelegate.isPresent()) { - return permissionsDelegate.get(); - } - - throw new InternalErrorException( - "No valid permissions scheme found for branch " + branch.getBranchId() - + " of project " + branch.getProject().getProjectId() - + " (" + branch.getProject().getProjectName() + ")"); - } } diff --git a/core/src/main/java/org/openmbee/mms/core/services/GenericServiceFactory.java b/core/src/main/java/org/openmbee/mms/core/services/GenericServiceFactory.java new file mode 100644 index 000000000..d9d7fa097 --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/services/GenericServiceFactory.java @@ -0,0 +1,61 @@ +package org.openmbee.mms.core.services; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Component +public class GenericServiceFactory implements ApplicationContextAware{ + + private ApplicationContext context; + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + public void setApplicationContext(ApplicationContext context) { + this.context = context; + } + + public T getService(Class clazz) { + try { + Map beans = context.getBeansOfType(clazz); + if(beans.size() > 0) { + return beans.entrySet().iterator().next().getValue(); + } + } catch (BeansException e) { + logger.error("Error getting service for Class : "+ clazz.getName(), e.getMessage()); + } + return null; + } + + public T getServiceForSchema(Class clazz, String schema) { + if (clazz == null && schema == null) { + return null; + } + + //Try to find a matching service + try { + Map beans = context.getBeansOfType(clazz); + List> matches = beans.entrySet().stream() + .filter(v -> v.getKey().startsWith(schema)).collect(Collectors.toList()); + if(matches.size() >= 1) { + return matches.get(0).getValue(); + } + } catch (BeansException e) { + if (clazz != null) { + logger.error("Error getting service for Class : " + clazz.getName()); + } else { + logger.error("Error getting service for Class : class was null"); + } + } + return null; + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/openmbee/mms/core/services/HierarchicalNodeService.java b/core/src/main/java/org/openmbee/mms/core/services/HierarchicalNodeService.java new file mode 100644 index 000000000..c8e50d5bb --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/services/HierarchicalNodeService.java @@ -0,0 +1,11 @@ +package org.openmbee.mms.core.services; + +import org.openmbee.mms.json.MountJson; +import org.springframework.data.util.Pair; + +import java.util.List; + +public interface HierarchicalNodeService extends NodeService { + + MountJson getProjectUsages(String projectId, String refId, String commitId, List> saw, boolean restrictOnPermissions); +} diff --git a/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfo.java b/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfo.java index 31a11e217..2c13f013e 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfo.java +++ b/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfo.java @@ -1,76 +1,29 @@ package org.openmbee.mms.core.services; -import java.time.Instant; -import java.util.Map; -import java.util.Set; -import org.openmbee.mms.data.domains.scoped.Node; -import org.openmbee.mms.json.CommitJson; import org.openmbee.mms.json.ElementJson; -public class NodeChangeInfo extends NodeGetInfo { - - Map toSaveNodeMap; - - Map updatedMap; - - Map deletedMap; - - Set oldDocIds; - - CommitJson commitJson; - - Instant now; - - public Instant getNow() { - return now; - } +import java.time.Instant; +import java.util.Map; - public void setNow(Instant now) { - this.now = now; - } +public interface NodeChangeInfo extends NodeGetInfo { - public Map getToSaveNodeMap() { - return toSaveNodeMap; - } + Instant getInstant(); - public NodeChangeInfo setToSaveNodeMap(Map toSaveNodeMap) { - this.toSaveNodeMap = toSaveNodeMap; - return this; - } + void setInstant(Instant instant); - public Map getUpdatedMap() { - return updatedMap; - } + Map getUpdatedMap(); - public NodeChangeInfo setUpdatedMap(Map updatedMap) { - this.updatedMap = updatedMap; - return this; - } + NodeChangeInfo setUpdatedMap(Map updatedMap); - public Map getDeletedMap() { - return deletedMap; - } + Map getDeletedMap(); - public NodeChangeInfo setDeletedMap(Map deletedMap) { - this.deletedMap = deletedMap; - return this; - } + NodeChangeInfo setDeletedMap(Map deletedMap); - public Set getOldDocIds() { - return oldDocIds; - } + boolean getPreserveTimestamps(); - public NodeChangeInfo setOldDocIds(Set oldDocIds) { - this.oldDocIds = oldDocIds; - return this; - } + NodeChangeInfo setPreserveTimestamps(boolean preserveTimestamps); - public CommitJson getCommitJson() { - return commitJson; - } + boolean getOverwrite(); - public NodeChangeInfo setCommitJson(CommitJson commitJson) { - this.commitJson = commitJson; - return this; - } + NodeChangeInfo setOverwrite(boolean overwrite); } diff --git a/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java b/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java new file mode 100644 index 000000000..ed0616488 --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java @@ -0,0 +1,75 @@ +package org.openmbee.mms.core.services; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +import org.openmbee.mms.json.ElementJson; + +public class NodeChangeInfoImpl extends NodeGetInfoImpl implements NodeChangeInfo { + + private Map updatedMap; + + private Map deletedMap; + + private Instant instant; + + private boolean overwrite; + + private boolean preserveTimestamps; + + @Override + public Instant getInstant() { + return instant; + } + + @Override + public void setInstant(Instant instant) { + this.instant = instant; + } + + @Override + public Map getUpdatedMap() { + if(this.updatedMap == null){ + this.updatedMap = new HashMap(); + } + return updatedMap; + } + + @Override + public NodeChangeInfo setUpdatedMap(Map updatedMap) { + this.updatedMap = updatedMap; + return this; + } + + @Override + public Map getDeletedMap() { + return deletedMap; + } + + @Override + public NodeChangeInfo setDeletedMap(Map deletedMap) { + this.deletedMap = deletedMap; + return this; + } + + public boolean getOverwrite() { + return overwrite; + } + + @Override + public NodeChangeInfo setOverwrite(boolean overwrite) { + this.overwrite = overwrite; + return this; + } + + public boolean getPreserveTimestamps() { + return preserveTimestamps; + } + + @Override + public NodeChangeInfo setPreserveTimestamps(boolean preserveTimestamps) { + this.preserveTimestamps = preserveTimestamps; + return this; + } +} diff --git a/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfo.java b/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfo.java index a718e7c81..96ea56a92 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfo.java +++ b/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfo.java @@ -1,100 +1,33 @@ package org.openmbee.mms.core.services; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; import org.openmbee.mms.core.objects.Rejection; -import org.openmbee.mms.data.domains.scoped.Node; +import org.openmbee.mms.json.CommitJson; import org.openmbee.mms.json.ElementJson; -public class NodeGetInfo { - - Set reqIndexIds; - - Map reqElementMap; - - Map existingNodeMap; - - // string is node id - // map of element json objects - Map existingElementMap; - - Map activeElementMap; - - Map rejected; - - String commitId; - - - public Set getReqIndexIds() { - return reqIndexIds; - } - - public NodeGetInfo setReqIndexIds(Set reqIndexIds) { - this.reqIndexIds = reqIndexIds; - return this; - } - - public Map getReqElementMap() { - return reqElementMap; - } +import java.util.Map; - public NodeGetInfo setReqElementMap(Map reqElementMap) { - this.reqElementMap = reqElementMap; - return this; - } +public interface NodeGetInfo { - public Map getExistingNodeMap() { - return existingNodeMap; - } + CommitJson getCommitJson(); - public NodeGetInfo setExistingNodeMap(Map existingNodeMap) { - this.existingNodeMap = existingNodeMap; - return this; - } + NodeGetInfo setCommitJson(CommitJson commitJson); - public Map getActiveElementMap() { - return activeElementMap; - } + Map getReqElementMap(); - public NodeGetInfo setActiveElementMap( - Map activeElementMap) { - this.activeElementMap = activeElementMap; - return this; - } + NodeGetInfo setReqElementMap(Map reqElementMap); - public Map getExistingElementMap() { - return existingElementMap; - } + Map getActiveElementMap(); - public NodeGetInfo setExistingElementMap( - Map existingElementMap) { - this.existingElementMap = existingElementMap; - return this; - } + NodeGetInfo setActiveElementMap(Map activeElementMap); - public Map getRejected() { - return rejected; - } + Map getExistingElementMap(); - public NodeGetInfo setRejected(Map rejected) { - this.rejected = rejected; - return this; - } + NodeGetInfo setExistingElementMap(Map existingElementMap); - public void addRejection(String id, Rejection rejection) { - if (this.rejected == null) { - this.rejected = new HashMap<>(); - } - this.rejected.put(id, rejection); - } + Map getRejected(); - public void setCommitId(String commitId) { - this.commitId = commitId; - } + NodeGetInfo setRejected(Map rejected); - public String getCommitId() { - return this.commitId; - } + void addRejection(String id, Rejection rejection); } diff --git a/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfoImpl.java b/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfoImpl.java new file mode 100644 index 000000000..1f0e57bef --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfoImpl.java @@ -0,0 +1,87 @@ +package org.openmbee.mms.core.services; + +import java.util.HashMap; +import java.util.Map; +import org.openmbee.mms.core.objects.Rejection; +import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.ElementJson; + +public class NodeGetInfoImpl implements NodeGetInfo { + + private Map reqElementMap; + + // string is node id + // map of element json objects + private Map existingElementMap; + + private Map activeElementMap; + + private Map rejected; + + private CommitJson commitJson; + + @Override + public Map getReqElementMap() { + return reqElementMap; + } + + @Override + public NodeGetInfo setReqElementMap(Map reqElementMap) { + this.reqElementMap = reqElementMap; + return this; + } + + @Override + public Map getActiveElementMap() { + return activeElementMap; + } + + @Override + public NodeGetInfo setActiveElementMap( + Map activeElementMap) { + this.activeElementMap = activeElementMap; + return this; + } + + @Override + public Map getExistingElementMap() { + return existingElementMap; + } + + @Override + public NodeGetInfo setExistingElementMap( + Map existingElementMap) { + this.existingElementMap = existingElementMap; + return this; + } + + @Override + public Map getRejected() { + return rejected; + } + + @Override + public NodeGetInfo setRejected(Map rejected) { + this.rejected = rejected; + return this; + } + + @Override + public void addRejection(String id, Rejection rejection) { + if (this.rejected == null) { + this.rejected = new HashMap<>(); + } + this.rejected.put(id, rejection); + } + + @Override + public CommitJson getCommitJson() { + return commitJson; + } + + @Override + public NodeGetInfo setCommitJson(CommitJson commitJson) { + this.commitJson = commitJson; + return this; + } +} diff --git a/core/src/main/java/org/openmbee/mms/core/services/NodeService.java b/core/src/main/java/org/openmbee/mms/core/services/NodeService.java index adaae9665..d87fa3764 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/NodeService.java +++ b/core/src/main/java/org/openmbee/mms/core/services/NodeService.java @@ -7,8 +7,6 @@ import org.openmbee.mms.core.objects.ElementsCommitResponse; import org.openmbee.mms.core.objects.ElementsRequest; import org.openmbee.mms.core.objects.ElementsResponse; -import org.openmbee.mms.data.domains.scoped.Node; -import org.openmbee.mms.json.ElementJson; public interface NodeService { @@ -21,12 +19,6 @@ public interface NodeService { ElementsCommitResponse createOrUpdate(String projectId, String refId, ElementsRequest req, Map params, String user); - void extraProcessPostedElement(ElementJson element, Node node, NodeChangeInfo info); - - void extraProcessDeletedElement(ElementJson element, Node node, NodeChangeInfo info); - - void extraProcessGotElement(ElementJson element, Node node, NodeGetInfo info); - ElementsCommitResponse delete(String projectId, String refId, String id, String user); ElementsCommitResponse delete(String projectId, String refId, ElementsRequest req, String user); diff --git a/core/src/main/java/org/openmbee/mms/core/utils/ElementUtils.java b/core/src/main/java/org/openmbee/mms/core/utils/ElementUtils.java new file mode 100644 index 000000000..39084f70d --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/utils/ElementUtils.java @@ -0,0 +1,7 @@ +package org.openmbee.mms.core.utils; + +import org.openmbee.mms.json.ElementJson; + +public interface ElementUtils { + Enum getNodeType(ElementJson e); +} \ No newline at end of file diff --git a/core/src/main/java/org/openmbee/mms/core/utils/PermissionsDelegateUtil.java b/core/src/main/java/org/openmbee/mms/core/utils/PermissionsDelegateUtil.java new file mode 100644 index 000000000..c0378b3e8 --- /dev/null +++ b/core/src/main/java/org/openmbee/mms/core/utils/PermissionsDelegateUtil.java @@ -0,0 +1,70 @@ +package org.openmbee.mms.core.utils; + +import org.openmbee.mms.core.delegation.PermissionsDelegate; +import org.openmbee.mms.core.delegation.PermissionsDelegateFactory; +import org.openmbee.mms.core.exceptions.InternalErrorException; +import org.openmbee.mms.core.services.DefaultPermissionService; +import org.openmbee.mms.json.OrgJson; +import org.openmbee.mms.json.ProjectJson; +import org.openmbee.mms.json.RefJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +@Component +public class PermissionsDelegateUtil { + private static final Logger logger = LoggerFactory.getLogger(PermissionsDelegateUtil.class); + + private List permissionsDelegateFactories; + + @Autowired + public void setPermissionsDelegateFactories(List permissionsDelegateFactories) { + this.permissionsDelegateFactories = permissionsDelegateFactories; + } + + public PermissionsDelegate getPermissionsDelegate(final OrgJson organization) { + Optional permissionsDelegate = permissionsDelegateFactories.stream() + .map(v -> v.getPermissionsDelegate(organization)).filter(Objects::nonNull).findFirst(); + + if (permissionsDelegate.isPresent()) { + return permissionsDelegate.get(); + } + + throw new InternalErrorException( + "No valid permissions scheme found for organization " + organization.getId() + + " (" + organization.getName() + ")"); + } + + public PermissionsDelegate getPermissionsDelegate(final ProjectJson project) { + Optional permissionsDelegate = permissionsDelegateFactories.stream() + .map(v -> v.getPermissionsDelegate(project)).filter(Objects::nonNull).findFirst(); + + if(permissionsDelegate.isPresent()) { + return permissionsDelegate.get(); + } + + throw new InternalErrorException( + "No valid permissions scheme found for project " + project.getProjectId() + + " (" + project.getName() + ")"); + } + + public PermissionsDelegate getPermissionsDelegate(final RefJson branch) { + Optional permissionsDelegate = permissionsDelegateFactories.stream() + .map(v -> v.getPermissionsDelegate(branch)).filter(Objects::nonNull).findFirst(); + + if(permissionsDelegate.isPresent()) { + return permissionsDelegate.get(); + } + + throw new InternalErrorException( + "No valid permissions scheme found for branch " + + (branch.getRefId() == null ? "?" : branch.getRefId()) + + " of project " + + (branch.getProjectId() == null ? "?" : branch.getProjectId())); + } +} diff --git a/crud/src/main/java/org/openmbee/mms/crud/CrudConstants.java b/crud/src/main/java/org/openmbee/mms/crud/CrudConstants.java new file mode 100644 index 000000000..95e8a4f9e --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/CrudConstants.java @@ -0,0 +1,15 @@ +package org.openmbee.mms.crud; + +public class CrudConstants { + public static final String COMMIT = "Commit"; + public static final String COMMIT_SC = "commit"; + public static final String COMMITID = "commitId"; + public static final String DEFAULT = "default"; + public static final String EMPTY = "Empty"; + public static final String NODE = "Node"; + public static final String OVERWRITE = "overwrite"; + public static final String PRESERVETIMESTAMPS = "preserveTimestamps"; + public static final String PROJECT = "Project"; + public static final String ORG = "Org"; + public static final String CREATED = "created"; +} \ No newline at end of file diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/BaseController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/BaseController.java index e8171935d..bbf0abb53 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/BaseController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/BaseController.java @@ -3,7 +3,8 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import org.openmbee.mms.core.dao.ProjectDAO; +import org.openmbee.mms.core.dao.BranchPersistence; +import org.openmbee.mms.core.dao.ProjectPersistence; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.exceptions.ConflictException; import org.openmbee.mms.core.exceptions.DeletedException; @@ -18,7 +19,8 @@ import org.openmbee.mms.core.services.NodeService; import org.openmbee.mms.core.services.PermissionService; import org.openmbee.mms.crud.services.ServiceFactory; -import org.openmbee.mms.data.domains.global.Project; +import org.openmbee.mms.json.ProjectJson; +import org.openmbee.mms.json.RefJson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -35,7 +37,9 @@ public abstract class BaseController { protected ServiceFactory serviceFactory; - protected ProjectDAO projectRepository; + protected ProjectPersistence projectPersistence; + + protected BranchPersistence branchPersistence; protected PermissionService permissionService; @@ -47,8 +51,13 @@ public void setServiceFactory(ServiceFactory serviceFactory) { } @Autowired - public void setProjectRepository(ProjectDAO projectRepository) { - this.projectRepository = projectRepository; + public void setProjectPersistence(ProjectPersistence projectPersistence) { + this.projectPersistence = projectPersistence; + } + + @Autowired + public void setBranchPersistence(BranchPersistence branchPersistence) { + this.branchPersistence = branchPersistence; } @Autowired @@ -70,8 +79,16 @@ public Map convertToMap(Object obj) { return om.convertValue(obj, new TypeReference>() {}); } + protected void findBranch(String projectId, String id){ + getProjectType(projectId); + Optional branchesOption = branchPersistence.findById(projectId, id); + if (!branchesOption.isPresent()) { + throw new NotFoundException("branch not found"); + } + } + protected String getProjectType(String projectId) { - Optional p = projectRepository.findByProjectId(projectId); + Optional p = projectPersistence.findById(projectId); if (p.isPresent()) { return p.get().getProjectType(); } diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java index 851701b83..096904e52 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java @@ -1,6 +1,8 @@ package org.openmbee.mms.crud.controllers.branches; import io.swagger.v3.oas.annotations.tags.Tag; + +import java.util.Optional; import java.util.UUID; import org.openmbee.mms.core.config.Privileges; import org.openmbee.mms.core.exceptions.BadRequestException; @@ -41,6 +43,8 @@ public RefsResponse getAllRefs( @PathVariable String projectId, Authentication auth) { + getProjectType(projectId); + RefsResponse res = branchService.getBranches(projectId); if (!permissionService.isProjectPublic(projectId)) { List filtered = new ArrayList<>(); @@ -94,7 +98,14 @@ public RefsResponse createRefs( RefJson res; branch.setCreator(auth.getName()); if (branch.getParentCommitId() == null || branch.getParentCommitId().isEmpty()) { - res = branchService.createBranch(projectId, branch); + Optional existingOptional = branchPersistence.findById(projectId, branch.getId()); + if (existingOptional.isPresent()) { + //Branch exists, should merge the json + branch.merge(existingOptional.get()); + res = branchService.updateBranch(projectId, branch); + } else { + res = branchService.createBranch(projectId, branch); + } } else { //TODO implement branching from historical commit response.addRejection(new Rejection(branch, 400, "Branching from historical commits is not implemented.")); diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/elements/ElementsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/elements/ElementsController.java index fef7654c1..07eca2d81 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/elements/ElementsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/elements/ElementsController.java @@ -19,6 +19,7 @@ import org.openmbee.mms.core.objects.ElementsRequest; import org.openmbee.mms.core.objects.ElementsResponse; import org.openmbee.mms.core.services.CommitService; +import org.openmbee.mms.crud.CrudConstants; import org.openmbee.mms.crud.controllers.BaseController; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.services.NodeService; @@ -124,7 +125,7 @@ public ElementsCommitResponse createOrUpdateElements( NodeService nodeService = getNodeService(projectId); return nodeService.createOrUpdate(projectId, refId, req, params, auth.getName()); } - throw new BadRequestException(response.addMessage("Empty")); + throw new BadRequestException(response.addMessage(CrudConstants.EMPTY)); } /* @PostMapping(value = "/stream", consumes = MediaType.APPLICATION_JSON_VALUE) @@ -190,7 +191,7 @@ public ElementsResponse getElements( NodeService nodeService = getNodeService(projectId); return nodeService.read(projectId, refId, req, params); } - throw new BadRequestException(response.addMessage("Empty")); + throw new BadRequestException(response.addMessage(CrudConstants.EMPTY)); } @DeleteMapping(value = "/{elementId}") diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java index c6b4c9d0f..a003b640c 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java @@ -1,15 +1,20 @@ package org.openmbee.mms.crud.controllers.orgs; import io.swagger.v3.oas.annotations.tags.Tag; + +import java.time.Instant; +import java.util.Collection; import java.util.List; import java.util.UUID; import java.util.Optional; + +import org.openmbee.mms.core.config.Formats; import org.openmbee.mms.core.config.Privileges; -import org.openmbee.mms.core.dao.OrgDAO; +import org.openmbee.mms.core.dao.OrgPersistence; import org.openmbee.mms.core.objects.OrganizationsRequest; import org.openmbee.mms.core.objects.OrganizationsResponse; import org.openmbee.mms.core.objects.Rejection; -import org.openmbee.mms.data.domains.global.Organization; +import org.openmbee.mms.crud.CrudConstants; import org.openmbee.mms.crud.controllers.BaseController; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.exceptions.NotFoundException; @@ -32,10 +37,10 @@ @Tag(name = "Orgs") public class OrgsController extends BaseController { - OrgDAO organizationRepository; + OrgPersistence organizationRepository; @Autowired - public OrgsController(OrgDAO organizationRepository) { + public OrgsController(OrgPersistence organizationRepository) { this.organizationRepository = organizationRepository; } @@ -44,12 +49,10 @@ public OrgsController(OrgDAO organizationRepository) { public OrganizationsResponse getAllOrgs( Authentication auth) { OrganizationsResponse response = new OrganizationsResponse(); - List allOrgs = organizationRepository.findAll(); - for (Organization org : allOrgs) { - if (mss.hasOrgPrivilege(auth, org.getOrganizationId(), Privileges.ORG_READ.name(), true)) { - OrgJson orgJson = new OrgJson(); - orgJson.merge(convertToMap(org)); - response.getOrgs().add(orgJson); + Collection allOrgs = organizationRepository.findAll(); + for (OrgJson org : allOrgs) { + if (mss.hasOrgPrivilege(auth, org.getId(), Privileges.ORG_READ.name(), true)) { + response.getOrgs().add(org); } } return response; @@ -62,13 +65,11 @@ public OrganizationsResponse getOrg( @PathVariable String orgId) { OrganizationsResponse response = new OrganizationsResponse(); - Optional orgOption = organizationRepository.findByOrganizationId(orgId); + Optional orgOption = organizationRepository.findById(orgId); if (!orgOption.isPresent()) { throw new NotFoundException(response.addMessage("Organization not found.")); } - OrgJson orgJson = new OrgJson(); - orgJson.merge(convertToMap(orgOption.get())); - response.getOrgs().add(orgJson); + response.getOrgs().add(orgOption.get()); return response; } @@ -89,25 +90,32 @@ public OrganizationsResponse createOrUpdateOrgs( org.setId(UUID.randomUUID().toString()); } - Organization o = organizationRepository.findByOrganizationId(org.getId()) - .orElse(new Organization()); + OrgJson o = organizationRepository.findById(org.getId()).orElse(new OrgJson()); boolean newOrg = true; if (o.getId() != null) { - if (!mss.hasOrgPrivilege(auth, o.getOrganizationId(), Privileges.ORG_EDIT.name(), false)) { + if (!mss.hasOrgPrivilege(auth, o.getId(), Privileges.ORG_EDIT.name(), false)) { response.addRejection(new Rejection(org, 403, "No permission to update org")); continue; } newOrg = false; + org.merge(o); + } else { + if (org.getCreated() == null || org.getCreated().isEmpty()) { + org.setCreated(Formats.FORMATTER.format(Instant.now())); + } + if (org.getType() == null || org.getType().isEmpty()) { + org.setType(CrudConstants.ORG); + } + if (org.getCreator() == null || org.getCreator().isEmpty()) { + org.setCreator(auth.getName()); + } } - o.setOrganizationId(org.getId()); - o.setOrganizationName(org.getName()); - logger.info("Saving organization: {}", o.getOrganizationId()); - Organization saved = organizationRepository.save(o); + logger.info("Saving organization: {}", org.getId()); + OrgJson saved = organizationRepository.save(org); if (newOrg) { permissionService.initOrgPerms(org.getId(), auth.getName()); } - org.merge(convertToMap(saved)); - response.getOrgs().add(org); + response.getOrgs().add(saved); } if (orgPost.getOrgs().size() == 1) { handleSingleResponse(response); @@ -122,15 +130,15 @@ public OrganizationsResponse deleteOrg( @PathVariable String orgId) { OrganizationsResponse response = new OrganizationsResponse(); - Optional orgOption = organizationRepository.findByOrganizationId(orgId); + Optional orgOption = organizationRepository.findById(orgId); if (!orgOption.isPresent()) { throw new NotFoundException(response.addMessage("Organization not found.")); } - Organization org = orgOption.get(); - if (!org.getProjects().isEmpty()) { + if (!projectPersistence.findAllByOrgId(orgId).isEmpty()) { throw new BadRequestException(response.addMessage("Organization is not empty")); } - organizationRepository.delete(org); + OrgJson deleted = organizationRepository.deleteById(orgId); + response.setOrgs(List.of(deleted)); return response; } } diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java index 8dc62b6a9..cecd977a5 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java @@ -1,42 +1,29 @@ package org.openmbee.mms.crud.controllers.projects; import io.swagger.v3.oas.annotations.tags.Tag; - -import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.config.Privileges; -import org.openmbee.mms.core.config.ProjectSchemas; -import org.openmbee.mms.core.dao.ProjectDAO; -import org.openmbee.mms.core.dao.ProjectIndex; +import org.openmbee.mms.core.config.*; +import org.openmbee.mms.core.exceptions.BadRequestException; +import org.openmbee.mms.core.exceptions.DeletedException; import org.openmbee.mms.core.exceptions.MMSException; +import org.openmbee.mms.core.exceptions.NotFoundException; import org.openmbee.mms.core.objects.ProjectsRequest; import org.openmbee.mms.core.objects.ProjectsResponse; -import org.openmbee.mms.core.exceptions.DeletedException; import org.openmbee.mms.core.objects.Rejection; -import org.openmbee.mms.data.domains.global.Project; -import org.openmbee.mms.crud.controllers.BaseController; -import org.openmbee.mms.core.exceptions.BadRequestException; -import org.openmbee.mms.core.exceptions.NotFoundException; import org.openmbee.mms.core.services.ProjectService; +import org.openmbee.mms.crud.CrudConstants; +import org.openmbee.mms.crud.controllers.BaseController; +import org.openmbee.mms.crud.services.ProjectDeleteService; import org.openmbee.mms.json.ProjectJson; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; +import org.springframework.web.bind.annotation.*; + +import java.time.Instant; +import java.util.*; + @RestController @RequestMapping("/projects") @@ -45,53 +32,53 @@ public class ProjectsController extends BaseController { private static final String PROJECT_ID_VALID_PATTERN = "^[\\w-]+$"; - ProjectDAO projectRepository; - ProjectIndex projectIndex; - ProjectSchemas schemas; + private ProjectDeleteService projectDeleteService; + private ProjectSchemas projectSchemas; + @Autowired + public void setProjectDeleteService(ProjectDeleteService projectDeleteService) { + this.projectDeleteService = projectDeleteService; + } @Autowired - public ProjectsController(ProjectDAO projectRepository, ProjectIndex projectIndex, ProjectSchemas schemas) { - this.projectRepository = projectRepository; - this.projectIndex = projectIndex; - this.schemas = schemas; + public void setProjectSchemas(ProjectSchemas projectSchemas) { + this.projectSchemas = projectSchemas; } @GetMapping - public ProjectsResponse getAllProjects(Authentication auth, - @RequestParam(required = false) String orgId) { + @Transactional(readOnly = true) + public ProjectsResponse getAllProjects(Authentication auth) { + ProjectsResponse response = new ProjectsResponse(); - List allProjects = orgId != null ? projectRepository.findAllByOrgId(orgId) : projectRepository.findAll(); - for (Project proj : allProjects) { - if (mss.hasProjectPrivilege(auth, proj.getProjectId(), Privileges.PROJECT_READ.name(), true)) { - ContextHolder.setContext(proj.getProjectId()); - if(proj.getDocId() != null && !proj.isDeleted()) { - Optional projectJsonOption = projectIndex.findById(proj.getDocId()); - projectJsonOption.ifPresentOrElse(json -> response.getProjects().add(json), ()-> { - logger.error("Project json not found for id: {}", proj.getProjectId()); - }); + List allProjects = projectPersistence.findAll(); + for (ProjectJson projectJson : allProjects) { + try { + if (mss.hasProjectPrivilege(auth, projectJson.getProjectId(), Privileges.PROJECT_READ.name(), true) + && projectJson.getDocId() != null + && !Constants.TRUE.equals(projectJson.getIsDeleted())) { + response.getProjects().add(projectJson); } + } catch(NotFoundException ex) { + logger.error("Project {} was not found when getting all projects.", projectJson.getProjectId()); } } return response; } @GetMapping(value = "/{projectId}") + @Transactional(readOnly = true) @PreAuthorize("@mss.hasProjectPrivilege(authentication, #projectId, 'PROJECT_READ', true)") public ProjectsResponse getProject( @PathVariable String projectId) { ContextHolder.setContext(projectId); ProjectsResponse response = new ProjectsResponse(); - Optional projectOption = projectRepository.findByProjectId(projectId); - if (!projectOption.isPresent()) { + Optional projectOption = projectPersistence.findById(projectId); + if (projectOption.isEmpty()) { throw new NotFoundException(response.addMessage("Project not found")); } - Optional projectJsonOption = projectIndex.findById(projectOption.get().getDocId()); - projectJsonOption.ifPresentOrElse(json -> response.getProjects().add(json), ()-> { - throw new NotFoundException(response.addMessage("Project JSON not found")); - }); - if (projectOption.get().isDeleted()) { + response.getProjects().add(projectOption.get()); + if (Constants.TRUE.equals(projectOption.get().getIsDeleted())) { throw new DeletedException(response); } return response; @@ -113,59 +100,72 @@ public ProjectsResponse createOrUpdateProjects( try { if (json.getProjectId() == null || json.getProjectId().isEmpty()) { json.setId(UUID.randomUUID().toString()); - } - if (!isProjectIdValid(json.getProjectId())) { + } else if (!isProjectIdValid(json.getProjectId())) { response.addRejection(new Rejection(json, 400, "Project id is invalid.")); continue; } - if ((json.getProjectType() != null) && (!((schemas.getSchemas()) - .containsKey(json.getProjectType())))) { - response.addRejection(new Rejection(json, 400, "Project schema is unknown.")); - continue; + + Optional existingOptional = projectPersistence.findById(json.getProjectId()); + if(existingOptional.isPresent()) { + //Project exists, should merge the json + json.merge(existingOptional.get()); + } else { + //New Project + if (json.getCreated() == null || json.getCreated().isEmpty()) { + json.setCreated(Formats.FORMATTER.format(Instant.now())); + } + if (json.getType() == null || json.getType().isEmpty()) { + json.setType(CrudConstants.PROJECT); + } + if ((json.getProjectType() != null) && (!((projectSchemas.getSchemas()) + .containsKey(json.getProjectType())))) { + response.addRejection(new Rejection(json, 400, "Project schema is unknown.")); + continue; + } + //This needs to be used for create and update + if (json.getCreator() == null || json.getCreator().isEmpty()) { + json.setCreator(auth.getName()); + } } + ProjectService ps = getProjectService(json); - if (!ps.exists(json.getProjectId())) { + String projectId = json.getProjectId(); + if (!ps.exists(projectId)) { if (!mss.hasOrgPrivilege(auth, json.getOrgId(), Privileges.ORG_CREATE_PROJECT.name(), false)) { response.addRejection(new Rejection(json, 403, "No permission to create project under org")); continue; } - if (json.getCreator() == null || json.getCreator().isEmpty()) { - json.setCreator(auth.getName()); - } response.getProjects().add(ps.create(json)); - permissionService.initProjectPerms(json.getProjectId(), true, auth.getName()); + permissionService.initProjectPerms(projectId, true, auth.getName()); } else { if (!mss.hasProjectPrivilege(auth, json.getProjectId(), Privileges.PROJECT_EDIT.name(), false)) { response.addRejection(new Rejection(json, 403, "No permission to change project")); continue; } - boolean updateInheritedPerms = false; if (json.getOrgId() != null && !json.getOrgId().isEmpty()) { - Project proj = projectRepository.findByProjectId(json.getProjectId()).get(); - String existingOrg = proj.getOrgId(); - if (!json.getOrgId().equals(existingOrg)) { - if (!mss.hasProjectPrivilege(auth, json.getProjectId(), Privileges.PROJECT_DELETE.name(), false) || - !mss.hasOrgPrivilege(auth, json.getOrgId(), Privileges.ORG_CREATE_PROJECT.name(), false)) { - response.addRejection( - new Rejection(json, 403, "No permission to move project org")); - continue; + projectPersistence.findById(json.getProjectId()).ifPresent(projectJson -> { + String existingOrg = projectJson.getOrgId(); + if (!json.getOrgId().equals(existingOrg)) { + if (!mss.hasProjectPrivilege(auth, json.getProjectId(), Privileges.PROJECT_DELETE.name(), false) || + !mss.hasOrgPrivilege(auth, json.getOrgId(), Privileges.ORG_CREATE_PROJECT.name(), false)) { + response.addRejection( + new Rejection(json, 403, "No permission to move project org")); + } + if (projectPersistence.inheritsPermissions(projectJson.getProjectId())) { + permissionService.setProjectInherit(false, json.getProjectId()); + permissionService.setProjectInherit(true, json.getProjectId()); + } } - if (proj.isInherit()) { - updateInheritedPerms = true; + } - } + ); } response.getProjects().add(ps.update(json)); - if (updateInheritedPerms) { - permissionService.setProjectInherit(false, json.getProjectId()); - permissionService.setProjectInherit(true, json.getProjectId()); - } } } catch (MMSException ex) { response.addRejection(new Rejection(json, ex.getCode().value(), ex.getMessageObject().toString())); - continue; } } if (projectsPost.getProjects().size() == 1) { @@ -180,27 +180,12 @@ public ProjectsResponse createOrUpdateProjects( @PreAuthorize("@mss.hasProjectPrivilege(authentication, #projectId, 'PROJECT_DELETE', false)") public ProjectsResponse deleteProject( @PathVariable String projectId, - @RequestParam(required = false, defaultValue = "false") boolean hard) { + @RequestParam(required = false, defaultValue = Constants.FALSE) boolean hard) { - ProjectsResponse response = new ProjectsResponse(); - Optional projectOption = projectRepository.findByProjectId(projectId); - if (!projectOption.isPresent()) { - throw new NotFoundException(response.addMessage("Project not found")); - } - Project project = projectOption.get(); - project.setDeleted(true); - ProjectJson projectJson = new ProjectJson(); - projectJson.merge(convertToMap(project)); - List res = new ArrayList<>(); - res.add(projectJson); - if (hard) { - projectRepository.delete(project); - projectIndex.delete(projectId); - } else { - projectRepository.save(project); - // TODO soft delete for index? + if(!isProjectIdValid(projectId)) { + throw new BadRequestException("Invalid project id"); } - return response.setProjects(res); + return projectDeleteService.deleteProject(projectId, hard); } private ProjectService getProjectService(ProjectJson json) { @@ -209,7 +194,7 @@ private ProjectService getProjectService(ProjectJson json) { try { type = this.getProjectType(json.getProjectId()); } catch (NotFoundException e) { - type = "default"; + type = CrudConstants.DEFAULT; } json.setProjectType(type); } diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/CommitDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/CommitDomain.java new file mode 100644 index 000000000..8ad7e9b56 --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/CommitDomain.java @@ -0,0 +1,22 @@ +package org.openmbee.mms.crud.domain; + +import org.openmbee.mms.core.config.Formats; +import org.openmbee.mms.crud.CrudConstants; +import org.openmbee.mms.json.CommitJson; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.ArrayList; + +@Component +public class CommitDomain { + + public void initCommitJson(CommitJson cmjs, Instant now) { + cmjs.setCreated(Formats.FORMATTER.format(now)); + cmjs.setAdded(new ArrayList<>()); + cmjs.setDeleted(new ArrayList<>()); + cmjs.setUpdated(new ArrayList<>()); + cmjs.setType(CrudConstants.COMMIT); + } + +} diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/ElementDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/ElementDomain.java new file mode 100644 index 000000000..766ce627c --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/ElementDomain.java @@ -0,0 +1,12 @@ +package org.openmbee.mms.crud.domain; + +import org.openmbee.mms.core.utils.ElementUtils; +import org.openmbee.mms.json.ElementJson; + +public interface ElementDomain { + + public abstract ElementUtils getElementUtils(String projectId); + + public abstract Integer getNodeType(String projectId, ElementJson element); + +} \ No newline at end of file diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java new file mode 100644 index 000000000..a0cd2a257 --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java @@ -0,0 +1,28 @@ +package org.openmbee.mms.crud.domain; + +import org.openmbee.mms.json.ElementJson; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class JsonDomain { + + public Map convertJsonToMap(List elements) { + if (elements == null || elements.isEmpty()) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + + for (ElementJson elem : elements) { + if (elem == null) { + continue; + } + if (elem.getId() != null && !elem.getId().equals("")) { + result.put(elem.getId(), elem); + } + } + return result; + } + +} diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java new file mode 100644 index 000000000..3dfc4a612 --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java @@ -0,0 +1,145 @@ +package org.openmbee.mms.crud.domain; + +import org.openmbee.mms.core.config.Formats; +import org.openmbee.mms.core.objects.Rejection; +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.core.services.NodeChangeInfoImpl; +import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.json.BaseJson; +import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.ElementJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.ParseException; +import java.time.Instant; +import java.util.*; + +public abstract class NodeChangeDomain extends JsonDomain { + private static final Logger logger = LoggerFactory.getLogger(NodeChangeDomain.class); + + private NodeGetDomain nodeGetDomain; + private CommitDomain commitDomain; + + public NodeChangeDomain(NodeGetDomain nodeGetDomain, CommitDomain commitDomain) { + this.nodeGetDomain = nodeGetDomain; + this.commitDomain = commitDomain; + } + + public NodeChangeInfo initInfo(CommitJson commitJson, boolean overwrite, boolean preserveTimestamps) { + + NodeChangeInfo info = (NodeChangeInfo) nodeGetDomain.initInfo(commitJson, this::createNodeChangeInfo); + + Instant now = Instant.now(); + if (commitJson != null) { + commitDomain.initCommitJson(commitJson, now); + } + + info.setUpdatedMap(new HashMap<>()); + info.setDeletedMap(new HashMap<>()); + info.setInstant(now); + info.setOverwrite(overwrite); + info.setPreserveTimestamps(preserveTimestamps); + + return info; + } + + + public void processElementAdded(NodeChangeInfo info, ElementJson element) { + CommitJson commitJson = info.getCommitJson(); + processElementAddedOrUpdated(info, element); + + element.setCreator(commitJson.getCreator()); //Only set on creation of new element + element.setCreated(commitJson.getCreated()); + } + + public void processElementUpdated(NodeChangeInfo info, ElementJson element) { + processElementAddedOrUpdated(info, element); + } + + protected void processElementAddedOrUpdated(NodeChangeInfo info, ElementJson element) { + CommitJson commitJson = info.getCommitJson(); + element.setProjectId(commitJson.getProjectId()); + element.setRefId(commitJson.getRefId()); + List inRefIds = new ArrayList<>(); + inRefIds.add(commitJson.getRefId()); + element.setInRefIds(inRefIds); + element.setCommitId(commitJson.getId()); + + if(!info.getPreserveTimestamps()) { + element.setModified(commitJson.getCreated()); + element.setModifier(commitJson.getCreator()); + } + + info.getUpdatedMap().put(element.getId(), element); + } + + public void processElementDeleted(NodeChangeInfo info, ElementJson element) { + //TODO Probably don't need this (see method below) + element.setIsDeleted("true"); + } + + public NodeChangeInfo processDeleteJson(NodeChangeInfo info, Collection elements) { + + for(ElementJson element : elements) { + //TODO I don't think we need to do this by default, it shouldn't come back if deleted + if (Boolean.parseBoolean(element.getIsDeleted())) { + info.addRejection(element.getId(), new Rejection(element, 304, "Already deleted")); + continue; + } + + ElementJson request = info.getReqElementMap().get(element.getId()); + request.putAll(element); + processElementDeleted(info, request); + info.getDeletedMap().put(element.getId(), request); + } + return info; + } + + protected NodeChangeInfo createNodeChangeInfo() { + return new NodeChangeInfoImpl(); + } + + protected void rejectNotFound(NodeGetInfo info, String elementId) { + info.addRejection(elementId, new Rejection(elementId, 404, "Not Found")); + } + + protected boolean isUpdated(BaseJson element, Map existing, NodeChangeInfo info) { + + if (element.isPartialOf(existing)) { + info.addRejection(element.getId(), new Rejection(element, 304, "Is Equivalent")); + return false; + } + return true; + } + + protected boolean diffUpdateJson(BaseJson element, Map existing, NodeChangeInfo info) { + + String jsonModified = element.getModified(); + Object existingModified = existing.get(BaseJson.MODIFIED); + if (jsonModified != null && !jsonModified.isEmpty()) { + try { + Date jsonModDate = Formats.SIMPLE_DATE_FORMAT.parse(jsonModified); + Date existingModDate = Formats.SIMPLE_DATE_FORMAT.parse(existingModified.toString()); + if (jsonModDate.before(existingModDate)) { + info.addRejection(element.getId(), new Rejection(element, 409, "Conflict Detected")); + return false; + } + } catch (ParseException e) { + logger.info("date parse exception: {} {}", jsonModified, existingModified); + } + } + element.merge(existing); + return true; + } + + // create new elastic id for all element json, update modified time, modifier (use dummy for now), set _projectId, _refId, _inRefIds + public abstract NodeChangeInfo processPostJson(NodeChangeInfo nodeChangeInfo, Collection elements); + + + public void addExistingElements(NodeChangeInfo nodeChangeInfo, List existingElements) { + nodeGetDomain.addExistingElements(nodeChangeInfo, existingElements); + } + + public abstract void primeNodeChangeInfo(NodeChangeInfo nodeChangeInfo, Collection transactedElements); +} diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java new file mode 100644 index 000000000..d71086fd7 --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java @@ -0,0 +1,58 @@ +package org.openmbee.mms.crud.domain; + +import org.openmbee.mms.core.objects.Rejection; +import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.core.services.NodeGetInfoImpl; +import org.openmbee.mms.core.services.NodeService; +import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.ElementJson; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +public class NodeGetDomain extends JsonDomain { + + public NodeGetInfo initInfo(CommitJson commitJson) { + return initInfo(commitJson, this::createNodeGetInfo); + } + + public NodeGetInfo initInfo(CommitJson commitJson, NodeGetInfoFactory nodeGetInfoFactory) { + Map existingElementMap = new HashMap<>(); + Map elementMapForRequests = new HashMap<>(); + + NodeGetInfo info = nodeGetInfoFactory.get(); + + info.setCommitJson(commitJson); + info.setExistingElementMap(existingElementMap); + info.setReqElementMap(elementMapForRequests); + info.setRejected(new HashMap<>()); + info.setActiveElementMap(new HashMap<>()); + + return info; + } + + public NodeGetInfo processGetJson(String projectId, String refId, String commitId, List elements, NodeService service) { + NodeGetInfo info = initInfo(null); + //TODO do we need to do anything here? + return info; + } + + protected void rejectNotFound(NodeGetInfo info, String elementId) { + info.addRejection(elementId, new Rejection(elementId, 404, "Not Found")); + } + + protected void rejectDeleted(NodeGetInfo info, String nodeId, ElementJson indexElement) { + info.addRejection(nodeId, new Rejection(indexElement, 410, "Element deleted")); + } + + public NodeGetInfo createNodeGetInfo() { + return new NodeGetInfoImpl(); + } + + public void addExistingElements(NodeGetInfo info, List elements) { + elements.forEach(e -> info.getExistingElementMap().put(e.getId(), e)); + } +} diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetInfoFactory.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetInfoFactory.java new file mode 100644 index 000000000..c764abdc5 --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetInfoFactory.java @@ -0,0 +1,7 @@ +package org.openmbee.mms.crud.domain; + +import org.openmbee.mms.core.services.NodeGetInfo; + +public interface NodeGetInfoFactory { + NodeGetInfo get(); +} diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultBranchService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultBranchService.java index 72201fb06..0a38bd6ef 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultBranchService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultBranchService.java @@ -3,13 +3,8 @@ import java.util.Collection; import org.openmbee.mms.core.config.Constants; -import org.openmbee.mms.core.config.ContextHolder; import org.openmbee.mms.core.config.Formats; -import org.openmbee.mms.core.dao.BranchDAO; -import org.openmbee.mms.core.dao.BranchIndexDAO; -import org.openmbee.mms.core.dao.CommitDAO; -import org.openmbee.mms.core.dao.NodeDAO; -import org.openmbee.mms.core.dao.NodeIndexDAO; +import org.openmbee.mms.core.dao.*; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.exceptions.DeletedException; import org.openmbee.mms.core.exceptions.InternalErrorException; @@ -18,9 +13,8 @@ import org.openmbee.mms.core.objects.RefsResponse; import org.openmbee.mms.core.services.BranchService; import org.openmbee.mms.core.services.EventService; -import org.openmbee.mms.data.domains.scoped.Branch; -import org.openmbee.mms.data.domains.scoped.Commit; -import org.openmbee.mms.data.domains.scoped.Node; +import org.openmbee.mms.crud.CrudConstants; +import org.openmbee.mms.json.CommitJson; import org.openmbee.mms.json.RefJson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,41 +28,27 @@ public class DefaultBranchService implements BranchService { protected final Logger logger = LoggerFactory.getLogger(getClass()); - private BranchDAO branchRepository; + private BranchPersistence branchPersistence; - private BranchIndexDAO branchIndex; + private CommitPersistence commitPersistence; - private CommitDAO commitRepository; - - private NodeDAO nodeRepository; - - private NodeIndexDAO nodeIndex; + private NodePersistence nodePersistence; protected Collection eventPublisher; @Autowired - public void setBranchRepository(BranchDAO branchRepository) { - this.branchRepository = branchRepository; + public void setBranchPersistence(BranchPersistence branchPersistence) { + this.branchPersistence = branchPersistence; } @Autowired - public void setCommitRepository(CommitDAO commitRepository) { - this.commitRepository = commitRepository; + public void setCommitPersistence(CommitPersistence commitPersistence) { + this.commitPersistence = commitPersistence; } @Autowired - public void setNodeRepository(NodeDAO nodeRepository) { - this.nodeRepository = nodeRepository; - } - - @Autowired - public void setNodeIndex(NodeIndexDAO nodeIndex) { - this.nodeIndex = nodeIndex; - } - - @Autowired - public void setBranchIndex(BranchIndexDAO branchIndex) { - this.branchIndex = branchIndex; + public void setNodePersistence(NodePersistence nodePersistence) { + this.nodePersistence = nodePersistence; } @Autowired @@ -77,124 +57,107 @@ public void setEventPublisher(Collection eventPublisher) { } public RefsResponse getBranches(String projectId) { - ContextHolder.setContext(projectId); RefsResponse branchesResponse = new RefsResponse(); - List branches = this.branchRepository.findAll(); - Set docIds = new HashSet<>(); - branches.forEach(branch -> { - docIds.add(branch.getDocId()); - }); - branchesResponse.setRefs(branchIndex.findAllById(docIds)); + branchesResponse.setRefs(branchPersistence.findAll(projectId)); return branchesResponse; } public RefsResponse getBranch(String projectId, String id) { - ContextHolder.setContext(projectId); - RefsResponse branchesResponse = new RefsResponse(); - Optional branchesOption = this.branchRepository.findByBranchId(id); - if (!branchesOption.isPresent()) { - throw new NotFoundException(branchesResponse); + RefsResponse response = new RefsResponse(); + Optional refJsonOptional = branchPersistence.findById(projectId, id); + if (refJsonOptional.isEmpty()) { + throw new NotFoundException(response.addMessage("Branch not found")); } - Branch b = branchesOption.get(); - List refs = new ArrayList<>(); - Optional refOption = branchIndex.findById(b.getDocId()); - if (!refOption.isPresent()) { - logger.error("DefaultBranchService: JSON Not found for {} with docId: {}", - b.getBranchId(), b.getDocId()); - throw new NotFoundException(branchesResponse); - } - refs.add(refOption.get()); - branchesResponse.setRefs(refs); - if (b.isDeleted()) { - throw new DeletedException(branchesResponse); + response.getRefs().add(refJsonOptional.get()); + if (refJsonOptional.get().isDeleted()) { + throw new DeletedException(response); } - return branchesResponse; + return response; } public RefJson createBranch(String projectId, RefJson branch) { + logger.info("Saving branch: {}", branch.getId()); + Instant now = Instant.now(); - ContextHolder.setContext(projectId); - Branch b = new Branch(); - - b.setBranchId(branch.getId()); - b.setBranchName(branch.getName()); - b.setDescription(branch.getDescription()); - b.setTag(branch.isTag()); - b.setTimestamp(now); branch.setCreated(Formats.FORMATTER.format(now)); branch.setDeleted(false); branch.setProjectId(projectId); - branch.setStatus("created"); + branch.setStatus(CrudConstants.CREATED); - if (branch.getDocId() == null || branch.getDocId().isEmpty()) { - String docId = branchIndex.createDocId(branch); - branch.setDocId(docId); - b.setDocId(docId); + //Ensure that the type is of Branch + if (branch.getType() == null || branch.getType().isEmpty()) { + branch.setType(Constants.BRANCH_TYPE); } - logger.info("Saving branch: {}", branch.getId()); + + //Find parent branch + String parentRefId; if (branch.getParentRefId() != null) { - b.setParentRefId(branch.getParentRefId()); + parentRefId = branch.getParentRefId(); } else { - branch.setParentRefId(Constants.MASTER_BRANCH); - b.setParentRefId(Constants.MASTER_BRANCH); + parentRefId = Constants.MASTER_BRANCH; + branch.setParentRefId(parentRefId); } - - //This service cannot create branches from historic versions - if (branch.getParentCommitId() != null) { - throw new BadRequestException("Internal Error: Invalid branch creation logic."); + Optional parentRefOption = branchPersistence.findById(projectId, parentRefId); + if(!parentRefOption.isPresent()) { + throw new BadRequestException("Parent branch cannot be determined"); } - Optional refOption = branchRepository.findByBranchId(b.getParentRefId()); - if (refOption.isPresent()) { - Optional parentCommit = commitRepository.findLatestByRef(refOption.get()); - parentCommit.ifPresent(parent -> { - b.setParentCommit(parent.getId()); - branch.setParentCommitId(parent.getCommitId()); //commit id is same as its docId - }); + //Find parent commit + // AND the commit federated are expecting the branches to be in PG + Optional parentCommit; + if(branch.getParentCommitId() != null) { + parentCommit = commitPersistence.findById(projectId, branch.getParentCommitId()); + } else { + parentCommit = commitPersistence.findLatestByProjectAndRef(projectId, parentRefId); + parentCommit.ifPresent(parent -> + branch.setParentCommitId(parent.getId()) + ); } - - if (b.getParentCommit() == null) { - throw new BadRequestException("Parent branch or commit cannot be determined"); - //creating more branches are not allowed until there's at least 1 commit, same as git + if(!parentCommit.isPresent()) { + throw new BadRequestException("Parent commit cannot be determined"); } + //Do branch creation try { - branchIndex.update(branch); - branchRepository.save(b); - Set docIds = new HashSet<>(); - for (Node n: nodeRepository.findAllByDeleted(false)) { - docIds.add(n.getDocId()); - } - try { nodeIndex.addToRef(docIds); } catch(Exception e) {} - eventPublisher.forEach((pub) -> pub.publish( - EventObject.create(projectId, branch.getId(), "branch_created", branch))); + RefJson committedBranch = branchPersistence.save(branch); + nodePersistence.branchElements(parentRefOption.get(), parentCommit.get(), committedBranch); + //TODO transaction commit + eventPublisher.forEach(pub -> pub.publish( + EventObject.create(projectId, committedBranch.getId(), "branch_created", committedBranch))); return branch; } catch (Exception e) { + //TODO transaction rollback logger.error("Couldn't create branch: {}", branch.getId(), e); - //TODO should clean up any created tables/rows? throw new InternalErrorException(e); } } + @Override + public RefJson updateBranch(String projectId, RefJson branch) { + if (projectId != null && branch != null) { + Optional refJson = branchPersistence.findById(projectId, branch.getId()); + if (!refJson.isPresent()) { + throw new BadRequestException("Branch: " + branch.getId() + " Not found for project: " + projectId); + } + if (refJson.get().isDeleted()) { + //un-delete action + branch.setDeleted(false); + } + } + return branchPersistence.update(branch); + } + public RefsResponse deleteBranch(String projectId, String id) { - ContextHolder.setContext(projectId); RefsResponse branchesResponse = new RefsResponse(); - if ("master".equals(id)) { + if (Constants.MASTER_BRANCH.equals(id)) { throw new BadRequestException(branchesResponse.addMessage("Cannot delete master")); } - Optional branch = this.branchRepository.findByBranchId(id); + Optional branch = branchPersistence.deleteById(projectId, id); if (!branch.isPresent()) { throw new NotFoundException(branchesResponse); } - Branch b = branch.get(); - b.setDeleted(true); - branchRepository.save(b); - List refs = new ArrayList<>(); - RefJson refJson = new RefJson().setDocId(b.getDocId()).setDeleted(true) - .setProjectId(projectId).setId(id); - refs.add(branchIndex.update(refJson)); - branchesResponse.setRefs(refs); + branchesResponse.setRefs(branch.map(List::of).orElseGet(List::of)); return branchesResponse; } } diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java index 6479e146b..4de1a422c 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java @@ -2,54 +2,37 @@ import java.text.ParseException; import java.time.Instant; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; -import org.openmbee.mms.core.config.ContextHolder; import org.openmbee.mms.core.config.Formats; import org.openmbee.mms.core.exceptions.BadRequestException; -import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.core.exceptions.NotFoundException; -import org.openmbee.mms.core.dao.BranchDAO; +import org.openmbee.mms.core.dao.*; import org.openmbee.mms.core.services.CommitService; -import org.openmbee.mms.data.domains.scoped.Branch; import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.RefJson; import org.openmbee.mms.core.objects.CommitsRequest; import org.openmbee.mms.core.objects.CommitsResponse; -import org.openmbee.mms.data.domains.scoped.Commit; -import org.openmbee.mms.core.dao.CommitDAO; -import org.openmbee.mms.core.dao.CommitIndexDAO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("defaultCommitService") public class DefaultCommitService implements CommitService { - protected CommitDAO commitRepository; - private CommitIndexDAO commitIndex; - private BranchDAO branchRepository; + protected CommitPersistence commitPersistence; + protected BranchPersistence branchPersistence; @Autowired - public void setCommitDao(CommitDAO commitDao) { - this.commitRepository = commitDao; + public void setCommitPersistence(CommitPersistence commitPersistence) { + this.commitPersistence = commitPersistence; } @Autowired - public void setBranchRepository(BranchDAO branchRepository) { - this.branchRepository = branchRepository; - } - - @Autowired - public void setCommitElasticDao(CommitIndexDAO commitElasticRepository) { - this.commitIndex = commitElasticRepository; + public void setBranchPersistence(BranchPersistence branchPersistence) { + this.branchPersistence = branchPersistence; } @Override public CommitsResponse getRefCommits(String projectId, String refId, Map params) { - ContextHolder.setContext(projectId, refId); int limit = 0; Instant timestamp = null; if (params.containsKey("limit")) { @@ -57,14 +40,14 @@ public CommitsResponse getRefCommits(String projectId, String refId, Map ref = branchRepository.findByBranchId(refId); + Optional ref = branchPersistence.findById(projectId, refId); List resJson = new ArrayList<>(); CommitsResponse res = new CommitsResponse(); @@ -72,17 +55,9 @@ public CommitsResponse getRefCommits(String projectId, String refId, Map { - List commits = commitRepository.findByRefAndTimestampAndLimit(branch, finalTimestamp, finalLimit); - for (Commit c : commits) { - CommitJson json = new CommitJson(); - json.setCreated(Formats.FORMATTER.format(c.getTimestamp())); - json.setCreator(c.getCreator()); - json.setId(c.getCommitId()); - json.setComment(c.getComment()); - json.setRefId(c.getBranchId()); - json.setProjectId(projectId); - resJson.add(json); - } + //branch for case that finalTimestamp is null AND/OR finalLimit is 0 + List commits = commitPersistence.findByProjectAndRefAndTimestampAndLimit(projectId, refId, finalTimestamp, finalLimit); + resJson.addAll(commits); }, () -> { throw new NotFoundException("Branch not found"); }); @@ -93,56 +68,50 @@ public CommitsResponse getRefCommits(String projectId, String refId, Map commit = commitIndex.findById(commitId); - if (commit.isPresent()) { - res.getCommits().add(commit.get()); - } else { + Optional commit = commitPersistence.findById(projectId, commitId); + if (!commit.isPresent()) { throw new NotFoundException("Commit not found"); } + res.getCommits().add(commit.get()); return res; } @Override public CommitsResponse getElementCommits(String projectId, String refId, String elementId, Map params) { - ContextHolder.setContext(projectId); CommitsResponse res = new CommitsResponse(); - Optional ref = branchRepository.findByBranchId(refId); + Optional ref = branchPersistence.findById(projectId, refId); if (!ref.isPresent()) { throw new NotFoundException("Branch not found"); } - List refCommits = commitRepository.findByRefAndTimestampAndLimit(ref.get(), null, 0); - Set commitIds = new HashSet<>(); - for (Commit commit: refCommits) { - commitIds.add(commit.getCommitId()); + List refCommits = commitPersistence.findByProjectAndRefAndTimestampAndLimit(projectId, refId, null, 0); + Set commitIds = new LinkedHashSet<>(); + for (CommitJson commit: refCommits) { + commitIds.add(commit.getId()); } - res.getCommits().addAll(commitIndex.elementHistory(elementId, commitIds)); + res.getCommits().addAll(commitPersistence.elementHistory(projectId, elementId, commitIds)); return res; } @Override public CommitsResponse getCommits(String projectId, CommitsRequest req) { - ContextHolder.setContext(projectId); Set ids = new HashSet<>(); for (CommitJson j : req.getCommits()) { ids.add(j.getId()); } CommitsResponse res = new CommitsResponse(); - try { - res.getCommits().addAll(commitIndex.findAllById(ids)); - } catch (Exception e) { - e.printStackTrace(); - throw new InternalErrorException(e); + List commitJsonList = commitPersistence.findAllById(projectId, ids); + if (commitJsonList.isEmpty()) { + throw new NotFoundException("Commit not found"); } + + res.getCommits().addAll(commitJsonList); return res; } @Override public boolean isProjectNew(String projectId) { - ContextHolder.setContext(projectId); - List commits = commitRepository.findAll(); + List commits = commitPersistence.findAllByProjectId(projectId); return commits == null || commits.isEmpty(); } } diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java index 6beb8ae60..5a5cf300d 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java @@ -1,96 +1,53 @@ package org.openmbee.mms.crud.services; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.*; - -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; +import org.openmbee.mms.core.dao.*; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.core.objects.ElementsCommitResponse; +import org.openmbee.mms.core.objects.ElementsRequest; +import org.openmbee.mms.core.objects.ElementsResponse; import org.openmbee.mms.core.objects.EventObject; import org.openmbee.mms.core.services.EventService; import org.openmbee.mms.core.services.NodeChangeInfo; import org.openmbee.mms.core.services.NodeGetInfo; import org.openmbee.mms.core.services.NodeService; -import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.objects.ElementsRequest; -import org.openmbee.mms.core.objects.ElementsResponse; -import org.openmbee.mms.data.domains.scoped.Commit; -import org.openmbee.mms.data.domains.scoped.CommitType; -import org.openmbee.mms.data.domains.scoped.Node; -import org.openmbee.mms.core.dao.CommitDAO; -import org.openmbee.mms.core.dao.CommitIndexDAO; -import org.openmbee.mms.core.dao.NodeDAO; -import org.openmbee.mms.core.dao.NodeIndexDAO; +import org.openmbee.mms.crud.CrudConstants; +import org.openmbee.mms.json.BaseJson; import org.openmbee.mms.json.CommitJson; import org.openmbee.mms.json.ElementJson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -@Service("defaultNodeService") -public class DefaultNodeService implements NodeService { +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; - @Value("${mms.stream.batch.size:100000}") - protected int streamLimit; - protected final ObjectMapper objectMapper = new ObjectMapper(); +@Service("defaultNodeService") +public class DefaultNodeService implements NodeService { protected final Logger logger = LoggerFactory.getLogger(getClass()); - protected NodeDAO nodeRepository; - protected CommitDAO commitRepository; - protected NodeIndexDAO nodeIndex; - //to save to this use base json classes - protected CommitIndexDAO commitIndex; - protected NodeGetHelper nodeGetHelper; - protected NodePostHelper nodePostHelper; - protected NodeDeleteHelper nodeDeleteHelper; + protected CommitPersistence commitPersistence; + protected NodePersistence nodePersistence; protected Collection eventPublisher; @Autowired - public void setNodeRepository(NodeDAO nodeRepository) { - this.nodeRepository = nodeRepository; + public void setCommitPersistence(CommitPersistence commitPersistence) { + this.commitPersistence = commitPersistence; } @Autowired - public void setCommitRepository(CommitDAO commitRepository) { - this.commitRepository = commitRepository; + public void setNodePersistence(NodePersistence nodePersistence) { + this.nodePersistence = nodePersistence; } - @Autowired - public void setNodeIndex(NodeIndexDAO nodeIndex) { - this.nodeIndex = nodeIndex; - } - - @Autowired - public void setCommitIndex(CommitIndexDAO commitIndex) { - this.commitIndex = commitIndex; - } - - @Autowired - public void setNodePostHelper(NodePostHelper nodePostHelper) { - this.nodePostHelper = nodePostHelper; - } - - @Autowired - public void setNodeDeleteHelper(NodeDeleteHelper nodeDeleteHelper) { - this.nodeDeleteHelper = nodeDeleteHelper; - } - - @Autowired - public void setNodeGetHelper(NodeGetHelper nodeGetHelper) { - this.nodeGetHelper = nodeGetHelper; + public NodePersistence getNodePersistence() { + return nodePersistence; } @Autowired @@ -99,187 +56,145 @@ public void setEventPublisher(Collection eventPublisher) { } @Override - public void readAsStream(String projectId, String refId, - Map params, OutputStream stream, String accept) throws IOException { + public void readAsStream(String projectId, String refId, Map params, OutputStream stream, + String accept) throws IOException { + + String commitId = params.getOrDefault(CrudConstants.COMMITID, null); - String commitId = params.getOrDefault("commitId", null); - ContextHolder.setContext(projectId, refId); - List nodes; - String resCommitId; if (commitId != null && !commitId.isEmpty()) { - if (!commitRepository.findByCommitId(commitId).isPresent()) { + if (!commitPersistence.findById(projectId, commitId).isPresent()) { throw new BadRequestException("commit id is invalid"); } - nodes = nodeRepository.findAll(); - resCommitId = commitId; } else { - nodes = nodeRepository.findAllByDeleted(false); - resCommitId = nodeGetHelper.getLatestRefCommitId(); + Optional commitJson = commitPersistence.findLatestByProjectAndRef(projectId, refId); + if (!commitJson.isPresent()) { + throw new InternalErrorException("Could not find latest commit for project and ref"); + } + commitId = commitJson.get().getId(); } + String separator = "\n"; if (!"application/x-ndjson".equals(accept)) { - String intro = "{\"commitId\":" + (resCommitId == null ? "null" : "\"" + resCommitId + "\"") + ",\"elements\":["; - stream.write(intro.getBytes(StandardCharsets.UTF_8)); + stream.write("{\"elements\":[".getBytes(StandardCharsets.UTF_8)); separator = ","; } - final String sep = separator; - AtomicInteger counter = new AtomicInteger(); - batches(nodes, streamLimit).forEach(ns -> { - try { - if (counter.get() == 0) { - counter.getAndIncrement(); - } else { - stream.write(sep.getBytes(StandardCharsets.UTF_8)); - } - Collection result = nodeGetHelper.processGetJsonFromNodes(ns, commitId, this) - .getActiveElementMap().values(); - stream.write(result.stream().map(this::toJson).collect(Collectors.joining(sep)) - .getBytes(StandardCharsets.UTF_8)); - } catch (IOException ioe) { - logger.error("Error writing to stream", ioe); - throw new InternalErrorException("Error writing to stream."); - } - }); + + nodePersistence.streamAllAtCommit(projectId, refId, commitId, stream, separator); + if (!"application/x-ndjson".equals(accept)) { stream.write("]}".getBytes(StandardCharsets.UTF_8)); } else { stream.write("\n".getBytes(StandardCharsets.UTF_8)); } - stream.close(); } @Override public ElementsResponse read(String projectId, String refId, String id, Map params) { + String commitId = params.getOrDefault(CrudConstants.COMMITID, null); + if (commitId == null) { + Optional commitJson = commitPersistence.findLatestByProjectAndRef(projectId, refId); + if (!commitJson.isPresent()) { + throw new InternalErrorException("Could not find latest commit for project and ref"); + } + commitId = commitJson.get().getId(); + } + + ElementsResponse response = new ElementsResponse(); if (id != null && !id.isEmpty()) { logger.debug("ElementId given: {}", id); - ElementsRequest req = buildRequest(id); - return read(projectId, refId, req, params); + NodeGetInfo getInfo = nodePersistence.findById(projectId, refId, commitId, id); + if(!getInfo.getRejected().isEmpty()){ + response.addRejection(getInfo.getRejected().get(id)); + }else{ + response.getElements().add(getInfo.getActiveElementMap().get(id)); + } } else { // If no id is provided, return all logger.debug("No ElementId given"); - ContextHolder.setContext(projectId, refId); - - ElementsResponse response = new ElementsResponse(); - String commitId = params.getOrDefault("commitId", null); - response.getElements().addAll(nodeGetHelper.processGetAll(commitId, this)); - if (commitId != null) { - response.setCommitId(commitId); - } else { - response.setCommitId(nodeGetHelper.getLatestRefCommitId()); - } - return response; + + List nodes = nodePersistence.findAll(projectId, refId, commitId); + response.getElements().addAll(nodes); } + return response; } @Override public ElementsResponse read(String projectId, String refId, ElementsRequest req, Map params) { - String commitId = params.getOrDefault("commitId", null); - ContextHolder.setContext(projectId, refId); - - NodeGetInfo info = nodeGetHelper.processGetJson(req.getElements(), commitId, this); + String commitId = params.getOrDefault(CrudConstants.COMMITID, null); + NodeGetInfo info = nodePersistence.findAll(projectId, refId, commitId, req.getElements()); ElementsResponse response = new ElementsResponse(); response.getElements().addAll(info.getActiveElementMap().values()); response.setRejected(new ArrayList<>(info.getRejected().values())); - response.setCommitId(info.getCommitId()); return response; } @Override public ElementsCommitResponse createOrUpdate(String projectId, String refId, ElementsRequest req, - Map params, String user) { - - ContextHolder.setContext(projectId, refId); - boolean overwriteJson = Boolean.parseBoolean(params.get("overwrite")); - nodePostHelper.setPreserveTimestamps(Boolean.parseBoolean(params.get("preserveTimestamps"))); - String commitId = params.get("commitId"); - String lastCommitId = req.getLastCommitId(); - - NodeChangeInfo info = nodePostHelper - .processPostJson(req.getElements(), overwriteJson, - createCommit(user, refId, projectId, req, commitId), this, lastCommitId); - - if (req.getDeletes() != null && !req.getDeletes().isEmpty()) { - NodeChangeInfo delete = nodeDeleteHelper.processDeleteJson(req.getDeletes(), createCommit(user, refId, projectId, req, info.getCommitJson().getCommitId()), this); - info.getCommitJson().getDeleted().addAll(delete.getCommitJson().getDeleted()); - info.getDeletedMap().putAll(delete.getDeletedMap()); - info.getToSaveNodeMap().putAll(delete.getToSaveNodeMap()); - info.getOldDocIds().addAll(delete.getOldDocIds()); - info.getRejected().putAll(delete.getRejected()); + Map params, String user) { + + boolean overwriteJson = Boolean.parseBoolean(params.get(CrudConstants.OVERWRITE)); + boolean preserveTimestamps = Boolean.parseBoolean(params.get(CrudConstants.PRESERVETIMESTAMPS)); + String commitId = params.getOrDefault(CrudConstants.COMMITID, null); + + NodeChangeInfo changes = nodePersistence.prepareChange(createCommit(user, refId, projectId, commitId, req), + overwriteJson, preserveTimestamps); + changes = nodePersistence.prepareAddsUpdates(changes, req.getElements()); + + for(ElementJson element : changes.getUpdatedMap().values()) { + extraProcessPostedElement(changes, element); } - commitChanges(info); + changes = nodePersistence.commitChanges(changes); + CommitJson commitJson = changes.getCommitJson(); + eventPublisher.forEach(pub -> pub.publish( + EventObject.create(commitJson.getProjectId(), commitJson.getRefId(), CrudConstants.COMMIT_SC, commitJson))); ElementsCommitResponse response = new ElementsCommitResponse(); - response.getElements().addAll(info.getUpdatedMap().values()); - response.setRejected(new ArrayList<>(info.getRejected().values())); - response.setDeleted(new ArrayList<>(info.getDeletedMap().values())); - if(!info.getUpdatedMap().isEmpty() || !info.getDeletedMap().isEmpty()) { - response.setCommitId(info.getCommitJson().getId()); + response.getElements().addAll(changes.getUpdatedMap().values()); + response.setRejected(new ArrayList<>(changes.getRejected().values())); + if(!changes.getUpdatedMap().isEmpty()) { + response.setCommitId(changes.getCommitJson().getId()); } return response; } - public void commitChanges(NodeChangeInfo info) { - Map nodes = info.getToSaveNodeMap(); - Map json = info.getUpdatedMap(); - CommitJson cmjs = info.getCommitJson(); - Instant now = info.getNow(); - if (!nodes.isEmpty()) { - try { - if (json != null && !json.isEmpty()) { - this.nodeIndex.indexAll(json.values()); - } - try { this.nodeIndex.removeFromRef(info.getOldDocIds()); } catch(Exception e) {} - this.commitIndex.index(cmjs); - - Optional existing = this.commitRepository.findByCommitId(cmjs.getId()); - existing.ifPresentOrElse( - current -> { - this.logger.debug(String.format("Commit object %s already exists. Skipping record creation.", current.getCommitId())); - }, - () -> { - Commit commit = new Commit(); - commit.setCommitId(cmjs.getId()); - commit.setBranchId(cmjs.getRefId()); - commit.setCommitType(CommitType.COMMIT); - commit.setCreator(cmjs.getCreator()); - commit.setTimestamp(now); - commit.setComment(cmjs.getComment()); - this.commitRepository.save(commit); - }); - this.nodeRepository.saveAll(new ArrayList<>(nodes.values())); - } catch (Exception e) { - logger.error("Error in commitChanges: ", e); - throw new InternalErrorException("Error committing changes: " + e.getMessage()); - } - eventPublisher.forEach((pub) -> pub.publish( - EventObject.create(cmjs.getProjectId(), cmjs.getRefId(), "commit", cmjs))); + + //Info is used in one of the child classes + public void extraProcessPostedElement(NodeChangeInfo info, ElementJson element) { + if (element.getType() == null || element.getType().isEmpty()) { + element.setType(CrudConstants.NODE); } } @Override - public void extraProcessPostedElement(ElementJson element, Node node, NodeChangeInfo info) { + public ElementsCommitResponse delete(String projectId, String refId, String id, String user) { + ElementsRequest req = buildRequest(id); + return delete(projectId, refId, req, user); } @Override - public void extraProcessDeletedElement(ElementJson element, Node node, NodeChangeInfo info) { - } + public ElementsCommitResponse delete(String projectId, String refId, ElementsRequest req, String user) { + NodeChangeInfo changes = nodePersistence.prepareChange(createCommit(user, refId, projectId, null, req), + false, false); + changes = nodePersistence.prepareDeletes(changes, req.getElements()); - @Override - public void extraProcessGotElement(ElementJson element, Node node, NodeGetInfo info) { - } - @Override - public ElementsCommitResponse delete(String projectId, String refId, String id, String user) { - ElementsRequest req = buildRequest(id); - return delete(projectId, refId, req, user); + changes = nodePersistence.commitChanges(changes); + commitPersistence.save(changes.getCommitJson(), changes.getInstant()); + ElementsCommitResponse response = new ElementsCommitResponse(); + response.getElements().addAll(changes.getDeletedMap().values()); + response.setRejected(new ArrayList<>(changes.getRejected().values())); + if(!changes.getDeletedMap().isEmpty()) { + response.setCommitId(changes.getCommitJson().getId()); + } + return response; } protected ElementsRequest buildRequest(String id) { @@ -300,52 +215,45 @@ protected ElementsRequest buildRequest(Collection ids) { return req; } - @Override - public ElementsCommitResponse delete(String projectId, String refId, ElementsRequest req, String user) { - ContextHolder.setContext(projectId, refId); - - NodeChangeInfo info = nodeDeleteHelper - .processDeleteJson(req.getElements(), createCommit(user, refId, projectId, req, null), - this); - ElementsCommitResponse response = new ElementsCommitResponse(); - - commitChanges(info); - - response.getElements().addAll(info.getDeletedMap().values()); - response.setRejected(new ArrayList<>(info.getRejected().values())); - if(!info.getDeletedMap().isEmpty()) { - response.setCommitId(info.getCommitJson().getId()); - } - return response; + protected ElementsRequest buildRequestFromJsons(Collection jsons) { + return buildRequest(jsons.stream().map(BaseJson::getId).collect(Collectors.toList())); } - private CommitJson createCommit(String creator, String refId, String projectId, - ElementsRequest req, String commitId) { + private CommitJson createCommit(String creator, String refId, String projectId, String commitId, ElementsRequest req) { CommitJson cmjs = new CommitJson(); cmjs.setCreator(creator); cmjs.setComment(req.getComment()); cmjs.setSource(req.getSource()); cmjs.setRefId(refId); cmjs.setProjectId(projectId); - - if (commitId != null && !commitId.isEmpty()) { - cmjs.setId(commitId); + if(commitId != null) { + cmjs.setCommitId(commitId); } - return cmjs; } - protected static Stream> batches(List source, int length) { - return IntStream.iterate(0, i -> i < source.size(), i -> i + length) - .mapToObj(i -> source.subList(i, Math.min(i + length, source.size()))); + protected List filter(List ids, List orig) { + Map map = convertJsonToMap(orig); + List ret = new ArrayList<>(); + for (String id: ids) { + if (map.containsKey(id)) { + ret.add(map.get(id)); + } + } + return ret; } - protected String toJson(ElementJson elementJson) { - try { - return objectMapper.writeValueAsString(elementJson); - } catch (JsonProcessingException e) { - logger.error("Error in toJson: ", e); + protected Map convertJsonToMap(List elements) { + Map result = new HashMap<>(); + for (ElementJson elem : elements) { + if (elem == null) { + continue; + } + if (elem.getId() != null && !elem.getId().isBlank()) { + result.put(elem.getId(), elem); + } } - return ""; + return result; } + } diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultProjectService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultProjectService.java index 2c633a97b..bfbca9148 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultProjectService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultProjectService.java @@ -1,70 +1,48 @@ package org.openmbee.mms.crud.services; -import java.util.Collection; - import org.openmbee.mms.core.config.Constants; -import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.config.Formats; -import org.openmbee.mms.core.dao.BranchDAO; -import org.openmbee.mms.core.dao.BranchIndexDAO; -import org.openmbee.mms.core.dao.OrgDAO; -import org.openmbee.mms.core.dao.ProjectDAO; -import org.openmbee.mms.core.dao.ProjectIndex; +import org.openmbee.mms.core.dao.BranchPersistence; +import org.openmbee.mms.core.dao.OrgPersistence; +import org.openmbee.mms.core.dao.ProjectPersistence; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.core.objects.EventObject; import org.openmbee.mms.core.objects.ProjectsResponse; import org.openmbee.mms.core.services.EventService; import org.openmbee.mms.core.services.ProjectService; -import org.openmbee.mms.data.domains.global.Organization; -import org.openmbee.mms.data.domains.global.Project; -import org.openmbee.mms.data.domains.scoped.Branch; -import org.openmbee.mms.json.ProjectJson; -import org.openmbee.mms.json.RefJson; -import org.openmbee.mms.json.RefType; +import org.openmbee.mms.json.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; -import java.time.Instant; +import java.util.Collection; import java.util.Optional; -import java.util.UUID; @Service("defaultProjectService") +@Primary public class DefaultProjectService implements ProjectService { protected final Logger logger = LoggerFactory.getLogger(getClass()); - protected ProjectDAO projectRepository; - protected OrgDAO orgRepository; - protected ProjectIndex projectIndex; - protected BranchDAO branchRepository; - protected BranchIndexDAO branchIndex; + protected ProjectPersistence projectPersistence; + protected OrgPersistence orgPersistence; + protected BranchPersistence branchPersistence; protected Collection eventPublisher; @Autowired - public void setProjectRepository(ProjectDAO projectRepository) { - this.projectRepository = projectRepository; - } - - @Autowired - public void setOrganizationRepository(OrgDAO orgRepository) { - this.orgRepository = orgRepository; + public void setProjectPersistence(ProjectPersistence projectPersistence) { + this.projectPersistence = projectPersistence; } @Autowired - public void setProjectIndex(ProjectIndex projectIndex) { - this.projectIndex = projectIndex; + public void setOrgPersistence(OrgPersistence orgDAO) { + this.orgPersistence = orgDAO; } @Autowired - public void setBranchRepository(BranchDAO branchRepository) { - this.branchRepository = branchRepository; - } - - @Autowired - public void setBranchIndex(BranchIndexDAO branchIndex) { - this.branchIndex = branchIndex; + public void setBranchPersistence(BranchPersistence branchPersistence) { + this.branchPersistence = branchPersistence; } @Autowired @@ -77,83 +55,42 @@ public ProjectJson create(ProjectJson project) { throw new BadRequestException("Organization ID not provided"); } - Optional org = orgRepository.findByOrganizationId(project.getOrgId()); - if (!org.isPresent() || org.get().getOrganizationId().isEmpty()) { + Optional org = orgPersistence.findById(project.getOrgId()); + if (org.isEmpty() || org.get().getId().isEmpty()) { throw new BadRequestException("Organization not found"); } - Project proj = new Project(); - proj.setProjectId(project.getId()); - proj.setProjectName(project.getName()); - proj.setOrganization(org.get()); - proj.setProjectType(project.getProjectType()); - - String uuid = UUID.randomUUID().toString(); - proj.setDocId(uuid); - project.setDocId(uuid); - project.setCreated(Formats.FORMATTER.format(Instant.now())); - project.setType("Project"); - try { - projectRepository.save(proj); - projectIndex.create(project); - - Optional masterBranch = branchRepository.findByBranchId(Constants.MASTER_BRANCH); - if (masterBranch.isPresent()) { - String docId = UUID.randomUUID().toString(); - Branch master = masterBranch.get(); - master.setDocId(docId); - master.setParentCommit(0L); + //TODO Transaction start + ProjectJson savedProjectJson = projectPersistence.save(project); - branchRepository.save(master); + //create and save master branch. We're combining operations with the branch unifiedDAO. + branchPersistence.save(createMasterRefJson(savedProjectJson)); + //TODO Transaction commit - RefJson branchJson = createRefJson(project, docId); - branchIndex.index(branchJson); - } - eventPublisher.forEach((pub) -> pub.publish( - EventObject.create(project.getId(), "master", "project_created", project))); - return project; + eventPublisher.forEach(pub -> pub.publish( + EventObject.create(savedProjectJson.getId(), Constants.MASTER_BRANCH, "project_created", savedProjectJson))); + return savedProjectJson; } catch (Exception e) { logger.error("Couldn't create project: {}", project.getProjectId(), e); + //Need to clean up in case of partial creation + projectPersistence.hardDelete(project.getProjectId()); + //TODO Transaction rollback (could include project delete in rollback) } throw new InternalErrorException("Could not create project"); } - public RefJson createRefJson(ProjectJson project, String docId){ - RefJson branchJson = new RefJson(); - branchJson.setId(Constants.MASTER_BRANCH); - branchJson.setName(Constants.MASTER_BRANCH); - branchJson.setParentRefId(null); - branchJson.setDocId(docId); - branchJson.setRefType(RefType.Branch); - branchJson.setCreated(project.getCreated()); - branchJson.setProjectId(project.getId()); - branchJson.setCreator(project.getCreator()); - branchJson.setDeleted(false); - return branchJson; - } - public ProjectJson update(ProjectJson project) { - Optional projOption = projectRepository.findByProjectId(project.getProjectId()); - if (projOption.isPresent()) { - ContextHolder.setContext(project.getProjectId()); - Project proj = projOption.get(); - if (project.getName() != null && !project.getName().isEmpty()) { - proj.setProjectName(project.getName()); + if (project.getOrgId() != null && !project.getOrgId().isEmpty()) { + Optional org = orgPersistence.findById(project.getOrgId()); + if (org.isPresent() && !org.get().getId().isEmpty()) { + project.setOrgId(org.get().getId()); + } else { + throw new BadRequestException("Invalid organization"); } - if (project.getOrgId() != null && !project.getOrgId().isEmpty()) { - Optional org = orgRepository.findByOrganizationId(project.getOrgId()); - if (org.isPresent() && !org.get().getOrganizationId().isEmpty()) { - proj.setOrganization(org.get()); - } else { - throw new BadRequestException("Invalid organization"); - } - } - project.setDocId(proj.getDocId()); - projectRepository.save(proj); - return projectIndex.update(project); } - throw new InternalErrorException("Could not update project"); + + return projectPersistence.update(project); } public ProjectsResponse read(String projectId) { @@ -161,7 +98,21 @@ public ProjectsResponse read(String projectId) { } public boolean exists(String projectId) { - Optional project = this.projectRepository.findByProjectId(projectId); + Optional project = this.projectPersistence.findById(projectId); return project.isPresent() && project.get().getProjectId().equals(projectId); } + + + public RefJson createMasterRefJson(ProjectJson project) { + RefJson branchJson = new RefJson(); + branchJson.setId(Constants.MASTER_BRANCH); + branchJson.setName(Constants.MASTER_BRANCH); + branchJson.setParentRefId(null); + branchJson.setRefType(RefType.Branch); + branchJson.setCreated(project.getCreated()); + branchJson.setProjectId(project.getId()); + branchJson.setCreator(project.getCreator()); + branchJson.setDeleted(false); + return branchJson; + } } diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/ElementUtilsFactory.java b/crud/src/main/java/org/openmbee/mms/crud/services/ElementUtilsFactory.java new file mode 100644 index 000000000..d3c566f53 --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/services/ElementUtilsFactory.java @@ -0,0 +1,27 @@ +package org.openmbee.mms.crud.services; + +import org.openmbee.mms.core.utils.ElementUtils; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +@Component +public class ElementUtilsFactory implements ApplicationContextAware { + private ApplicationContext context; + + public void setApplicationContext(ApplicationContext context) { + this.context = context; + } + + public ElementUtils getElementUtil(String type) { + try { + ElementUtils eu = context.getBean(type + "Helper", ElementUtils.class); + if (eu != null) { + return eu; + } + } catch (BeansException e) { + } + return null; + } +} \ No newline at end of file diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/NodeDeleteHelper.java b/crud/src/main/java/org/openmbee/mms/crud/services/NodeDeleteHelper.java deleted file mode 100644 index 98caddfc7..000000000 --- a/crud/src/main/java/org/openmbee/mms/crud/services/NodeDeleteHelper.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.openmbee.mms.crud.services; - -import java.util.List; -import java.util.Map; - -import org.openmbee.mms.core.objects.Rejection; -import org.openmbee.mms.core.services.NodeChangeInfo; -import org.openmbee.mms.core.services.NodeService; -import org.openmbee.mms.data.domains.scoped.Node; -import org.openmbee.mms.json.CommitJson; -import org.openmbee.mms.json.ElementJson; -import org.springframework.stereotype.Service; - -@Service -public class NodeDeleteHelper extends NodeOperation { - - public NodeChangeInfo processDeleteJson(List elements, CommitJson cmjs, NodeService service) { - NodeChangeInfo info = initInfo(elements, cmjs); - - for (String nodeId : info.getReqElementMap().keySet()) { - if (!existingNodeContainsNodeId(info, nodeId)) { - continue; - } - Node node = info.getExistingNodeMap().get(nodeId); - Map indexElement = info.getExistingElementMap().get(nodeId); - if (node.isDeleted()) { - info.addRejection(nodeId, new Rejection(indexElement, 304, "Already deleted")); - continue; - } - if (indexElement == null) { - logger.warn("node db and index mismatch on element delete: nodeId: " + nodeId + - ", docId not found: " + info.getExistingNodeMap().get(nodeId).getDocId()); - indexElement = Map.of("id", nodeId); - } - ElementJson request = info.getReqElementMap().get(nodeId); - request.putAll(indexElement); - processElementDeleted(request, node, info); - service.extraProcessDeletedElement(request, node, info); - info.getDeletedMap().put(nodeId, request); - } - return info; - } -} diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/NodeGetHelper.java b/crud/src/main/java/org/openmbee/mms/crud/services/NodeGetHelper.java deleted file mode 100644 index 61faa7226..000000000 --- a/crud/src/main/java/org/openmbee/mms/crud/services/NodeGetHelper.java +++ /dev/null @@ -1,209 +0,0 @@ -package org.openmbee.mms.crud.services; - -import java.time.Instant; -import java.util.*; - -import java.util.stream.Collectors; - -import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.objects.Rejection; -import org.openmbee.mms.core.services.NodeGetInfo; -import org.openmbee.mms.core.exceptions.BadRequestException; -import org.openmbee.mms.core.services.NodeService; -import org.openmbee.mms.data.domains.scoped.Branch; -import org.openmbee.mms.data.domains.scoped.Commit; -import org.openmbee.mms.data.domains.scoped.Node; -import org.openmbee.mms.json.ElementJson; -import org.springframework.stereotype.Service; - -import static org.openmbee.mms.core.config.ContextHolder.getContext; - -@Service -public class NodeGetHelper extends NodeOperation { - - public NodeGetInfo processGetJsonFromNodes(List nodes, NodeService service) { - NodeGetInfo info = initInfoFromNodes(nodes, null); - return processLatest(info, service); - } - - public NodeGetInfo processGetJson(List elements, NodeService service) { - NodeGetInfo info = initInfo(elements, null); - return processLatest(info, service); - } - - private NodeGetInfo processLatest(NodeGetInfo info, NodeService service) { - for (String nodeId : info.getReqElementMap().keySet()) { - if (!existingNodeContainsNodeId(info, nodeId)) { - continue; - } - ElementJson indexElement = info.getExistingElementMap().get(nodeId); - if (info.getExistingNodeMap().get(nodeId).isDeleted()) { - rejectDeleted(info, nodeId, indexElement == null ? new ElementJson().setId(nodeId) : indexElement); - continue; - } - if (indexElement == null) { - logger.warn("node db and index mismatch on element get: nodeId: " + nodeId + - ", docId not found: " + info.getExistingNodeMap().get(nodeId).getDocId()); - rejectNotFound(info, nodeId); - continue; - } - if (service != null) { - service.extraProcessGotElement(indexElement, info.getExistingNodeMap().get(nodeId), info); - } - info.getActiveElementMap().put(nodeId, indexElement); - } - info.setCommitId(getLatestRefCommitId()); - return info; - } - - public NodeGetInfo processGetJsonFromNodes(List nodes, String commitId, NodeService service) { - if (commitId == null || commitId.isEmpty()) { - return processGetJsonFromNodes(nodes, service); - } - NodeGetInfo info = initInfoFromNodes(nodes, null); - return processCommit(info, commitId, service); - } - - public NodeGetInfo processGetJson(List elements, String commitId, NodeService service) { - if (commitId == null || commitId.isEmpty()) { - return processGetJson(elements, service); - } - NodeGetInfo info = initInfo(elements, null); //gets all current nodes - return processCommit(info, commitId, service); - } - - private NodeGetInfo processCommit(NodeGetInfo info, String commitId, NodeService service) { - Optional commit = commitRepository.findByCommitId(commitId); - if (!commit.isPresent() ) { - throw new BadRequestException("commitId is invalid"); - } - info.setCommitId(commitId); - Instant time = commit.get().getTimestamp(); //time of commit - List refCommitIds = null; //get it later if needed - for (String nodeId : info.getReqElementMap().keySet()) { - if (!existingNodeContainsNodeId(info, nodeId)) { // nodeId not found - continue; - } - ElementJson indexElement = info.getExistingElementMap().get(nodeId); - if (indexElement == null) { - Node n = info.getExistingNodeMap().get(nodeId); - logger.warn("node db and index mismatch on element commit get: nodeId: " + nodeId + - ", docId not found: " + n.getDocId()); - Optional last = commitRepository.findByCommitId(n.getLastCommit()); - Optional first = commitRepository.findByCommitId(n.getInitialCommit()); - if (!last.isPresent() || !first.isPresent()) { - rejectNotFound(info, nodeId); - continue; - } - indexElement = new ElementJson().setId(nodeId).setDocId(n.getDocId()); - indexElement.setModified(formatter.format(last.get().getTimestamp())); - indexElement.setModifier(last.get().getCreator()); - indexElement.setCommitId(last.get().getCommitId()); - indexElement.setCreator(first.get().getCreator()); - indexElement.setCreated(formatter.format(first.get().getTimestamp())); - } - Instant modified = Instant.from(formatter.parse(indexElement.getModified())); - Instant created = Instant.from(formatter.parse(indexElement.getCreated())); - indexElement.setRefId(ContextHolder.getContext().getBranchId()); - if (commitId.equals(indexElement.getCommitId())) { //exact match - info.getActiveElementMap().put(nodeId, indexElement); - } else if (created.isAfter(time)) { // element created after commit - rejectNotFound(info, nodeId); - } else if (modified.isAfter(time)) { // latest element is after commit - Optional tryExact = nodeIndex.getByCommitId(commitId, nodeId); - if (tryExact.isPresent()) { - tryExact.get().setRefId(ContextHolder.getContext().getBranchId()); - info.getActiveElementMap().put(nodeId, tryExact.get()); - continue; // found exact match at commit - } - if (refCommitIds == null) { // need list of commitIds of current ref to filter on - refCommitIds = getRefCommitIds(time); - } - Optional e = nodeIndex.getElementLessThanOrEqualTimestamp(nodeId, - formatter.format(time), refCommitIds); - if (e.isPresent()) { // found version of element at commit time - //TODO determine if element was deleted at the time? - e.get().setRefId(ContextHolder.getContext().getBranchId()); - info.getActiveElementMap().put(nodeId, e.get()); - } else { - rejectNotFound(info, nodeId); // element not found at commit time - } - } else if (info.getExistingNodeMap().get(nodeId).isDeleted()) { // latest element is before commit, but deleted - rejectDeleted(info, nodeId, indexElement); - } else { // latest element version is version at commit, not deleted - info.getActiveElementMap().put(nodeId, indexElement); - } - } - return info; - } - - public NodeGetInfo processGetJson(List elements, Instant time, NodeService service) { - Optional ref = branchRepository.findByBranchId(getContext().getBranchId()); - if (ref.isPresent()) { - Optional c = commitRepository.findLatestByRef(ref.get()); - if (c.isPresent()) { - return processGetJson(elements, c.get().getCommitId(), service); - } else { - throw new BadRequestException("invalid time"); - } - } - return null; - } - - public List processGetAll() { - Set indexIds = new HashSet<>(); - List existingNodes = nodeRepository.findAllByDeleted(false); - for (Node node : existingNodes) { - indexIds.add(node.getDocId()); - } - List els = nodeIndex.findAllById(indexIds); - String ref = ContextHolder.getContext().getBranchId(); - for (ElementJson el: els) { - el.setRefId(ref); - } - return els; - } - - public List processGetAll(String commitId, NodeService service) { - if (commitId == null || commitId.isEmpty()) { - return processGetAll(); - } else { - List nodes = nodeRepository.findAll(); - List el = nodes.stream().map( - node -> new ElementJson().setId(node.getNodeId())).collect(Collectors.toList()); - NodeGetInfo info = processGetJson(el, commitId, service); - return new ArrayList<>(info.getActiveElementMap().values()); - } - } - - public List processGetAll(Instant time, NodeService service) { - List result = new ArrayList<>(); - Optional ref = branchRepository.findByBranchId(getContext().getBranchId()); - if (ref.isPresent()) { - Optional c = commitRepository.findByRefAndTimestamp(ref.get(), time); - if (c.isPresent()) { - result.addAll(processGetAll(c.get().getCommitId(), service)); - } else { - throw new BadRequestException("invalid time"); - } - } - return result; - } - - protected void rejectDeleted(NodeGetInfo info, String nodeId, ElementJson indexElement) { - info.addRejection(nodeId, new Rejection(indexElement, 410, "Element deleted")); - } - - protected List getRefCommitIds(Instant time) { - List commitIds = new ArrayList<>(); - - Optional ref = branchRepository.findByBranchId(getContext().getBranchId()); - ref.ifPresent(current -> { - List refCommits = commitRepository.findByRefAndTimestampAndLimit(current, time, 0); - for (Commit c : refCommits) { - commitIds.add(c.getCommitId()); - } - }); - return commitIds; - } -} \ No newline at end of file diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/NodeOperation.java b/crud/src/main/java/org/openmbee/mms/crud/services/NodeOperation.java deleted file mode 100644 index d6d465a4e..000000000 --- a/crud/src/main/java/org/openmbee/mms/crud/services/NodeOperation.java +++ /dev/null @@ -1,289 +0,0 @@ -package org.openmbee.mms.crud.services; - -import java.time.Instant; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; - -import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.objects.Rejection; -import org.openmbee.mms.core.services.NodeChangeInfo; -import org.openmbee.mms.core.services.NodeGetInfo; -import org.openmbee.mms.core.dao.BranchDAO; -import org.openmbee.mms.core.dao.CommitDAO; -import org.openmbee.mms.data.domains.scoped.Branch; -import org.openmbee.mms.data.domains.scoped.Commit; -import org.openmbee.mms.data.domains.scoped.Node; -import org.openmbee.mms.core.dao.NodeDAO; -import org.openmbee.mms.core.dao.NodeIndexDAO; -import org.openmbee.mms.json.CommitJson; -import org.openmbee.mms.json.ElementVersion; -import org.openmbee.mms.json.ElementJson; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class NodeOperation { - - protected final Logger logger = LoggerFactory.getLogger(getClass()); - protected NodeDAO nodeRepository; - protected NodeIndexDAO nodeIndex; - protected CommitDAO commitRepository; - protected BranchDAO branchRepository; - - protected DateTimeFormatter formatter = DateTimeFormatter - .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone( - ZoneId.systemDefault()); - - private boolean preserveTimestamps = false; - - - @Autowired - public void setNodeRepository(NodeDAO nodeRepository) { - this.nodeRepository = nodeRepository; - } - - @Autowired - public void setNodeIndex(NodeIndexDAO nodeIndex) { - this.nodeIndex = nodeIndex; - } - - @Autowired - public void setCommitRepository(CommitDAO commitRepository) { - this.commitRepository = commitRepository; - } - - @Autowired - public void setBranchRepository(BranchDAO branchRepository) { - this.branchRepository = branchRepository; - } - - public void initCommitJson(CommitJson cmjs, Instant now) { - if (cmjs.getId() == null || cmjs.getId().isEmpty()) { - cmjs.setId(UUID.randomUUID().toString()); - cmjs.setDocId(cmjs.getId()); - } - if (cmjs.getDocId() == null || cmjs.getDocId().isEmpty()) { - cmjs.setDocId(UUID.randomUUID().toString()); - } - cmjs.setCreated(formatter.format(now)); - cmjs.setAdded(new ArrayList<>()); - cmjs.setDeleted(new ArrayList<>()); - cmjs.setUpdated(new ArrayList<>()); - cmjs.setType("Commit"); - } - - public NodeChangeInfo initInfoFromNodes(List existingNodes, CommitJson cmjs) { - Set indexIds = new HashSet<>(); - Map existingNodeMap = new HashMap<>(); - Map reqElementMap = new HashMap<>(); - for (Node node : existingNodes) { - indexIds.add(node.getDocId()); - existingNodeMap.put(node.getNodeId(), node); - reqElementMap.put(node.getNodeId(), new ElementJson().setId(node.getNodeId())); - } - // bulk read existing elements in elastic - List existingElements = nodeIndex.findAllById(indexIds); - // set the _refId of the element json to be the ref it's 'found/requested' in, - String ref = ContextHolder.getContext().getBranchId(); - for (ElementJson e: existingElements) { - e.setRefId(ref); - } - Map existingElementMap = convertJsonToMap(existingElements); - - Instant now = Instant.now(); - if (cmjs != null) { - initCommitJson(cmjs, now); - } - - NodeChangeInfo info = new NodeChangeInfo(); - info.setCommitJson(cmjs); - info.setUpdatedMap(new HashMap<>()); - info.setDeletedMap(new HashMap<>()); - info.setExistingElementMap(existingElementMap); - info.setExistingNodeMap(existingNodeMap); - info.setReqElementMap(reqElementMap); - info.setReqIndexIds(indexIds); - info.setToSaveNodeMap(new HashMap<>()); - info.setRejected(new HashMap<>()); - info.setNow(now); - info.setOldDocIds(new HashSet<>()); - info.setActiveElementMap(new HashMap<>()); - return info; - } - - public NodeChangeInfo initInfo(List elements, CommitJson cmjs) { - Map reqElementMap = convertJsonToMap(elements); - List existingNodes = nodeRepository.findAllByNodeIds(reqElementMap.keySet()); - NodeChangeInfo info = initInfoFromNodes(existingNodes, cmjs); - info.setReqElementMap(reqElementMap); - return info; - } - - public void processElementAdded(ElementJson e, Node n, NodeChangeInfo info) { - CommitJson cmjs = info.getCommitJson(); - processElementAddedOrUpdated(e, n, info); - - e.setCreator(cmjs.getCreator()); //Only set on creation of new element - e.setCreated(cmjs.getCreated()); - - ElementVersion newObj = new ElementVersion() - .setDocId(e.getDocId()) - .setId(e.getId()) - .setType("Element"); - cmjs.getAdded().add(newObj); - - n.setNodeId(e.getId()); - n.setInitialCommit(e.getCommitId()); - } - - public void processElementUpdated(ElementJson e, Node n, NodeChangeInfo info) { - String previousDocId = n.getDocId(); - processElementAddedOrUpdated(e, n, info); - - info.getOldDocIds().add(previousDocId); - ElementVersion newObj= new ElementVersion() - .setPreviousDocId(previousDocId) - .setDocId(e.getDocId()) - .setId(e.getId()) - .setType("Element"); - info.getCommitJson().getUpdated().add(newObj); - } - - private void processElementAddedOrUpdated(ElementJson e, Node n, NodeChangeInfo info) { - CommitJson cmjs = info.getCommitJson(); - e.setProjectId(cmjs.getProjectId()); - e.setRefId(cmjs.getRefId()); - List inRefIds = new ArrayList<>(); - inRefIds.add(cmjs.getRefId()); - e.setInRefIds(inRefIds); - String docId = UUID.randomUUID().toString(); - e.setDocId(docId); - e.setCommitId(cmjs.getId()); - - if(!preserveTimestamps) { - e.setModified(cmjs.getCreated()); - e.setModifier(cmjs.getCreator()); - } - - n.setDocId(e.getDocId()); - n.setLastCommit(cmjs.getId()); - n.setDeleted(false); - n.setNodeType(0); - - info.getToSaveNodeMap().put(e.getId(), n); - info.getUpdatedMap().put(e.getId(), e); - } - - public void processElementDeleted(ElementJson e, Node n, NodeChangeInfo info) { - ElementVersion newObj = new ElementVersion() - .setPreviousDocId(n.getDocId()) - .setId(e.getId()) - .setType("Element"); - info.getCommitJson().getDeleted().add(newObj); - info.getOldDocIds().add(n.getDocId()); - info.getToSaveNodeMap().put(n.getNodeId(), n); - n.setDeleted(true); - } - - public boolean existingNodeContainsNodeId(NodeGetInfo info, String nodeId) { - if (!info.getExistingNodeMap().containsKey(nodeId)) { - rejectNotFound(info, nodeId); - return false; - } - return true; - } - - protected void rejectNotFound(NodeGetInfo info, String nodeId) { - info.addRejection(nodeId, new Rejection(nodeId, 404, "Not Found")); - } - - public static Map convertJsonToMap(List elements) { - Map result = new HashMap<>(); - for (ElementJson elem : elements) { - if (elem == null) { - continue; - } - if (elem.getId() != null && !elem.getId().equals("")) { - result.put(elem.getId(), elem); - } - } - return result; - } - - public static Map convertNodesToMap(List nodes) { - Map result = new HashMap<>(); - for (Node node : nodes) { - if (node == null) { - continue; - } - if (node.getNodeId() != null && !node.getNodeId().equals("")) { - result.put(node.getNodeId(), node); - } - } - return result; - } - - public static List sort(List ids, List orig) { - Map map = convertJsonToMap(orig); - List ret = new ArrayList<>(); - for (String id: ids) { - if (map.containsKey(id)) { - ret.add(map.get(id)); - } - } - return ret; - } - - //find first element of type in types following e's relkey (assuming relkey's value is an element id) - public Optional getFirstRelationshipOfType(ElementJson e, List types, String relkey) { - //only for latest graph - String nextId = (String)e.get(relkey); - if (nextId == null || nextId.isEmpty()) { - return Optional.empty(); - } - Optional nextNode = nodeRepository.findByNodeId(nextId); - while (nextNode.isPresent() && !nextNode.get().isDeleted()) { - Optional nextJson = nodeIndex.findById(nextNode.get().getDocId()); - if (!nextJson.isPresent()) { - return Optional.empty(); - } - if (types.contains(nextNode.get().getNodeType())) { - return nextJson; - } - nextId = (String)nextJson.get().get(relkey); - if (nextId == null || nextId.isEmpty()) { - return Optional.empty(); - } - nextNode = nodeRepository.findByNodeId(nextId); - } - return Optional.empty(); - } - - public boolean isPreserveTimestamps() { - return preserveTimestamps; - } - - public void setPreserveTimestamps(boolean preserveTimestamps) { - this.preserveTimestamps = preserveTimestamps; - } - - public String getLatestRefCommitId() { - Optional branch = branchRepository.findByBranchId(ContextHolder.getContext().getBranchId()); - Optional commit = commitRepository.findLatestByRef(branch.get()); - if (commit.isPresent()) { - return commit.get().getCommitId(); - } else { - return null; - } - } -} diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/NodePostHelper.java b/crud/src/main/java/org/openmbee/mms/crud/services/NodePostHelper.java deleted file mode 100644 index e0b9d27a5..000000000 --- a/crud/src/main/java/org/openmbee/mms/crud/services/NodePostHelper.java +++ /dev/null @@ -1,121 +0,0 @@ -package org.openmbee.mms.crud.services; - -import java.text.ParseException; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import java.util.Optional; -import java.util.UUID; -import org.openmbee.mms.core.config.Formats; -import org.openmbee.mms.core.exceptions.ConflictException; -import org.openmbee.mms.core.objects.Rejection; -import org.openmbee.mms.core.services.NodeChangeInfo; -import org.openmbee.mms.core.services.NodeService; -import org.openmbee.mms.data.domains.scoped.Commit; -import org.openmbee.mms.json.BaseJson; -import org.openmbee.mms.json.CommitJson; -import org.openmbee.mms.json.ElementJson; -import org.openmbee.mms.data.domains.scoped.Node; -import org.springframework.stereotype.Service; - -@Service -public class NodePostHelper extends NodeOperation { - - public boolean isUpdated(BaseJson element, Map existing, - NodeChangeInfo info) { - - if (element.isPartialOf(existing)) { - info.addRejection(element.getId(), new Rejection(element, 304, "Is Equivalent")); - return false; - } - return true; - } - - public boolean diffUpdateJson(BaseJson element, Map existing, - NodeChangeInfo info) { - - String jsonModified = element.getModified(); - Object existingModified = existing.get(BaseJson.MODIFIED); - if (jsonModified != null && !jsonModified.isEmpty() && existingModified != null) { - try { - Date jsonModDate = Formats.SDF.parse(jsonModified); - Date existingModDate = Formats.SDF.parse(existingModified.toString()); - if (jsonModDate.before(existingModDate)) { - info.addRejection(element.getId(), new Rejection(element, 409, "Conflict Detected")); - return false; - } - } catch (ParseException e) { - logger.info("date parse exception:" + jsonModified + " " + existingModified); - } - } - element.merge(existing); - return true; - } - - // create new elastic id for all element json, update modified time, modifier (use dummy for now), set _projectId, _refId, _inRefIds - public NodeChangeInfo processPostJson(List elements, boolean overwriteJson, - CommitJson cmjs, NodeService service, String lastCommitId) { - if (lastCommitId != null && !lastCommitId.isEmpty()) { - if (!lastCommitId.equals(getLatestRefCommitId())) { - throw new ConflictException("Given commitId " + lastCommitId + " is not the latest"); - } - } - NodeChangeInfo info = initInfo(elements, cmjs); - - // Logic for update/add - for (ElementJson element : elements) { - if (element == null) { - continue; - } - boolean added = false; - boolean updated = false; - if (element.getId() == null || element.getId().isEmpty()) { - element.setId(UUID.randomUUID().toString()); - } - ElementJson indexElement = info.getExistingElementMap().get(element.getId()); - Node n = info.getExistingNodeMap().get(element.getId()); - if (n == null) { - added = true; - } else if (indexElement == null) { - logger.warn("node db and index mismatch on element update: nodeId: " + n.getNodeId() + ", docId not found: " + n.getDocId()); - //info.addRejection(element.getId(), new Rejection(element, 500, "Update failed: previous element not found")); - //continue; - indexElement = new ElementJson().setId(n.getNodeId()).setDocId(n.getDocId()); - Optional init = commitRepository.findByCommitId(n.getInitialCommit()); - if (init.isPresent()) { - indexElement.setCreator(init.get().getCreator()); - indexElement.setCreated(formatter.format(init.get().getTimestamp())); - } - } - - if (!added) { - if (!overwriteJson) { - if (n.isDeleted() || isUpdated(element, indexElement, info)) { - updated = diffUpdateJson(element, indexElement, info); - } - } else { - updated = true; - String ARTIFACTS = "_artifacts"; - if (indexElement.containsKey(ARTIFACTS) && !element.containsKey(ARTIFACTS)) { - element.put(ARTIFACTS, indexElement.get(ARTIFACTS)); - } - element.setCreated(indexElement.getCreated()); - element.setCreator(indexElement.getCreator()); - } - } - - // create new elastic id for all element json, update modified time, modifier (use dummy for now), set _projectId, _refId, _inRefIds - if (added) { - Node node = new Node(); - processElementAdded(element, node, info); - service.extraProcessPostedElement(element, node, info); - } else if (updated) { - Node node = info.getExistingNodeMap().get(element.getId()); - processElementUpdated(element, node, info); - service.extraProcessPostedElement(element, node, info); - } - } - return info; - } -} diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/ProjectDeleteService.java b/crud/src/main/java/org/openmbee/mms/crud/services/ProjectDeleteService.java new file mode 100644 index 000000000..4736e3cf2 --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/services/ProjectDeleteService.java @@ -0,0 +1,62 @@ +package org.openmbee.mms.crud.services; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.openmbee.mms.core.config.Constants; +import org.openmbee.mms.core.config.ContextHolder; +import org.openmbee.mms.core.dao.ProjectPersistence; +import org.openmbee.mms.core.exceptions.NotFoundException; +import org.openmbee.mms.core.objects.ProjectsResponse; +import org.openmbee.mms.json.ProjectJson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@Service +public class ProjectDeleteService { + + private ProjectPersistence projectPersistence; + protected ObjectMapper om; + + @Autowired + public void setProjectPersistence(ProjectPersistence projectPersistence) { + this.projectPersistence = projectPersistence; + } + + @Autowired + public void setOm(ObjectMapper om) { + this.om = om; + } + + public ProjectsResponse deleteProject(String projectId, boolean hard) { + ProjectsResponse response = new ProjectsResponse(); + ProjectJson projectJson; + ContextHolder.setContext(projectId); + Optional projectJsonOption = projectPersistence.findById(projectId); + + List res = new ArrayList<>(); + + //Do not try to do a soft delete when an error condition is present. + if(projectJsonOption.isEmpty() && !hard) { + throw new NotFoundException("Project state is invalid"); + } + + projectJson = projectJsonOption.orElseGet(() -> { + ProjectJson newProject = new ProjectJson(); + newProject.setProjectId(projectId); + return newProject; + }); + + if(hard){ + projectPersistence.hardDelete(projectId); + } else { + projectPersistence.softDelete(projectId); + } + + projectJson.setIsDeleted(Constants.TRUE); + res.add(projectJson); + return response.setProjects(res); + } +} diff --git a/crud/src/test/java/org/openmbee/mms/crud/controllers/branches/BranchesControllerTest.java b/crud/src/test/java/org/openmbee/mms/crud/controllers/branches/BranchesControllerTest.java index 7dbd4eab0..c5addb154 100644 --- a/crud/src/test/java/org/openmbee/mms/crud/controllers/branches/BranchesControllerTest.java +++ b/crud/src/test/java/org/openmbee/mms/crud/controllers/branches/BranchesControllerTest.java @@ -6,7 +6,6 @@ public class BranchesControllerTest { - private void checkFail(String id){ assertFalse(BranchesController.isBranchIdValid(id)); } diff --git a/crud/src/test/java/org/openmbee/mms/crud/services/DefaultCommitServiceTest.java b/crud/src/test/java/org/openmbee/mms/crud/services/DefaultCommitServiceTest.java new file mode 100644 index 000000000..ddb163d4b --- /dev/null +++ b/crud/src/test/java/org/openmbee/mms/crud/services/DefaultCommitServiceTest.java @@ -0,0 +1,350 @@ +package org.openmbee.mms.crud.services; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.openmbee.mms.core.dao.BranchPersistence; +import org.openmbee.mms.core.dao.CommitPersistence; +import org.openmbee.mms.core.exceptions.BadRequestException; +import org.openmbee.mms.core.exceptions.InternalErrorException; +import org.openmbee.mms.core.exceptions.NotFoundException; +import org.openmbee.mms.core.objects.CommitsRequest; +import org.openmbee.mms.core.objects.CommitsResponse; +import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.RefJson; + +import java.util.*; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; + +public class DefaultCommitServiceTest { + + @Spy + @InjectMocks + private DefaultCommitService defaultCommitService; + + @Mock + private CommitPersistence commitPersistence; + @Mock + private BranchPersistence branchPersistence; + + + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + defaultCommitService.setCommitPersistence(commitPersistence); + defaultCommitService.setBranchPersistence(branchPersistence); + } + + + @Test + public void getRefCommitsBranchNotFound() { + String projectId = "PROJECT_ID"; + String refId = "REF_ID"; + + Map params = new HashMap<>(); + params.put("limit", Integer.toString(1)); + params.put("maxTimestamp", "2026-07-26T12:06:01.072-0400"); + + when(branchPersistence.findById(projectId, refId)).thenReturn(Optional.empty()); + + try { + defaultCommitService.getRefCommits(projectId, refId, params); + } catch (NotFoundException e) { + assertNotNull(e); + } + + resetMocks(); + } + + @Test + public void getRefCommitsBadParams() { + String projectId = "PROJECT_ID"; + String refId = "REF_ID"; + + Map params = new HashMap<>(); + params.put("limit", Integer.toString(1)); + params.put("maxTimestamp", "1776-07-04"); + + when(branchPersistence.findById(projectId, refId)).thenReturn(Optional.empty()); + + try { + defaultCommitService.getRefCommits(projectId, refId, params); + } catch (BadRequestException e) { + assertNotNull(e); + } + + resetMocks(); + } + + + @Test + public void getRefCommits() { + String projectId = "PROJECT_ID"; + String refId = "REF_ID"; + + Map params = new HashMap<>(); + params.put("limit", Integer.toString(1)); + params.put("maxTimestamp", "2026-07-26T12:06:01.072-0400"); + + CommitJson testCommit = new CommitJson(); + testCommit.setCommitId("TEST_COMMIT_ID"); + testCommit.setCreator("TEST_CREATOR"); + testCommit.setCreated("TEST_CREATED"); + testCommit.setDocId("TEST_DOC_ID"); + testCommit.setComment("TEST_COMMENT"); + + List testCommitList = new ArrayList(); + testCommitList.add(testCommit); + + RefJson refJson = new RefJson(); + when(branchPersistence.findById(projectId, refId)).thenReturn(Optional.of(refJson)); + + when(commitPersistence.findByProjectAndRefAndTimestampAndLimit(eq(projectId), eq(refId), any(), anyInt())).thenReturn(testCommitList); + + CommitsResponse response = defaultCommitService.getRefCommits(projectId, refId, params); + + assertFalse(response.getCommits().isEmpty()); + + resetMocks(); + } + + @Test + public void getCommitCommitNotFound() { + String projectId = "PROJECT_ID"; + String commitId = "COMMIT_ID"; + + when(commitPersistence.findById(projectId, commitId)).thenReturn(Optional.empty()); + + try { + defaultCommitService.getCommit(projectId, commitId); + } catch (NotFoundException e) { + assertNotNull(e); + } + + resetMocks(); + } + + @Test + public void getCommit() { + String projectId = "PROJECT_ID"; + String commitId = "COMMIT_ID"; + + CommitJson testCommit = new CommitJson(); + testCommit.setCommitId(commitId); + + when(commitPersistence.findById(projectId, commitId)).thenReturn(Optional.of(testCommit)); + + CommitsResponse response = defaultCommitService.getCommit(projectId, commitId); + + assertFalse(response.getCommits().isEmpty()); + + resetMocks(); + } + + @Test + public void getElementCommitsBranchNotFound() { + String projectId = "PROJECT_ID"; + String refId = "REF_ID"; + String elementId = "ELEMENT_ID"; + + Map params = new HashMap<>(); + params.put("limit", Integer.toString(1)); + params.put("maxTimestamp", "2026-07-26T12:06:01.072-0400"); + + when(branchPersistence.findById(projectId, refId)).thenReturn(Optional.empty()); + + try { + defaultCommitService.getElementCommits(projectId, refId, elementId, params); + } catch (NotFoundException e) { + assertNotNull(e); + } + + resetMocks(); + } + + @Test + public void getElementCommitsCommitsNotFound() { + String projectId = "PROJECT_ID"; + String refId = "REF_ID"; + String elementId = "ELEMENT_ID"; + + Map params = new HashMap<>(); + params.put("limit", Integer.toString(1)); + params.put("maxTimestamp", "2026-07-26T12:06:01.072-0400"); + + RefJson refJson = new RefJson(); + when(branchPersistence.findById(projectId, refId)).thenReturn(Optional.of(refJson)); + + when(commitPersistence.findByProjectAndRefAndTimestampAndLimit(projectId, refId, null, 0)).thenReturn(new ArrayList<>()); + when(commitPersistence.elementHistory(eq(projectId), eq(elementId), any())).thenReturn(new ArrayList<>()); + + CommitsResponse response = defaultCommitService.getElementCommits(projectId, refId, elementId, params); + + assertTrue(response.getCommits().isEmpty()); + + resetMocks(); + } + + @Test + public void getElementCommits() { + String projectId = "PROJECT_ID"; + String refId = "REF_ID"; + String elementId = "ELEMENT_ID"; + + Map params = new HashMap<>(); + params.put("limit", Integer.toString(1)); + params.put("maxTimestamp", "2026-07-26T12:06:01.072-0400"); + + CommitJson testCommit = new CommitJson(); + testCommit.setCommitId("TEST_COMMIT_ID"); + testCommit.setCreator("TEST_CREATOR"); + testCommit.setCreated("TEST_CREATED"); + testCommit.setDocId("TEST_DOC_ID"); + testCommit.setComment("TEST_COMMENT"); + + List testCommitList = new ArrayList(); + testCommitList.add(testCommit); + + RefJson refJson = new RefJson(); + when(branchPersistence.findById(projectId, refId)).thenReturn(Optional.of(refJson)); + + when(commitPersistence.findByProjectAndRefAndTimestampAndLimit(projectId, refId, null, 0)).thenReturn(testCommitList); + when(commitPersistence.elementHistory(eq(projectId), eq(elementId), any())).thenReturn(testCommitList); + + CommitsResponse response = defaultCommitService.getElementCommits(projectId, refId, elementId, params); + + assertFalse(response.getCommits().isEmpty()); + + resetMocks(); + } + + @Test + public void getCommitsThrowsError() { + String projectId = "PROJECT_ID"; + + CommitJson testCommit = new CommitJson(); + testCommit.setCommitId("TEST_COMMIT_ID"); + testCommit.setCreator("TEST_CREATOR"); + testCommit.setCreated("TEST_CREATED"); + testCommit.setDocId("TEST_DOC_ID"); + testCommit.setComment("TEST_COMMENT"); + + List testCommitList = new ArrayList(); + testCommitList.add(testCommit); + + CommitsRequest req = new CommitsRequest(); + req.setCommits(testCommitList); + + when(commitPersistence.findAllById(eq(projectId), any())).thenThrow(InternalErrorException.class); + + try { + defaultCommitService.getCommits(projectId, req); + } catch (InternalErrorException e) { + assertNotNull(e); + } + + resetMocks(); + } + + + @Test + public void getCommits() { + String projectId = "PROJECT_ID"; + + CommitJson testCommit = new CommitJson(); + testCommit.setCommitId("TEST_COMMIT_ID"); + testCommit.setCreator("TEST_CREATOR"); + testCommit.setCreated("TEST_CREATED"); + testCommit.setDocId("TEST_DOC_ID"); + testCommit.setComment("TEST_COMMENT"); + + List testCommitList = new ArrayList(); + testCommitList.add(testCommit); + + CommitsRequest req = new CommitsRequest(); + req.setCommits(testCommitList); + + when(commitPersistence.findAllById(eq(projectId), any())).thenReturn(testCommitList); + + CommitsResponse response = defaultCommitService.getCommits(projectId, req); + + assertFalse(response.getCommits().isEmpty()); + + resetMocks(); + } + + + @Test + public void getCommitsCommitsNotFound() { + String projectId = "PROJECT_ID"; + + List testCommitList = new ArrayList(); + CommitsRequest req = new CommitsRequest(); + req.setCommits(testCommitList); + + when(commitPersistence.findAllById(eq(projectId), any())).thenReturn(new ArrayList<>()); + + try { + defaultCommitService.getCommits(projectId, req); + } catch (Exception e) { + assertNotNull(e); + } + } + + @Test + public void isProjectNewCommitsIsEmpty() { + String projectId = "PROJECT_ID"; + + when(commitPersistence.findAllByProjectId(projectId)).thenReturn(new ArrayList<>()); + + assertTrue(defaultCommitService.isProjectNew(projectId)); + + resetMocks(); + } + + @Test + public void isProjectNewCommitsIsNull() { + String projectId = "PROJECT_ID"; + + when(commitPersistence.findAllByProjectId(projectId)).thenReturn(null); + + assertTrue(defaultCommitService.isProjectNew(projectId)); + + resetMocks(); + } + + @Test + public void isProjectNew() { + String projectId = "PROJECT_ID"; + + CommitJson testCommit = new CommitJson(); + testCommit.setCommitId("TEST_COMMIT_ID"); + testCommit.setCreator("TEST_CREATOR"); + testCommit.setCreated("TEST_CREATED"); + testCommit.setDocId("TEST_DOC_ID"); + testCommit.setComment("TEST_COMMENT"); + + List testCommitList = new ArrayList(); + testCommitList.add(testCommit); + + when(commitPersistence.findAllByProjectId(projectId)).thenReturn(testCommitList); + + assertFalse(defaultCommitService.isProjectNew(projectId)); + + resetMocks(); + } + + public void resetMocks() { + reset(branchPersistence); + reset(commitPersistence); + } +} diff --git a/data/data.gradle b/data/data.gradle index a4b4c31c0..2771c7eef 100644 --- a/data/data.gradle +++ b/data/data.gradle @@ -1,4 +1,7 @@ dependencies { + implementation project(':json') + implementation project(':core') + api commonDependencies.'spring-security-core' api commonDependencies.'spring-data-commons' diff --git a/core/src/main/java/org/openmbee/mms/core/dao/BranchDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/BranchDAO.java similarity index 89% rename from core/src/main/java/org/openmbee/mms/core/dao/BranchDAO.java rename to data/src/main/java/org/openmbee/mms/data/dao/BranchDAO.java index 5b8c8bebb..5f055a69e 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/BranchDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/BranchDAO.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import java.util.List; import java.util.Optional; diff --git a/core/src/main/java/org/openmbee/mms/core/dao/BranchGDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/BranchGDAO.java similarity index 87% rename from core/src/main/java/org/openmbee/mms/core/dao/BranchGDAO.java rename to data/src/main/java/org/openmbee/mms/data/dao/BranchGDAO.java index 5f76f5700..e8d656fe3 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/BranchGDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/BranchGDAO.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import java.util.Optional; import org.openmbee.mms.data.domains.global.Branch; diff --git a/core/src/main/java/org/openmbee/mms/core/dao/BranchIndexDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/BranchIndexDAO.java similarity index 94% rename from core/src/main/java/org/openmbee/mms/core/dao/BranchIndexDAO.java rename to data/src/main/java/org/openmbee/mms/data/dao/BranchIndexDAO.java index 125c4e977..99e1165b8 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/BranchIndexDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/BranchIndexDAO.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import java.util.Collection; import java.util.List; diff --git a/core/src/main/java/org/openmbee/mms/core/dao/CommitDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/CommitDAO.java similarity index 94% rename from core/src/main/java/org/openmbee/mms/core/dao/CommitDAO.java rename to data/src/main/java/org/openmbee/mms/data/dao/CommitDAO.java index 938e872ea..870da6cf0 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/CommitDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/CommitDAO.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import java.time.Instant; import java.util.List; diff --git a/core/src/main/java/org/openmbee/mms/core/dao/CommitIndexDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/CommitIndexDAO.java similarity index 95% rename from core/src/main/java/org/openmbee/mms/core/dao/CommitIndexDAO.java rename to data/src/main/java/org/openmbee/mms/data/dao/CommitIndexDAO.java index 34926b267..abd1c8e0e 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/CommitIndexDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/CommitIndexDAO.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import java.util.Collection; import java.util.List; diff --git a/core/src/main/java/org/openmbee/mms/core/dao/NodeDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/NodeDAO.java similarity index 89% rename from core/src/main/java/org/openmbee/mms/core/dao/NodeDAO.java rename to data/src/main/java/org/openmbee/mms/data/dao/NodeDAO.java index 478582103..d28040b6f 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/NodeDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/NodeDAO.java @@ -1,9 +1,10 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import java.util.Collection; import java.util.List; import java.util.Optional; +import org.openmbee.mms.core.dao.BaseDAO; import org.openmbee.mms.data.domains.scoped.Node; public interface NodeDAO extends BaseDAO { diff --git a/core/src/main/java/org/openmbee/mms/core/dao/NodeIndexDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/NodeIndexDAO.java similarity index 95% rename from core/src/main/java/org/openmbee/mms/core/dao/NodeIndexDAO.java rename to data/src/main/java/org/openmbee/mms/data/dao/NodeIndexDAO.java index 8d14d5fed..2bfb25ddb 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/NodeIndexDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/NodeIndexDAO.java @@ -1,10 +1,9 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; - import org.openmbee.mms.json.BaseJson; import org.openmbee.mms.json.ElementJson; diff --git a/core/src/main/java/org/openmbee/mms/core/dao/OrgDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/OrgDAO.java similarity index 91% rename from core/src/main/java/org/openmbee/mms/core/dao/OrgDAO.java rename to data/src/main/java/org/openmbee/mms/data/dao/OrgDAO.java index 4dd579bc7..1c2d0e0ca 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/OrgDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/OrgDAO.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import java.util.List; import java.util.Optional; diff --git a/core/src/main/java/org/openmbee/mms/core/dao/ProjectDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/ProjectDAO.java similarity index 52% rename from core/src/main/java/org/openmbee/mms/core/dao/ProjectDAO.java rename to data/src/main/java/org/openmbee/mms/data/dao/ProjectDAO.java index 244f543c9..858be1e3c 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/ProjectDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/ProjectDAO.java @@ -1,20 +1,24 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import java.util.List; import java.util.Optional; import org.openmbee.mms.data.domains.global.Project; +import javax.transaction.Transactional; + public interface ProjectDAO { Optional findByProjectId(String id); Optional findByProjectName(String name); + public List findAllByOrgId(String id); + + @Transactional(Transactional.TxType.REQUIRES_NEW) Project save(Project p); - void delete(Project p); + @Transactional(Transactional.TxType.REQUIRES_NEW) + void delete(String projectId); List findAll(); - - List findAllByOrgId(String id); } diff --git a/core/src/main/java/org/openmbee/mms/core/dao/ProjectIndex.java b/data/src/main/java/org/openmbee/mms/data/dao/ProjectIndex.java similarity index 92% rename from core/src/main/java/org/openmbee/mms/core/dao/ProjectIndex.java rename to data/src/main/java/org/openmbee/mms/data/dao/ProjectIndex.java index 5461c6300..009db5f69 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/ProjectIndex.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/ProjectIndex.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import java.util.List; import java.util.Optional; diff --git a/core/src/main/java/org/openmbee/mms/core/dao/WebhookDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/WebhookDAO.java similarity index 92% rename from core/src/main/java/org/openmbee/mms/core/dao/WebhookDAO.java rename to data/src/main/java/org/openmbee/mms/data/dao/WebhookDAO.java index b9e0957e3..fe44ed1e5 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/WebhookDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/WebhookDAO.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.core.dao; +package org.openmbee.mms.data.dao; import org.openmbee.mms.data.domains.global.Webhook; diff --git a/data/src/main/java/org/openmbee/mms/data/domains/global/Group.java b/data/src/main/java/org/openmbee/mms/data/domains/global/Group.java index 21dd2d860..9766154af 100644 --- a/data/src/main/java/org/openmbee/mms/data/domains/global/Group.java +++ b/data/src/main/java/org/openmbee/mms/data/domains/global/Group.java @@ -10,6 +10,7 @@ @Table(name = "groups") public class Group extends Base { + public static final String NAME_COLUMN = "name"; @Column(unique = true) private String name; diff --git a/data/src/main/java/org/openmbee/mms/data/domains/global/User.java b/data/src/main/java/org/openmbee/mms/data/domains/global/User.java index 549a4bf81..e25bc5992 100644 --- a/data/src/main/java/org/openmbee/mms/data/domains/global/User.java +++ b/data/src/main/java/org/openmbee/mms/data/domains/global/User.java @@ -2,12 +2,9 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Collection; import javax.persistence.*; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; - @JsonInclude(JsonInclude.Include.NON_EMPTY) @Entity @Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = "username"), @@ -20,7 +17,6 @@ public class User extends Base { private String lastName; private boolean admin; - @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) private String password; private boolean enabled; @@ -77,22 +73,6 @@ public void setPassword(String password) { this.password = password; } - public void encodePassword(String password) { - BCryptPasswordEncoder bcrypt = new BCryptPasswordEncoder(); - this.password = bcrypt.encode(password); - } - - public void updatePassword(String old, String newPass1, String newPass2) { - BCryptPasswordEncoder bcrypt = new BCryptPasswordEncoder(); - if (!password.equals(bcrypt.encode(old))) { - throw new IllegalArgumentException("Existing Password invalid"); - } - if (!newPass1.equals(newPass2)) { - throw new IllegalArgumentException("New Passwords don't match"); - } - this.password = bcrypt.encode(newPass1); - } - public String getFirstName() { return firstName; } diff --git a/elastic/elastic.gradle b/elastic/elastic.gradle index 4147846ef..5e0f73c22 100644 --- a/elastic/elastic.gradle +++ b/elastic/elastic.gradle @@ -1,8 +1,15 @@ dependencies { implementation project(':core') + implementation project(':data') implementation project(':search') implementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:$elasticVersion" testImplementation commonDependencies.'spring-boot-starter-test' } + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } +} \ No newline at end of file diff --git a/elastic/src/main/java/org/openmbee/mms/elastic/BranchElasticDAOImpl.java b/elastic/src/main/java/org/openmbee/mms/elastic/BranchElasticDAOImpl.java index 83ccb3e9b..c78fe68bd 100644 --- a/elastic/src/main/java/org/openmbee/mms/elastic/BranchElasticDAOImpl.java +++ b/elastic/src/main/java/org/openmbee/mms/elastic/BranchElasticDAOImpl.java @@ -6,7 +6,7 @@ import java.util.Set; import java.util.UUID; -import org.openmbee.mms.core.dao.BranchIndexDAO; +import org.openmbee.mms.data.dao.BranchIndexDAO; import org.openmbee.mms.json.BaseJson; import org.openmbee.mms.json.RefJson; import org.springframework.stereotype.Component; diff --git a/elastic/src/main/java/org/openmbee/mms/elastic/CommitElasticDAOImpl.java b/elastic/src/main/java/org/openmbee/mms/elastic/CommitElasticDAOImpl.java index 9cb2ce422..0b3f73268 100644 --- a/elastic/src/main/java/org/openmbee/mms/elastic/CommitElasticDAOImpl.java +++ b/elastic/src/main/java/org/openmbee/mms/elastic/CommitElasticDAOImpl.java @@ -6,7 +6,6 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.client.RequestOptions; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; @@ -14,7 +13,7 @@ import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortOrder; -import org.openmbee.mms.core.dao.CommitIndexDAO; +import org.openmbee.mms.data.dao.CommitIndexDAO; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.elastic.utils.Index; import org.openmbee.mms.json.BaseJson; diff --git a/elastic/src/main/java/org/openmbee/mms/elastic/NodeElasticDAOImpl.java b/elastic/src/main/java/org/openmbee/mms/elastic/NodeElasticDAOImpl.java index 15d82b963..d1d150c8b 100644 --- a/elastic/src/main/java/org/openmbee/mms/elastic/NodeElasticDAOImpl.java +++ b/elastic/src/main/java/org/openmbee/mms/elastic/NodeElasticDAOImpl.java @@ -10,7 +10,6 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.update.UpdateRequest; -import org.elasticsearch.client.RequestOptions; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.script.Script; @@ -20,7 +19,7 @@ import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortOrder; import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.dao.NodeIndexDAO; +import org.openmbee.mms.data.dao.NodeIndexDAO; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.elastic.utils.BulkProcessor; import org.openmbee.mms.elastic.utils.Index; diff --git a/elastic/src/main/java/org/openmbee/mms/elastic/ProjectElasticImpl.java b/elastic/src/main/java/org/openmbee/mms/elastic/ProjectElasticImpl.java index ccfc173f2..1d4a1d18f 100644 --- a/elastic/src/main/java/org/openmbee/mms/elastic/ProjectElasticImpl.java +++ b/elastic/src/main/java/org/openmbee/mms/elastic/ProjectElasticImpl.java @@ -13,7 +13,7 @@ import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.xcontent.XContentType; import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.dao.ProjectIndex; +import org.openmbee.mms.data.dao.ProjectIndex; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.json.ProjectJson; import org.springframework.beans.factory.annotation.Autowired; diff --git a/elastic/src/main/java/org/openmbee/mms/elastic/services/ElasticSearchService.java b/elastic/src/main/java/org/openmbee/mms/elastic/services/ElasticSearchService.java index 13b8798c2..73adc77a2 100644 --- a/elastic/src/main/java/org/openmbee/mms/elastic/services/ElasticSearchService.java +++ b/elastic/src/main/java/org/openmbee/mms/elastic/services/ElasticSearchService.java @@ -18,8 +18,9 @@ import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.openmbee.mms.core.config.Constants; import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.dao.NodeDAO; +import org.openmbee.mms.data.dao.NodeDAO; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.core.objects.ElementsSearchResponse; import org.openmbee.mms.core.objects.Rejection; @@ -100,7 +101,7 @@ public ElementsSearchResponse recursiveSearch(String projectId, String refId, Ma boolean showDeletedAsRejected = false; String showDeleted = params.remove(SearchConstants.SHOW_DELETED_FIELD); - if(showDeleted != null && showDeleted.equals("true")) { + if(showDeleted != null && showDeleted.equals(Constants.TRUE)) { showDeletedAsRejected = true; } diff --git a/elastic/src/main/resources/elastic_mappings/msosa_node.json b/elastic/src/main/resources/elastic_mappings/msosa_node.json new file mode 100644 index 000000000..515df203c --- /dev/null +++ b/elastic/src/main/resources/elastic_mappings/msosa_node.json @@ -0,0 +1,110 @@ +{ + + "dynamic_templates": [ + { + "id_as_keywords": { + "match_mapping_type": "string", + "match_pattern": "regex", + "match": ".*(Id|Ids)", + "mapping": { + "type": "keyword" + } + } + }, + { + "id_and_type": { + "match_mapping_type": "string", + "match_pattern": "regex", + "match": "(id|ids|type|uri|URI)", + "mapping": { + "type": "keyword" + } + } + }, + { + "boolean": { + "match_mapping_type": "*", + "match_pattern": "regex", + "match": "is[A-Z].*", + "mapping": { + "type": "boolean" + } + } + }, + { + "text": { + "match_mapping_type": "string", + "match_pattern": "regex", + "match": "(body|documentation)", + "mapping": { + "type": "text" + } + } + } + ], + "properties": { + "aggregation": { + "type": "keyword" + }, + "visibility": { + "type": "keyword" + }, + "direction": { + "type": "keyword" + }, + "ordering": { + "type": "keyword" + }, + "concurrency": { + "type": "keyword" + }, + "kind": { + "type": "keyword" + }, + "mode": { + "type": "keyword" + }, + "interactionOperator": { + "type": "keyword" + }, + "messageKind": { + "type": "keyword" + }, + "messageSort": { + "type": "keyword" + }, + "effect": { + "type": "keyword" + }, + "mustIsolate": { + "type": "boolean" + }, + "_creator": { + "type": "keyword" + }, + "_modifier": { + "type": "keyword" + }, + "_qualifiedName": { + "type": "keyword" + }, + "_qualifiedId": { + "type": "keyword" + }, + "_created": { + "type": "date", + "format": "yyyy-MM-dd'T'HH:mm:ss.SSSZ" + }, + "_modified": { + "type": "date", + "format": "yyyy-MM-dd'T'HH:mm:ss.SSSZ" + }, + "_docId": { + "type": "keyword" + }, + "value" : { + "type": "text" + } + } + +} \ No newline at end of file diff --git a/example/example.gradle b/example/example.gradle index bc279fa9e..370165470 100644 --- a/example/example.gradle +++ b/example/example.gradle @@ -26,6 +26,9 @@ dependencies { project(':search'), project(':storage'), project(':groups'), + project(':view'), + project(':msosa'), + project(':federatedpersistence'), 'org.springframework.boot:spring-boot-starter-web', 'org.postgresql:postgresql:42.2.5', //'mysql:mysql-connector-java:8.0.17', diff --git a/federatedpersistence/README.md b/federatedpersistence/README.md new file mode 100644 index 000000000..7851fbe41 --- /dev/null +++ b/federatedpersistence/README.md @@ -0,0 +1,3 @@ +## Federated Persistence + +This module implements the federated persistence model diff --git a/federatedpersistence/federatedpersistence.gradle b/federatedpersistence/federatedpersistence.gradle new file mode 100644 index 000000000..1f4d5bd15 --- /dev/null +++ b/federatedpersistence/federatedpersistence.gradle @@ -0,0 +1,11 @@ +dependencies { + implementation project(':core') + implementation project(':crud') + implementation project(':data') + implementation project(':rdb') + implementation project(':webhooks') + implementation 'org.apache.commons:commons-lang3:3.10' + implementation commonDependencies.'spring-boot' + + testImplementation commonDependencies.'spring-boot-starter-test' +} diff --git a/permissions/src/main/java/org/openmbee/mms/permissions/config/DefaultPermissionsDelegateConfig.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/config/DefaultPermissionsDelegateConfig.java similarity index 64% rename from permissions/src/main/java/org/openmbee/mms/permissions/config/DefaultPermissionsDelegateConfig.java rename to federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/config/DefaultPermissionsDelegateConfig.java index f4f00f365..b1e1d3d3a 100644 --- a/permissions/src/main/java/org/openmbee/mms/permissions/config/DefaultPermissionsDelegateConfig.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/config/DefaultPermissionsDelegateConfig.java @@ -1,7 +1,7 @@ -package org.openmbee.mms.permissions.config; +package org.openmbee.mms.federatedpersistence.config; import org.openmbee.mms.core.delegation.PermissionsDelegateFactory; -import org.openmbee.mms.permissions.delegation.DefaultPermissionsDelegateFactory; +import org.openmbee.mms.federatedpersistence.permissions.DefaultFederatedPermissionsDelegateFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @@ -12,7 +12,7 @@ public class DefaultPermissionsDelegateConfig { @Bean @Order(0) public PermissionsDelegateFactory permissionsDelegateFactory(){ - return new DefaultPermissionsDelegateFactory(); + return new DefaultFederatedPermissionsDelegateFactory(); } } diff --git a/permissions/src/main/java/org/openmbee/mms/permissions/config/PermissionInit.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/config/PermissionInit.java similarity index 98% rename from permissions/src/main/java/org/openmbee/mms/permissions/config/PermissionInit.java rename to federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/config/PermissionInit.java index e2eff178b..caa3cd062 100644 --- a/permissions/src/main/java/org/openmbee/mms/permissions/config/PermissionInit.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/config/PermissionInit.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.permissions.config; +package org.openmbee.mms.federatedpersistence.config; import org.openmbee.mms.data.domains.global.Group; import org.openmbee.mms.data.domains.global.Privilege; diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedBranchPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedBranchPersistence.java new file mode 100644 index 000000000..b346c5da4 --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedBranchPersistence.java @@ -0,0 +1,165 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.config.Constants; +import org.openmbee.mms.core.config.ContextHolder; +import org.openmbee.mms.core.config.Formats; +import org.openmbee.mms.core.dao.*; +import org.openmbee.mms.core.exceptions.BadRequestException; +import org.openmbee.mms.core.exceptions.DeletedException; +import org.openmbee.mms.core.exceptions.InternalErrorException; +import org.openmbee.mms.core.exceptions.NotFoundException; +import org.openmbee.mms.core.objects.RefsResponse; +import org.openmbee.mms.data.dao.*; +import org.openmbee.mms.data.domains.global.Project; +import org.openmbee.mms.data.domains.scoped.Branch; +import org.openmbee.mms.data.domains.scoped.Commit; +import org.openmbee.mms.json.RefJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; + +@Component +public class FederatedBranchPersistence implements BranchPersistence { + private static Logger logger = LoggerFactory.getLogger(FederatedBranchPersistence.class); + + private BranchDAO branchDAO; + private BranchGDAO branchGDAO; + private BranchIndexDAO branchIndexDAO; + private CommitDAO commitDAO; + private ProjectDAO projectDAO; + + @Autowired + public FederatedBranchPersistence(BranchDAO branchDAO, BranchGDAO branchGDAO, BranchIndexDAO branchIndexDAO, CommitDAO commitDAO, ProjectDAO projectDAO) { + this.branchDAO = branchDAO; + this.branchGDAO = branchGDAO; + this.branchIndexDAO = branchIndexDAO; + this.commitDAO = commitDAO; + this.projectDAO = projectDAO; + } + + @Override + public RefJson save(RefJson refJson) { + //Master branch special case + boolean isMasterBranch = refJson.getName().equals(Constants.MASTER_BRANCH); + + //Fill in docId + if (refJson.getDocId() == null || refJson.getDocId().isEmpty()) { + refJson.setDocId(branchIndexDAO.createDocId(refJson)); + } + + //Setup scoped Branch object + Branch scopedBranch = new Branch(); + scopedBranch.setBranchId(refJson.getId()); + scopedBranch.setBranchName(refJson.getName()); + scopedBranch.setDescription(refJson.getDescription()); + scopedBranch.setTag(refJson.isTag()); + scopedBranch.setTimestamp(Formats.FORMATTER.parse(refJson.getCreated(), Instant::from)); + scopedBranch.setParentRefId(refJson.getParentRefId()); + scopedBranch.setDocId(refJson.getDocId()); + + //Setup global Branch object + Optional project = projectDAO.findByProjectId(refJson.getProjectId()); + if(project.isEmpty()) { + throw new NotFoundException("project not found"); + } + org.openmbee.mms.data.domains.global.Branch globalBranch = new org.openmbee.mms.data.domains.global.Branch(); + globalBranch.setProject(project.get()); + globalBranch.setBranchId(refJson.getId()); + globalBranch.setInherit(true); + + //Master branch case. Can skip parent branch and parent commit check + if(!isMasterBranch) { + //Validate parent branch + Optional refOption = branchDAO.findByBranchId(scopedBranch.getParentRefId()); + if (!refOption.isPresent()) { + throw new InternalErrorException("Cannot determine parent branch."); + } + + //Validate parent commit + Optional parentCommit = commitDAO.findLatestByRef(refOption.get()); + if (!parentCommit.isPresent()) { + throw new InternalErrorException("Cannot determine latest commit of branch."); + } + + if (refJson.getParentCommitId() != null) { + if (!refJson.getParentCommitId().equals(parentCommit.get().getCommitId())) { + //This DAO cannot create branches from historic versions + throw new BadRequestException("Internal Error: Invalid branch creation logic."); + } + } else { + refJson.setParentCommitId(parentCommit.get().getCommitId()); + } + parentCommit.ifPresent(parent -> scopedBranch.setParentCommit(parent.getId())); + } + + //DO save + ContextHolder.setContext(null); + branchGDAO.save(globalBranch); + + String projectId = refJson.getProjectId(); + ContextHolder.setContext(projectId); + branchIndexDAO.update(refJson); + branchDAO.save(scopedBranch); + return refJson; + } + + @Override + public RefJson update(RefJson refJson) { + return save(refJson); + } + + @Override + public List findAll(String projectId) { + ContextHolder.setContext(projectId); + List branches = branchDAO.findAll(); + Set docIds = new HashSet<>(); + branches.forEach(branch -> docIds.add(branch.getDocId())); + return branchIndexDAO.findAllById(docIds); + } + + @Override + public Optional findById(String projectId, String refId) { + ContextHolder.setContext(projectId); + Optional branchesOption = this.branchDAO.findByBranchId(refId); + if (!branchesOption.isPresent()) { + return Optional.empty(); + } + Branch b = branchesOption.get(); + if (b.isDeleted()) { + throw new DeletedException(new RefsResponse()); + } + Optional refOption = branchIndexDAO.findById(b.getDocId()); + if (!refOption.isPresent()) { + logger.error("Federated data inconsistency: JSON Not found for {} with docId: {}", + b.getBranchId(), b.getDocId()); + throw new NotFoundException(new RefsResponse()); + } + return refOption; + } + + @Override + public Optional deleteById(String projectId, String refId) { + ContextHolder.setContext(projectId); + Optional branch = this.branchDAO.findByBranchId(refId); + if (!branch.isPresent()) { + return Optional.empty(); + } + Branch b = branch.get(); + b.setDeleted(true); + branchDAO.save(b); + RefJson refJson = new RefJson().setDocId(b.getDocId()).setDeleted(true) + .setProjectId(projectId).setId(refId); + return Optional.of(branchIndexDAO.update(refJson)); + } + + @Override + public boolean inheritsPermissions(String projectId, String branchId) { + Optional branch = + branchGDAO.findByProject_ProjectIdAndBranchId(projectId, branchId); + return branch.map(org.openmbee.mms.data.domains.global.Branch::isInherit).orElse(false); + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java new file mode 100644 index 000000000..70c21127d --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java @@ -0,0 +1,188 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.config.ContextHolder; +import org.openmbee.mms.core.config.Formats; +import org.openmbee.mms.data.dao.BranchDAO; +import org.openmbee.mms.data.dao.CommitDAO; +import org.openmbee.mms.data.dao.CommitIndexDAO; +import org.openmbee.mms.core.dao.CommitPersistence; +import org.openmbee.mms.core.exceptions.InternalErrorException; +import org.openmbee.mms.core.exceptions.NotFoundException; +import org.openmbee.mms.data.domains.scoped.Branch; +import org.openmbee.mms.data.domains.scoped.Commit; +import org.openmbee.mms.data.domains.scoped.CommitType; +import org.openmbee.mms.json.BaseJson; +import org.openmbee.mms.json.CommitJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; + +@Component("federatedCommitPersistence") +public class FederatedCommitPersistence implements CommitPersistence { + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + private CommitDAO commitDAO; + private CommitIndexDAO commitIndexDAO; + private BranchDAO branchDAO; + + @Autowired + public FederatedCommitPersistence(CommitDAO commitDAO, CommitIndexDAO commitIndexDAO, BranchDAO branchDAO) { + this.commitDAO = commitDAO; + this.commitIndexDAO = commitIndexDAO; + this.branchDAO = branchDAO; + } + + @Override + public CommitJson save(CommitJson commitJson, Instant now) { + ContextHolder.setContext(commitJson.getProjectId()); + Commit commit = new Commit(); + commit.setComment(commitJson.getComment()); + commit.setCommitId(commitJson.getDocId()); + commit.setCreator(commitJson.getCreator()); + commit.setBranchId(commitJson.getRefId()); + commit.setCommitType(CommitType.COMMIT); + commit.setTimestamp(now); + + try { + commitDAO.save(commit); + commitIndexDAO.index(commitJson); + return commitJson; + } catch (Exception e) { + logger.error("Couldn't create commit: {}", commitJson.getId(), e); + //Need to clean up in case of partial creation + deleteById(commitJson.getProjectId(), commitJson.getId()); + throw new InternalErrorException("Could not create commit"); + } + } + + @Override + public CommitJson update(CommitJson commitJson) { + //TODO need to work on this logic + ContextHolder.setContext(commitJson.getProjectId()); + Optional commitOptional = commitDAO.findByCommitId(commitJson.getId()); + + if (commitOptional.isPresent()) { + Instant now = Instant.now(); + commitJson.setModified(Formats.FORMATTER.format(now)); + + Commit commit = commitOptional.get(); + commit.setComment(commitJson.getComment()); + commit.setTimestamp(now); + commitDAO.save(commit); + return commitIndexDAO.update(commitJson); + } + throw new NotFoundException("Could not update commit"); + } + + @Override + public Optional findById(String projectId, String commitId) { + ContextHolder.setContext(projectId); + Optional commit = commitDAO.findByCommitId(commitId); + if(!commit.isPresent()) { + return Optional.empty(); + } + Optional commitJson = commitIndexDAO.findById(commit.get().getCommitId()); + if(!commitJson.isPresent()) { + throw new InternalErrorException( + String.format("Federated data model inconsistency: Could not find commit json for docId %s", + commit.get().getCommitId())); + } + return commitJson; + } + + @Override + public List findAllById(String projectId, Set commitIds) { + ContextHolder.setContext(projectId); + Set foundCommitIds = new HashSet<>(); + + commitIds.forEach(commitId -> { + Optional commitOptional = commitDAO.findByCommitId(commitId); + if (commitOptional.isPresent()) { + foundCommitIds.add(commitOptional.get().getCommitId()); + } + }); + List commits = commitIndexDAO.findAllById(foundCommitIds); + commits.sort(Comparator.comparing(BaseJson::getCreated)); + return commits; + } + + @Override + public List findAllByProjectId(String projectId) { + ContextHolder.setContext(projectId); + Set commitIds = new HashSet<>(); + commitDAO.findAll().forEach(commit -> commitIds.add(commit.getCommitId())); + List commits = commitIndexDAO.findAllById(commitIds); + commits.sort(Comparator.comparing(CommitJson::getCreated).reversed()); + return commits; + } + + @Override + public Optional findLatestByProjectAndRef(String projectId, String refId) { + ContextHolder.setContext(projectId); + Optional branch = branchDAO.findByBranchId(refId); + if(!branch.isPresent()) { + return Optional.empty(); + } + Optional commit = commitDAO.findLatestByRef(branch.get()); + if(!commit.isPresent()) { + return Optional.empty(); + } + Optional commitJson = commitIndexDAO.findById(commit.get().getCommitId()); + if(!commitJson.isPresent()) { + throw new InternalErrorException( + String.format("Federated data model inconsistency: Could not find commit json for commitId %s", + commit.get().getCommitId())); + } + return commitJson; + } + + @Override + public Optional findByProjectAndRefAndTimestamp(String projectId, String refId, Instant timestamp) { + return Optional.empty(); + } + + @Override + public List findByProjectAndRefAndTimestampAndLimit(String projectId, String refId, Instant timestamp, int limit) { + ContextHolder.setContext(projectId); + Set commitIds = new HashSet<>(); + Optional branchOptional = branchDAO.findByBranchId(refId); + if(!branchOptional.isPresent()) { + return new ArrayList<>(); + } + Branch b = branchOptional.get(); + List commitList = commitDAO.findByRefAndTimestampAndLimit(b, timestamp, limit); + commitList.forEach(commit -> commitIds.add(commit.getCommitId())); + List commits = commitIndexDAO.findAllById(commitIds); + commits.sort(Comparator.comparing(CommitJson::getCreated).reversed()); + return commits; + } + + @Override + public List elementHistory(String projectId, String elementId, Set commitDocIds) { + ContextHolder.setContext(projectId); + List commits = commitIndexDAO.elementHistory(elementId, commitDocIds); + commits.sort(Comparator.comparing(CommitJson::getCreated).reversed()); + return commits; + } + + @Override + public Optional deleteById(String projectId, String commitId) { + ContextHolder.setContext(projectId); + Optional commitOptional = commitDAO.findByCommitId(commitId); + try { + Optional commitJsonOptional = commitOptional.isPresent() ? + commitIndexDAO.findById(commitOptional.get().getCommitId()) : Optional.empty(); + if (commitJsonOptional.isPresent()) { + commitIndexDAO.deleteById(commitJsonOptional.get().getDocId()); + return commitJsonOptional; + } + } catch (Exception e) { + throw new InternalErrorException("Could not delete commit"); + } + throw new NotFoundException("Could not delete commit"); + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedGroupPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedGroupPersistence.java new file mode 100644 index 000000000..f85d3d94f --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedGroupPersistence.java @@ -0,0 +1,64 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.dao.GroupPersistence; +import org.openmbee.mms.data.domains.global.Group; +import org.openmbee.mms.federatedpersistence.utils.FederatedJsonUtils; +import org.openmbee.mms.json.GroupJson; +import org.openmbee.mms.rdb.repositories.GroupRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Component +public class FederatedGroupPersistence implements GroupPersistence { + + private GroupRepository groupRepository; + private FederatedJsonUtils jsonUtils; + + @Autowired + public void setGroupRepository(GroupRepository groupRepository) { + this.groupRepository = groupRepository; + } + + @Autowired + public void setJsonUtils(FederatedJsonUtils jsonUtils) { + this.jsonUtils = jsonUtils; + } + + @Override + public GroupJson save(GroupJson groupJson) { + Group groupObj = new Group(); + groupObj.setName(groupJson.getName()); + Group saved = groupRepository.saveAndFlush(groupObj); + return getJson(saved); + } + + @Override + public void delete(GroupJson groupJson) { + Optional group = groupRepository.findByName(groupJson.getName()); + group.ifPresent(g -> groupRepository.delete(g)); + } + + @Override + public Optional findByName(String name) { + Optional group = groupRepository.findByName(name); + return group.map(this::getJson); + } + + @Override + public Collection findAll() { + List groups = groupRepository.findAll(Sort.by(Group.NAME_COLUMN)); + return groups.stream().map(this::getJson).collect(Collectors.toList()); + } + + private GroupJson getJson(Group saved) { + GroupJson json = new GroupJson(); + json.merge(jsonUtils.convertToMap(saved)); + return json; + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeChangeInfo.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeChangeInfo.java new file mode 100644 index 000000000..d8103939a --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeChangeInfo.java @@ -0,0 +1,17 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.data.domains.scoped.Node; + +import java.util.Map; +import java.util.Set; + +public interface FederatedNodeChangeInfo extends NodeChangeInfo, FederatedNodeGetInfo { + Map getToSaveNodeMap(); + + NodeChangeInfo setToSaveNodeMap(Map toSaveNodeMap); + + Set getOldDocIds(); + + NodeChangeInfo setOldDocIds(Set oldDocIds); +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeChangeInfoImpl.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeChangeInfoImpl.java new file mode 100644 index 000000000..39574a3d2 --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeChangeInfoImpl.java @@ -0,0 +1,60 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.core.services.NodeChangeInfoImpl; +import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.data.domains.scoped.Node; + +import java.util.Map; +import java.util.Set; + +public class FederatedNodeChangeInfoImpl extends NodeChangeInfoImpl implements FederatedNodeChangeInfo { + private Map toSaveNodeMap; + private Map existingNodeMap; + private Set oldDocIds; + private Set reqIndexIds; + + @Override + public Map getToSaveNodeMap() { + return toSaveNodeMap; + } + + @Override + public NodeChangeInfo setToSaveNodeMap(Map toSaveNodeMap) { + this.toSaveNodeMap = toSaveNodeMap; + return this; + } + + @Override + public Map getExistingNodeMap() { + return existingNodeMap; + } + + @Override + public NodeGetInfo setExistingNodeMap(Map existingNodeMap) { + this.existingNodeMap = existingNodeMap; + return this; + } + + @Override + public Set getOldDocIds() { + return oldDocIds; + } + + @Override + public NodeChangeInfo setOldDocIds(Set oldDocIds) { + this.oldDocIds = oldDocIds; + return this; + } + + @Override + public Set getReqIndexIds() { + return reqIndexIds; + } + + @Override + public NodeChangeInfo setReqIndexIds(Set reqIndexIds) { + this.reqIndexIds = reqIndexIds; + return this; + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeGetInfo.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeGetInfo.java new file mode 100644 index 000000000..6a61f2f78 --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeGetInfo.java @@ -0,0 +1,18 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.data.domains.scoped.Node; + +import java.util.Map; +import java.util.Set; + +public interface FederatedNodeGetInfo extends NodeGetInfo { + + Set getReqIndexIds(); + + NodeGetInfo setReqIndexIds(Set reqIndexIds); + + Map getExistingNodeMap(); + + NodeGetInfo setExistingNodeMap(Map existingNodeMap); +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeGetInfoImpl.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeGetInfoImpl.java new file mode 100644 index 000000000..ead54f239 --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodeGetInfoImpl.java @@ -0,0 +1,36 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.core.services.NodeGetInfoImpl; +import org.openmbee.mms.data.domains.scoped.Node; + +import java.util.Map; +import java.util.Set; + +public class FederatedNodeGetInfoImpl extends NodeGetInfoImpl implements FederatedNodeGetInfo { + + private Set reqIndexIds; + private Map existingNodeMap; + + @Override + public Set getReqIndexIds() { + return reqIndexIds; + } + + @Override + public NodeGetInfo setReqIndexIds(Set reqIndexIds) { + this.reqIndexIds = reqIndexIds; + return this; + } + + @Override + public Map getExistingNodeMap() { + return existingNodeMap; + } + + @Override + public NodeGetInfo setExistingNodeMap(Map existingNodeMap) { + this.existingNodeMap = existingNodeMap; + return this; + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java new file mode 100644 index 000000000..1d9faac6b --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java @@ -0,0 +1,264 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.openmbee.mms.core.config.ContextHolder; +import org.openmbee.mms.core.dao.*; +import org.openmbee.mms.core.exceptions.BadRequestException; +import org.openmbee.mms.core.exceptions.InternalErrorException; +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.data.dao.*; +import org.openmbee.mms.federatedpersistence.domain.*; +import org.openmbee.mms.data.domains.scoped.Branch; +import org.openmbee.mms.data.domains.scoped.Commit; +import org.openmbee.mms.data.domains.scoped.Node; +import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.ElementJson; +import org.openmbee.mms.json.RefJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +@Component +public class FederatedNodePersistence implements NodePersistence { + protected static final Logger logger = LoggerFactory.getLogger(FederatedNodePersistence.class); + + protected CommitPersistence commitPersistence; + + @Autowired + public void setCommitPersistence(CommitPersistence commitPersistence) { + } + + @Value("${mms.stream.batch.size:100000}") + protected int streamLimit; + protected final ObjectMapper objectMapper = new ObjectMapper(); + + private final NodeDAO nodeDAO; + private final NodeIndexDAO nodeIndexDAO; + private final CommitDAO commitDAO; + private final BranchDAO branchDAO; + private final ObjectFactory nodeGetDomainFactory; + private final ObjectFactory nodeChangeDomainObjectFactory; + + public FederatedNodePersistence(NodeDAO nodeDAO, NodeIndexDAO nodeIndexDAO, CommitDAO commitDAO, + BranchDAO branchDAO, ObjectFactory nodeGetDomainFactory, + ObjectFactory nodeChangeDomainObjectFactory, + CommitPersistence commitPersistence) { + this.nodeDAO = nodeDAO; + this.nodeIndexDAO = nodeIndexDAO; + this.commitDAO = commitDAO; + this.branchDAO = branchDAO; + this.nodeGetDomainFactory = nodeGetDomainFactory; + this.nodeChangeDomainObjectFactory = nodeChangeDomainObjectFactory; + this.commitPersistence = commitPersistence; + } + + public FederatedNodeGetDomain getNodeGetDomain() { + return nodeGetDomainFactory.getObject(); + } + + public FederatedNodeChangeDomain getNodeChangeDomain() { + return nodeChangeDomainObjectFactory.getObject(); + } + + + @Override + public NodeChangeInfo prepareChange(CommitJson commitJson, boolean overwrite, boolean preserveTimestamps) { + return getNodeChangeDomain().initInfo(commitJson, overwrite, preserveTimestamps); + } + + @Override + public NodeChangeInfo prepareAddsUpdates(NodeChangeInfo nodeChangeInfo, Collection elements) { + ContextHolder.setContext(nodeChangeInfo.getCommitJson().getProjectId(), nodeChangeInfo.getCommitJson().getRefId()); + primeNodeChangeInfo(nodeChangeInfo, elements); + return getNodeChangeDomain().processPostJson(nodeChangeInfo, elements); + } + + @Override + public NodeChangeInfo prepareDeletes(NodeChangeInfo info, Collection jsons) { + if(!(info instanceof FederatedNodeChangeInfo)) { + throw new InternalErrorException("Invalid NodeChangeInfo type presented to NodeFederatedDAO"); + } + ContextHolder.setContext(info.getCommitJson().getProjectId(), info.getCommitJson().getRefId()); + primeNodeChangeInfo(info, jsons); + return getNodeChangeDomain().processDeleteJson(info, jsons); + } + + protected void primeNodeChangeInfo(NodeChangeInfo nodeChangeInfo, Collection transactedElements) { + getNodeChangeDomain().primeNodeChangeInfo(nodeChangeInfo, transactedElements); + } + + @Override + public NodeGetInfo findById(String projectId, String refId, String commitId, String elementId) { + ContextHolder.setContext(projectId, refId); + List elements = new ArrayList<>(); + elements.add(new ElementJson().setId(elementId)); + return getNodeGetDomain().processGetJson(elements, commitId); + } + + @Override + public List findAllByNodeType(String projectId, String refId, String commitId, int nodeType) { + ContextHolder.setContext(projectId, refId); + Optional branch = branchDAO.findByBranchId(refId); + validateBranch(branch); + List nodes; + Optional latestCommit = commitDAO.findLatestByRef(branch.get()); + if (commitId != null && !commitId.isEmpty() && !commitId.equals(latestCommit.map(Commit::getCommitId).orElse(null))) { + validateCommit(commitDAO.findByCommitId(commitId)); + nodes = nodeDAO.findAllByNodeType(nodeType); + } else { + nodes = nodeDAO.findAllByDeletedAndNodeType(false, nodeType); + } + return new ArrayList<>(getNodeGetDomain().processGetJsonFromNodes(nodes, commitId).getActiveElementMap().values()); + } + + + @Override + public NodeGetInfo findAll(String projectId, String refId, String commitId, List elements) { + ContextHolder.setContext(projectId, refId); + return getNodeGetDomain().processGetJson(elements, commitId); + } + + @Override + public List findAll(String projectId, String refId, String commitId) { + ContextHolder.setContext(projectId, refId); + Optional branch = branchDAO.findByBranchId(refId); + validateBranch(branch); + List nodes; + Optional latestCommit = commitDAO.findLatestByRef(branch.get()); + if (commitId != null && !commitId.isEmpty() && !commitId.equals(latestCommit.map(Commit::getCommitId).orElse(null))) { + validateCommit(commitDAO.findByCommitId(commitId)); + nodes = nodeDAO.findAll(); + } else { + nodes = nodeDAO.findAllByDeleted(false); + } + return new ArrayList<>(getNodeGetDomain().processGetJsonFromNodes(nodes, commitId).getActiveElementMap().values()); + } + + + @Override + public void streamAllAtCommit(String projectId, String refId, String commitId, OutputStream stream, String separator) { + ContextHolder.setContext(projectId, refId); + List nodes; + Optional branch = branchDAO.findByBranchId(refId); + validateBranch(branch); + Optional latestCommit = commitDAO.findLatestByRef(branch.get()); + if (commitId != null && !commitId.isEmpty() && !commitId.equals(latestCommit.map(Commit::getCommitId).orElse(null))) { + validateCommit(commitDAO.findByCommitId(commitId)); + nodes = nodeDAO.findAll(); + } else { + nodes = nodeDAO.findAllByDeleted(false); + } + + AtomicInteger counter = new AtomicInteger(); + batches(nodes, streamLimit).forEach(ns -> { + try { + if (counter.get() == 0) { + counter.getAndIncrement(); + } else { + stream.write(separator.getBytes(StandardCharsets.UTF_8)); + } + Collection result = getNodeGetDomain().processGetJsonFromNodes(ns, commitId) + .getActiveElementMap().values(); + stream.write(result.stream().map(this::toJson).collect(Collectors.joining(separator)) + .getBytes(StandardCharsets.UTF_8)); + } catch (IOException ioe) { + logger.error("Error writing to stream", ioe); + } + }); + } + + + @Override + public void branchElements(RefJson parentBranch, CommitJson parentCommit, RefJson targetBranch) { + if (parentBranch != null && parentCommit != null && targetBranch != null) { + String projectId = parentBranch.getProjectId(); + ContextHolder.setContext(projectId, parentBranch.getId()); + Set docIds = new HashSet<>(); + for (Node n : nodeDAO.findAllByDeleted(false)) { + docIds.add(n.getDocId()); + } + ContextHolder.setContext(projectId, targetBranch.getId()); + nodeIndexDAO.addToRef(docIds); + } else { + throw new InternalErrorException("Error committing transaction"); + } + } + + protected static Stream> batches(List source, int length) { + return IntStream.iterate(0, i -> i < source.size(), i -> i + length) + .mapToObj(i -> source.subList(i, Math.min(i + length, source.size()))); + } + + protected String toJson(ElementJson elementJson) { + try { + return objectMapper.writeValueAsString(elementJson); + } catch (JsonProcessingException e) { + logger.error("Error in toJson: ", e); + } + return ""; + } + + @Override + public FederatedNodeChangeInfo commitChanges(NodeChangeInfo info) { + if(!(info instanceof FederatedNodeChangeInfo)) { + throw new InternalErrorException("Invalid NodeChangeInfo type presented to NodeFederatedDAO"); + } + //TODO: Test rollback on IndexDAO failure + //TODO: move transaction stuff out into transaction service + TransactionDefinition def = new DefaultTransactionDefinition(); + TransactionStatus status = nodeDAO.getTransactionManager().getTransaction(def); + + FederatedNodeChangeInfo federatedInfo = (FederatedNodeChangeInfo) info; + Map nodes = federatedInfo.getToSaveNodeMap(); + Map json = federatedInfo.getUpdatedMap(); + CommitJson cmjs = federatedInfo.getCommitJson(); + Instant now = federatedInfo.getInstant(); + if (!nodes.isEmpty()) { + try { + nodeDAO.saveAll(new ArrayList<>(nodes.values())); + if (json != null && !json.isEmpty()) { + nodeIndexDAO.indexAll(json.values()); + } + nodeIndexDAO.removeFromRef(federatedInfo.getOldDocIds()); + commitPersistence.save(cmjs,now); + + nodeDAO.getTransactionManager().commit(status); + } catch (Exception e) { + logger.error("commitChanges error: ", e); + nodeDAO.getTransactionManager().rollback(status); + throw new InternalErrorException("Error committing transaction"); + } + } + return federatedInfo; + } + + private void validateBranch(Optional branch) { + if (!branch.isPresent()) { + throw new InternalErrorException("Cannot find branch"); + } + } + + private void validateCommit(Optional commit) { + if (!commit.isPresent()) { + throw new BadRequestException("commit id is invalid"); + } + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedOrgPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedOrgPersistence.java new file mode 100644 index 000000000..0bc7b70bc --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedOrgPersistence.java @@ -0,0 +1,83 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.exceptions.ForbiddenException; +import org.openmbee.mms.data.dao.OrgDAO; +import org.openmbee.mms.core.dao.OrgPersistence; +import org.openmbee.mms.core.exceptions.NotFoundException; +import org.openmbee.mms.data.domains.global.Organization; +import org.openmbee.mms.federatedpersistence.utils.FederatedJsonUtils; +import org.openmbee.mms.json.OrgJson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.Optional; +import java.util.stream.Collectors; + +@Component("federatedOrgPersistence") +public class FederatedOrgPersistence implements OrgPersistence { + + private OrgDAO orgDAO; + private FederatedJsonUtils jsonUtils; + + @Autowired + public void setOrgDAO(OrgDAO orgDAO) { + this.orgDAO = orgDAO; + } + + @Autowired + public void setJsonUtils(FederatedJsonUtils jsonUtils) { + this.jsonUtils = jsonUtils; + } + + @Override + public OrgJson save(OrgJson orgJson) { + Optional organizationOptional = orgDAO.findByOrganizationId(orgJson.getId()); + if (organizationOptional.isPresent()) { + throw new ForbiddenException("org " + orgJson.getId() + " already exists"); + } + Organization organization = new Organization(); + organization.setOrganizationId(orgJson.getId()); + organization.setOrganizationName(orgJson.getName()); + return getOrgJson(orgDAO.save(organization)); + } + + @Override + public Optional findById(String orgId) { + return orgDAO.findByOrganizationId(orgId).map(this::getOrgJson); + } + + @Override + public Collection findAll() { + return orgDAO.findAll().stream().map(this::getOrgJson).collect(Collectors.toList()); + } + + @Override + public OrgJson deleteById(String orgId) { + Optional organization = orgDAO.findByOrganizationId(orgId); + if(organization.isEmpty()) { + throw new NotFoundException(getOrgNotFoundMessage(orgId)); + } + orgDAO.delete(organization.get()); + return getOrgJson(organization.get()); + } + + @Override + public boolean hasPublicPermissions(String orgId) { + Optional organization = orgDAO.findByOrganizationId(orgId); + if (organization.isEmpty()) { + throw new NotFoundException(getOrgNotFoundMessage(orgId)); + } + return organization.get().isPublic(); + } + + protected OrgJson getOrgJson(Organization organization) { + OrgJson orgJson = new OrgJson(); + orgJson.merge(jsonUtils.convertToMap(organization)); + return orgJson; + } + + private String getOrgNotFoundMessage(String id) { + return String.format("org %s not found", id); + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java new file mode 100644 index 000000000..b20707c11 --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java @@ -0,0 +1,223 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.config.Constants; +import org.openmbee.mms.core.config.ContextHolder; +import org.openmbee.mms.core.dao.ProjectPersistence; +import org.openmbee.mms.core.exceptions.BadRequestException; +import org.openmbee.mms.core.exceptions.InternalErrorException; +import org.openmbee.mms.core.exceptions.NotFoundException; +import org.openmbee.mms.data.dao.OrgDAO; +import org.openmbee.mms.data.dao.ProjectDAO; +import org.openmbee.mms.data.dao.ProjectIndex; +import org.openmbee.mms.data.domains.global.Organization; +import org.openmbee.mms.data.domains.global.Project; +import org.openmbee.mms.json.ProjectJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Component("federatedProjectPersistence") +public class FederatedProjectPersistence implements ProjectPersistence { + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + private ProjectDAO projectDAO; + private ProjectIndex projectIndexDAO; + private OrgDAO orgRepository; + + @Autowired + public FederatedProjectPersistence(ProjectDAO projectDAO, ProjectIndex projectIndexDAO, OrgDAO orgDAO) { + this.projectDAO = projectDAO; + this.projectIndexDAO = projectIndexDAO; + this.orgRepository = orgDAO; + } + + @Override + public Optional findById(String projectId) { + ContextHolder.setContext(projectId); + Optional projectOption = projectDAO.findByProjectId(projectId); + + if (projectOption.isEmpty()) { + return Optional.empty(); + } + + Project project = projectOption.get(); + + Optional projectJsonOption = projectIndexDAO.findById(project.getDocId()); + if (projectJsonOption.isEmpty()) { + logger.error("Federated data inconsistency: JSON Not found for {} with docId: {}", + project.getProjectId(), project.getDocId()); + throw new NotFoundException("Project not found"); + } + + return projectJsonOption; + } + + @Override + public List findAllById(Set projectIds) { + return projectIds.stream().map(projectId -> { + Optional projectOption = projectDAO.findByProjectId(projectId); + if (projectOption.isPresent()) { + Project project = projectOption.get(); + ContextHolder.setContext(project.getProjectId()); + return projectIndexDAO.findById(project.getDocId()).orElse(null); + } + return null; + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + + @Override + public List findAll() { + return projectDAO.findAll().stream().map(project -> { + ContextHolder.setContext(project.getProjectId()); + return projectIndexDAO.findById(project.getDocId()).orElse(null); + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + + @Override + public Collection findAllByOrgId(String orgId) { + Optional org = orgRepository.findByOrganizationId(orgId); + if(org.isEmpty()) { + throw new NotFoundException("org not found"); + } + if(org.get().getProjects() == null) { + return List.of(); + } + return org.get().getProjects().stream().map(project -> { + ContextHolder.setContext(project.getProjectId()); + return projectIndexDAO.findById(project.getDocId()).orElse(null); + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + + @Override + public void hardDelete(String projectId) { + String message = ""; + try { + projectDAO.delete(projectId); + } catch (Exception ex){ + message = message.concat("Project DAO, cannot delete. "); + } + try { + ContextHolder.setContext(projectId); + projectIndexDAO.delete(projectId); + if(!message.equals("")) { + message = message.concat("Project index DAO deleted "); + } + } catch (Exception ex){ + message = message.concat("Project index DAO, cannot delete. "); + } + + if(!message.equals("")) { + throw new NotFoundException(message); + } + } + + @Override + public boolean inheritsPermissions(String projectId) { + Optional project = projectDAO.findByProjectId(projectId); + if (project.isEmpty()) { + throw new NotFoundException("project " + projectId + " not found"); + } + return project.get().isInherit(); + } + + @Override + public boolean hasPublicPermissions(String projectId) { + Optional project = projectDAO.findByProjectId(projectId); + if (project.isEmpty()) { + throw new NotFoundException("project " + projectId + " not found"); + } + return project.get().isPublic(); + } + + @Override + public void softDelete(String projectId) { + //TODO not called locally, otherwise delete + ContextHolder.setContext(projectId); + Optional project = this.projectDAO.findByProjectId(projectId); + + ContextHolder.setContext(projectId); + Optional projectJsonOption = project.isPresent() ? + projectIndexDAO.findById(project.get().getDocId()) : Optional.empty(); + if (project.isEmpty() || projectJsonOption.isEmpty()) { + throw new NotFoundException("Project state is invalid, cannot delete."); + } + + Project p = project.get(); + + p.setDeleted(true); + ContextHolder.setContext(null); + projectDAO.save(p); + + ProjectJson projectJson = projectJsonOption.get(); + projectJson.setIsDeleted(Constants.TRUE); + + ContextHolder.setContext(projectId); + projectIndexDAO.update(projectJson); + } + + @Override + public ProjectJson save(ProjectJson projectJson) { + + Optional org = orgRepository.findByOrganizationId(projectJson.getOrgId()); + + if(org.isEmpty()) { + throw new NotFoundException("org not found"); + } + + if (projectJson.getDocId() == null || projectJson.getDocId().isEmpty()) { + projectJson.setDocId(UUID.randomUUID().toString()); + } + + Project proj = new Project(); + proj.setProjectId(projectJson.getId()); + proj.setProjectName(projectJson.getName()); + proj.setOrganization(org.get()); + proj.setProjectType(projectJson.getProjectType()); + proj.setDocId(projectJson.getDocId()); + proj.setDeleted(Boolean.parseBoolean(projectJson.getIsDeleted())); + + try { + projectDAO.save(proj); + ContextHolder.setContext(projectJson.getProjectId()); + projectIndexDAO.create(projectJson); + + return projectJson; + } catch (Exception e) { + logger.error("Couldn't create project: {}", projectJson.getProjectId(), e); + //Need to clean up in case of partial creation + hardDelete(projectJson.getProjectId()); + throw new InternalErrorException("Could not create project"); + } + } + + @Override + public ProjectJson update(ProjectJson projectJson) { + + ContextHolder.setContext(projectJson.getProjectId()); + + Optional projOption = projectDAO.findByProjectId(projectJson.getProjectId()); + if (projOption.isPresent()) { + Project proj = projOption.get(); + if (projectJson.getName() != null && !projectJson.getName().isEmpty()) { + proj.setProjectName(projectJson.getName()); + } + if (projectJson.getOrgId() != null && !projectJson.getOrgId().isEmpty()) { + Optional org = orgRepository.findByOrganizationId(projectJson.getOrgId()); + if (org.isPresent() && !org.get().getOrganizationId().isEmpty()) { + proj.setOrganization(org.get()); + } else { + throw new BadRequestException("Invalid organization"); + } + } + projectJson.setDocId(proj.getDocId()); + projectDAO.save(proj); + ContextHolder.setContext(projectJson.getProjectId()); + return projectIndexDAO.update(projectJson); + } + throw new NotFoundException("Could not update project"); + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserGroupsPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserGroupsPersistence.java new file mode 100644 index 000000000..3d13c001b --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserGroupsPersistence.java @@ -0,0 +1,109 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.dao.UserGroupsPersistence; +import org.openmbee.mms.data.domains.global.Group; +import org.openmbee.mms.data.domains.global.User; +import org.openmbee.mms.federatedpersistence.utils.FederatedJsonUtils; +import org.openmbee.mms.json.GroupJson; +import org.openmbee.mms.json.UserJson; +import org.openmbee.mms.rdb.repositories.GroupRepository; +import org.openmbee.mms.rdb.repositories.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Component +public class FederatedUserGroupsPersistence implements UserGroupsPersistence { + private UserRepository userRepository; + private GroupRepository groupRepository; + private FederatedJsonUtils jsonUtils; + + @Autowired + public void setUserRepository(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Autowired + public void setGroupRepository(GroupRepository groupRepository) { + this.groupRepository = groupRepository; + } + + @Autowired + public void setJsonUtils(FederatedJsonUtils jsonUtils) { + this.jsonUtils = jsonUtils; + } + + @Override + public boolean addUserToGroup(String groupName, String username) { + Optional userOptional = userRepository.findByUsernameIgnoreCase(username); + if(userOptional.isEmpty()) { + return false; + } + Optional groupOptional = groupRepository.findByName(groupName); + if(groupOptional.isEmpty()) { + return false; + } + + User user = userOptional.get(); + if(user.getGroups().contains(groupOptional.get())) { + return false; + } + user.getGroups().add(groupOptional.get()); + userRepository.save(user); + return true; + } + + @Override + public boolean removeUserFromGroup(String groupName, String username) { + Optional userOptional = userRepository.findByUsernameIgnoreCase(username); + if(userOptional.isEmpty()) { + return false; + } + Optional groupOptional = groupRepository.findByName(groupName); + if(groupOptional.isEmpty()) { + return false; + } + + User user = userOptional.get(); + if(!user.getGroups().contains(groupOptional.get())) { + return false; + } + user.getGroups().remove(groupOptional.get()); + userRepository.save(user); + return true; + } + + @Override + public Collection findUsersInGroup(String groupName) { + Optional groupOptional = groupRepository.findByName(groupName); + if(groupOptional.isEmpty()){ + return List.of(); + } + return groupOptional.get().getUsers().stream().map(this::getJson).collect(Collectors.toList()); + } + + @Override + public Collection findGroupsAssignedToUser(String username) { + Optional userOptional = userRepository.findByUsernameIgnoreCase(username); + if(userOptional.isEmpty()) { + return List.of(); + } + return userOptional.get().getGroups().stream().map(this::getJson).collect(Collectors.toList()); + } + + private GroupJson getJson(Group saved) { + GroupJson json = new GroupJson(); + json.merge(jsonUtils.convertToMap(saved)); + return json; + } + + private UserJson getJson(User saved) { + UserJson savedJson = new UserJson(); + savedJson.merge(jsonUtils.convertToMap(saved)); + return savedJson; + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserPersistence.java new file mode 100644 index 000000000..21fdd4b42 --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserPersistence.java @@ -0,0 +1,73 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.dao.UserPersistence; +import org.openmbee.mms.data.domains.global.User; +import org.openmbee.mms.federatedpersistence.utils.FederatedJsonUtils; +import org.openmbee.mms.json.UserJson; +import org.openmbee.mms.rdb.repositories.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.Optional; +import java.util.stream.Collectors; + +@Component +public class FederatedUserPersistence implements UserPersistence { + + private UserRepository userRepository; + private FederatedJsonUtils jsonUtils; + + @Autowired + public void setUserRepository(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Autowired + public void setJsonUtils(FederatedJsonUtils jsonUtils) { + this.jsonUtils = jsonUtils; + } + + @Override + public UserJson save(UserJson userJson) { + + Optional existing = userRepository.findByUsernameIgnoreCase(userJson.getUsername()); + + User user; + if(existing.isPresent()) { + user = existing.get(); + } else { + user = new User(); + user.setUsername(userJson.getUsername()); + } + + user.setEmail(userJson.getEmail()); + user.setFirstName(userJson.getFirstName()); + user.setLastName(userJson.getLastName()); + user.setPassword(userJson.getPassword()); + user.setEnabled(userJson.isEnabled()); + user.setAdmin(userJson.isAdmin()); + + User saved = userRepository.save(user); + + return getJson(saved); + } + + @Override + public Optional findByUsername(String username) { + Optional user = userRepository.findByUsernameIgnoreCase(username); + return user.map(this::getJson); + } + + @Override + public Collection findAll() { + Collection users = userRepository.findAll(); + return users.stream().map(this::getJson).collect(Collectors.toList()); + } + + private UserJson getJson(User saved) { + UserJson savedJson = new UserJson(); + savedJson.merge(jsonUtils.convertToMap(saved)); + return savedJson; + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedWebhookPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedWebhookPersistence.java new file mode 100644 index 000000000..ffe46caa3 --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedWebhookPersistence.java @@ -0,0 +1,129 @@ +package org.openmbee.mms.federatedpersistence.dao; + +import org.openmbee.mms.core.exceptions.InternalErrorException; +import org.openmbee.mms.data.dao.ProjectDAO; +import org.openmbee.mms.data.dao.WebhookDAO; +import org.openmbee.mms.data.domains.global.Project; +import org.openmbee.mms.data.domains.global.Webhook; +import org.openmbee.mms.federatedpersistence.utils.FederatedJsonUtils; +import org.openmbee.mms.webhooks.json.WebhookJson; +import org.openmbee.mms.webhooks.persistence.WebhookPersistence; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Component +public class FederatedWebhookPersistence implements WebhookPersistence { + + private static Logger logger = LoggerFactory.getLogger(FederatedWebhookPersistence.class); + + private WebhookDAO webhookDao; + private ProjectDAO projectDao; + private FederatedJsonUtils jsonUtils; + + + @Autowired + public void setWebhookDao(WebhookDAO webhookDao) { + this.webhookDao = webhookDao; + } + + @Autowired + public void setProjectDao(ProjectDAO projectDao) { + this.projectDao = projectDao; + } + + @Autowired + public void setJsonUtils(FederatedJsonUtils jsonUtils) { + this.jsonUtils = jsonUtils; + } + + @Override + public WebhookJson save(WebhookJson webhookJson) { + if(webhookJson.getProjectId() == null || webhookJson.getProjectId().isEmpty()) { + logger.error("Webhook cannot be saved, missing project id"); + throw new InternalErrorException("Missing project id"); + } + Optional project = projectDao.findByProjectId(webhookJson.getProjectId()); + if(project.isEmpty()) { + throw new InternalErrorException("Cannot find project"); + } + + Optional existing = getWebhookById(webhookJson.getId()); + if(existing.isEmpty()) { + existing = webhookDao.findByProject_ProjectIdAndUrl(webhookJson.getProjectId(), webhookJson.getUrl()); + if(existing.isPresent()) { + //This webhook already exists, so just return what's there. + return existing.map(this::getWebhookJson).orElseThrow(() -> + new InternalErrorException("Could not generated webhook json from webhook")); + } + } + + Webhook webhook; + if (existing.isEmpty()) { + //Webhook doesn't exist, create it + webhook = new Webhook(); + webhook.setProject(project.get()); + } else { + //Webhook is there, but needs to have url updated + webhook = existing.get(); + } + webhook.setUrl(webhookJson.getUrl()); + Webhook saved = webhookDao.save(webhook); + webhookJson.setId(String.valueOf(saved.getId())); + return webhookJson; + } + + + @Override + public Optional findById(String id) { + if(id == null || id.isEmpty()) { + return Optional.empty(); + } + return getWebhookById(id).map(this::getWebhookJson); + } + + @Override + public List findAllByProjectId(String projectId) { + List webhooks = webhookDao.findAllByProject_ProjectId(projectId); + + return webhooks.stream().map(webhook -> { + WebhookJson webhookJson = getWebhookJson(webhook); + webhookJson.setProjectId(projectId); + return webhookJson; + }).collect(Collectors.toList()); + } + + + @Override + public Optional findByProjectIdAndUrl(String projectId, String url) { + return webhookDao.findByProject_ProjectIdAndUrl(projectId, url).map(this::getWebhookJson); + } + + @Override + public void delete(WebhookJson webhookJson) { + Optional webhook = getWebhookById(webhookJson.getId()); + webhook.ifPresent(w -> webhookDao.delete(w)); + } + + protected Optional getWebhookById(String id) { + try { + return webhookDao.findById(Long.parseLong(id)); + } catch(NumberFormatException ex) { + logger.error("Invalid webhook id format: {}", id); + return Optional.empty(); + } + } + + protected WebhookJson getWebhookJson(Webhook webhook) { + WebhookJson webhookJson = new WebhookJson(); + webhookJson.merge(jsonUtils.convertToMap(webhook)); + webhookJson.setId(webhook.getId().toString()); + return webhookJson; + } + +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedElementDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedElementDomain.java new file mode 100644 index 000000000..7a5b707e9 --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedElementDomain.java @@ -0,0 +1,64 @@ +package org.openmbee.mms.federatedpersistence.domain; + +import java.util.Optional; + +import org.openmbee.mms.data.dao.ProjectDAO; +import org.openmbee.mms.core.exceptions.NotFoundException; +import org.openmbee.mms.core.utils.ElementUtils; +import org.openmbee.mms.crud.domain.ElementDomain; +import org.openmbee.mms.crud.services.ElementUtilsFactory; +import org.openmbee.mms.data.domains.global.Project; +import org.openmbee.mms.json.ElementJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class FederatedElementDomain implements ElementDomain { + private static final Logger logger = LoggerFactory.getLogger(FederatedNodeChangeDomain.class); + + private ProjectDAO projectRepository; + private ElementUtilsFactory elementUtilsFactory; + + @Autowired + public void setProjectRepository(ProjectDAO projectRepository) { + this.projectRepository = projectRepository; + } + + @Autowired + public void setElementUtilsFactory(ElementUtilsFactory elementUtilsFactory) { + this.elementUtilsFactory = elementUtilsFactory; + } + + @Override + public ElementUtils getElementUtils(String projectId) { + return elementUtilsFactory.getElementUtil(getProjectType(projectId)); + } + + @Override + public Integer getNodeType(String projectId, ElementJson element){ + if (null == projectId) { + return 0; + } + + ElementUtils elementUtils = getElementUtils(projectId); + if (null == elementUtils) { + return 0; + } + return elementUtils.getNodeType(element).ordinal()+1; + } + + private String getProjectType(String projectId) { + return getProject(projectId).getProjectType(); + } + + private Project getProject(String projectId) { + Optional p = projectRepository.findByProjectId(projectId); + if (p.isPresent()) { + return p.get(); + } + throw new NotFoundException("project not found"); + } + +} \ No newline at end of file diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java new file mode 100644 index 000000000..2fe898a27 --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -0,0 +1,285 @@ +package org.openmbee.mms.federatedpersistence.domain; + +import org.openmbee.mms.data.dao.NodeDAO; +import org.openmbee.mms.data.dao.NodeIndexDAO; +import org.openmbee.mms.core.exceptions.InternalErrorException; +import org.openmbee.mms.core.objects.Rejection; +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.crud.domain.CommitDomain; +import org.openmbee.mms.crud.domain.NodeChangeDomain; +import org.openmbee.mms.crud.domain.NodeGetDomain; +import org.openmbee.mms.data.domains.scoped.Node; +import org.openmbee.mms.federatedpersistence.dao.FederatedNodeChangeInfo; +import org.openmbee.mms.federatedpersistence.dao.FederatedNodeChangeInfoImpl; +import org.openmbee.mms.federatedpersistence.dao.FederatedNodeGetInfo; +import org.openmbee.mms.json.BaseJson; +import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.ElementJson; +import org.openmbee.mms.json.ElementVersion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Component +public class FederatedNodeChangeDomain extends NodeChangeDomain { + private static final Logger logger = LoggerFactory.getLogger(FederatedNodeChangeDomain.class); + + protected FederatedNodeGetDomain getDomain; + protected FederatedElementDomain elementDomain; + protected NodeDAO nodeRepository; + protected NodeIndexDAO nodeIndex; + + @Autowired + public FederatedNodeChangeDomain(NodeGetDomain nodeGetDomain, CommitDomain commitDomain, + FederatedNodeGetDomain getDomain, NodeDAO nodeRepository, NodeIndexDAO nodeIndex, FederatedElementDomain elementDomain) { + super(nodeGetDomain, commitDomain); + this.getDomain = getDomain; + this.nodeRepository = nodeRepository; + this.nodeIndex = nodeIndex; + this.elementDomain = elementDomain; + } + + @Override + protected NodeChangeInfo createNodeChangeInfo() { + return new FederatedNodeChangeInfoImpl(); + } + + public NodeChangeInfo initInfo(CommitJson commitJson, boolean overwrite, boolean preserveTimestamps) { + commitJson.setId(UUID.randomUUID().toString()); + commitJson.setDocId(commitJson.getId()); + + NodeChangeInfo info = super.initInfo(commitJson, overwrite, preserveTimestamps); + if(info instanceof FederatedNodeChangeInfo) { + ((FederatedNodeChangeInfo)info).setOldDocIds(new HashSet<>()); + ((FederatedNodeChangeInfo)info).setReqIndexIds(new HashSet<>()); + ((FederatedNodeChangeInfo)info).setToSaveNodeMap(new HashMap<>()); + } else { + throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); + } + return info; + } + + + @Override + public void processElementAdded(NodeChangeInfo info, ElementJson element) { + if(!(info instanceof FederatedNodeChangeInfo)) { + throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); + } + + Node node = new Node(); + node.setNodeId(element.getId()); + + ((FederatedNodeChangeInfo) info).getExistingNodeMap().put(element.getId(), node); + super.processElementAdded(info, element); + + node.setInitialCommit(element.getDocId()); + + CommitJson commitJson = info.getCommitJson(); + ElementVersion newObj = new ElementVersion() + .setDocId(element.getDocId()) + .setId(element.getId()) + .setType("Element"); + commitJson.getAdded().add(newObj); + + ((FederatedNodeChangeInfo) info).getToSaveNodeMap().put(element.getId(), node); + } + + @Override + public void processElementUpdated(NodeChangeInfo info, ElementJson element) { + if(!(info instanceof FederatedNodeChangeInfo)) { + throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); + } + String previousDocId; + Node n = ((FederatedNodeChangeInfo) info).getExistingNodeMap().get(element.getId()); + if(n != null) { + previousDocId = n.getDocId(); + ((FederatedNodeChangeInfo) info).getOldDocIds().add(previousDocId); + } else { + return; + } + + super.processElementUpdated(info, element); + ElementVersion newObj= new ElementVersion() + .setPreviousDocId(previousDocId) + .setDocId(element.getDocId()) + .setId(element.getId()) + .setType("Element"); + info.getCommitJson().getUpdated().add(newObj); + } + + @Override + protected void processElementAddedOrUpdated(NodeChangeInfo info, ElementJson element) { + + if(! (info instanceof FederatedNodeChangeInfo)) { + throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); + } + + Node n = ((FederatedNodeChangeInfo) info).getExistingNodeMap().get(element.getId()); + if(n == null) { + return; + } + + String docId = UUID.randomUUID().toString(); + element.setDocId(docId); + + super.processElementAddedOrUpdated(info, element); + n.setDocId(element.getDocId()); + n.setLastCommit(info.getCommitJson().getId()); + n.setDeleted(false); + n.setNodeType(elementDomain.getNodeType(element.getProjectId(), element)); + + ((FederatedNodeChangeInfo) info).getToSaveNodeMap().put(element.getId(), n); + } + + @Override + public void processElementDeleted(NodeChangeInfo info, ElementJson element) { + if(! (info instanceof FederatedNodeChangeInfo)) { + throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); + } + Node n = ((FederatedNodeChangeInfo) info).getExistingNodeMap().get(element.getId()); + if(n != null) { + n.setNodeId(element.getId()); + n.setInitialCommit(element.getDocId()); + } else { + return; + } + + super.processElementDeleted(info, element); + + ElementVersion newObj = new ElementVersion() + .setPreviousDocId(n.getDocId()) + .setId(element.getId()) + .setType("Element"); + info.getCommitJson().getDeleted().add(newObj); + ((FederatedNodeChangeInfo) info).getOldDocIds().add(n.getDocId()); + ((FederatedNodeChangeInfo) info).getToSaveNodeMap().put(n.getNodeId(), n); + n.setDeleted(true); + } + + @Override + public FederatedNodeChangeInfo processDeleteJson(NodeChangeInfo info, Collection elements) { + if(! (info instanceof FederatedNodeChangeInfo)) { + throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); + } + + FederatedNodeChangeInfo federatedInfo = (FederatedNodeChangeInfo) info; + + for (String nodeId : info.getReqElementMap().keySet()) { + if (!existingNodeContainsNodeId(federatedInfo, nodeId)) { + continue; + } + Node node = federatedInfo.getExistingNodeMap().get(nodeId); + ElementJson indexElement = info.getExistingElementMap().get(nodeId); + + if (node.isDeleted()) { + info.addRejection(nodeId, new Rejection(indexElement, 410, "Already deleted")); + continue; + } + + ElementJson request = info.getReqElementMap().get(nodeId); + request.putAll(indexElement); + processElementDeleted(info, request); + info.getDeletedMap().put(nodeId, request); + } + return federatedInfo; + } + + protected boolean existingNodeContainsNodeId(FederatedNodeGetInfo info, String nodeId) { + if (!info.getExistingNodeMap().containsKey(nodeId)) { + rejectNotFound(info, nodeId); + return false; + } + return true; + } + + // create new elastic id for all element json, update modified time, modifier (use dummy for now), set _projectId, _refId, _inRefIds + @Override + public FederatedNodeChangeInfo processPostJson(NodeChangeInfo info, Collection elements) { + + if(!(info instanceof FederatedNodeChangeInfo)) { + throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); + } + FederatedNodeChangeInfo federatedInfo = (FederatedNodeChangeInfo) info; + + // Logic for update/add + for (ElementJson element : elements) { + if (element == null) { + continue; + } + boolean added = false; + boolean updated = false; + if (element.getId() == null || element.getId().isEmpty()) { + element.setId(UUID.randomUUID().toString()); + } + ElementJson indexElement = info.getExistingElementMap().get(element.getId()); + Node n = federatedInfo.getExistingNodeMap().get(element.getId()); + if (n == null) { + added = true; + } else if (indexElement == null) { + logger.warn("node db and index mismatch on element update: nodeId: " + n.getNodeId() + ", docId not found: " + n.getDocId()); + info.addRejection(element.getId(), new Rejection(element, 500, "Update failed: previous element not found")); + continue; + } + + if (!added) { + if (!info.getOverwrite()) { + if (n.isDeleted() || isUpdated(element, indexElement, info)) { + updated = diffUpdateJson(element, indexElement, info); + } + } else { + updated = true; + } + } + + // create new doc id for all element json, update modified time, modifier (use dummy for now), set _projectId, _refId, _inRefIds + if (added) { + processElementAdded(info, element); + } else if (updated) { + processElementUpdated(info, element); + } + } + return federatedInfo; + } + + @Override + public void addExistingElements(NodeChangeInfo nodeChangeInfo, List existingElements) { + getDomain.addExistingElements(nodeChangeInfo, existingElements); + } + + //ToDo :: Check + @Override + public void primeNodeChangeInfo(NodeChangeInfo nodeChangeInfo, Collection transactedElements) { + Set elementIds = transactedElements.stream().map(BaseJson::getId).filter(id->null!=id).collect(Collectors.toSet()); + List existingNodes = nodeRepository.findAllByNodeIds(elementIds); + + Set indexIds = new HashSet<>(); + Map existingNodeMap = new HashMap<>(); + Map reqElementMap = new HashMap<>(); + for (Node node : existingNodes) { + indexIds.add(node.getDocId()); + existingNodeMap.put(node.getNodeId(), node); + // reqElementMap.put(node.getNodeId(), new ElementJson().setId(node.getNodeId())); + } + if(!transactedElements.isEmpty()){ + reqElementMap.putAll(convertJsonToMap(transactedElements.parallelStream().collect(Collectors.toList()))); + } + + // bulk read existing elements in elastic + List existingElements = nodeIndex.findAllById(indexIds); + Map existingElementMap = convertJsonToMap(existingElements); + + if (nodeChangeInfo instanceof FederatedNodeChangeInfo) { + ((FederatedNodeChangeInfo) nodeChangeInfo).setExistingElementMap(existingElementMap); + ((FederatedNodeChangeInfo) nodeChangeInfo).setExistingNodeMap(existingNodeMap); + ((FederatedNodeChangeInfo) nodeChangeInfo).setReqElementMap(reqElementMap); + ((FederatedNodeChangeInfo) nodeChangeInfo).setReqIndexIds(indexIds); + ((FederatedNodeChangeInfo) nodeChangeInfo).setRejected(new HashMap<>()); + ((FederatedNodeChangeInfo) nodeChangeInfo).setActiveElementMap(new HashMap<>()); + } + } + +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java new file mode 100644 index 000000000..2bb42a70e --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java @@ -0,0 +1,202 @@ +package org.openmbee.mms.federatedpersistence.domain; + +import org.openmbee.mms.core.config.Formats; +import org.openmbee.mms.core.dao.CommitPersistence; +import org.openmbee.mms.data.dao.NodeDAO; +import org.openmbee.mms.data.dao.NodeIndexDAO; +import org.openmbee.mms.core.exceptions.BadRequestException; +import org.openmbee.mms.core.exceptions.InternalErrorException; +import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.crud.domain.NodeGetDomain; +import org.openmbee.mms.data.domains.scoped.Node; +import org.openmbee.mms.federatedpersistence.dao.FederatedNodeGetInfo; +import org.openmbee.mms.federatedpersistence.dao.FederatedNodeGetInfoImpl; +import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.ElementJson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; + +import static org.openmbee.mms.core.config.ContextHolder.getContext; + +@Component +public class FederatedNodeGetDomain extends NodeGetDomain { + + private CommitPersistence commitPersistence; + private NodeDAO nodeDAO; + private NodeIndexDAO nodeIndex; + + @Autowired + public FederatedNodeGetDomain(CommitPersistence commitPersistence, NodeDAO nodeDAO, NodeIndexDAO nodeIndex) { + this.commitPersistence = commitPersistence; + this.nodeDAO = nodeDAO; + this.nodeIndex = nodeIndex; + } + + public NodeGetInfo initInfo(List elements, CommitJson commitJson) { + Set indexIds = new HashSet<>(); + elements.stream().map(ElementJson::getId).forEach(indexIds::add); + List existingNodes = nodeDAO.findAllByNodeIds(indexIds); + NodeGetInfo nodeGetInfo = initInfoFromNodes(existingNodes, commitJson); + nodeGetInfo.getReqElementMap().putAll(convertJsonToMap(elements)); + return nodeGetInfo; + + } + + public NodeGetInfo initInfoFromNodes(List existingNodes, CommitJson commitJson) { + NodeGetInfo nodeGetInfo = super.initInfo(commitJson, this::createNodeGetInfo); + Set indexIds = new HashSet<>(); + Map existingNodeMap = new HashMap<>(); + for (Node node : existingNodes) { + indexIds.add(node.getDocId()); + existingNodeMap.put(node.getNodeId(), node); + } + // bulk read existing elements in elastic + List existingElements = nodeIndex.findAllById(indexIds); + addExistingElements(nodeGetInfo, existingElements); // handeled in addExistingElements + return nodeGetInfo; + } + + public NodeGetInfo processGetJsonFromNodes(List nodes, String commitId) { + if (commitId == null || commitId.isEmpty()) { + return processGetJsonFromNodes(nodes); + } + NodeGetInfo info = initInfoFromNodes(nodes, null); + return processCommit(info, commitId); + } + + public NodeGetInfo processGetJson(List elements, String commitId) { + if (commitId == null || commitId.isEmpty()) { + return processGetJson(elements); + } + NodeGetInfo info = initInfo(elements, null); //gets all current nodes + return processCommit(info, commitId); + } + + + public NodeGetInfo processGetJsonFromNodes(List nodes) { + NodeGetInfo info = initInfoFromNodes(nodes, null); + return processLatest(info); + } + + public NodeGetInfo processGetJson(List elements) { + NodeGetInfo info = initInfo(elements,null); + return processLatest(info); + } + + protected NodeGetInfo processLatest(NodeGetInfo info) { + if(!(info instanceof FederatedNodeGetInfo)) { + throw new InternalErrorException("Invalid use of FederatedNodeGetDomain"); + } + FederatedNodeGetInfo federatedInfo = (FederatedNodeGetInfo) info; + for (String nodeId : info.getReqElementMap().keySet()) { + if (!existingNodeContainsNodeId(federatedInfo, nodeId)) { + continue; + } + ElementJson indexElement = info.getExistingElementMap().get(nodeId); + + if (federatedInfo.getExistingNodeMap().get(nodeId).isDeleted()) { + rejectDeleted(info, nodeId, indexElement); + continue; + } + info.getActiveElementMap().put(nodeId, indexElement); + } + return info; + } + + protected NodeGetInfo processCommit(NodeGetInfo info, String commitId) { + if(!(info instanceof FederatedNodeGetInfo)) { + throw new InternalErrorException("Invalid use of FederatedNodeGetDomain"); + } + FederatedNodeGetInfo federatedInfo = (FederatedNodeGetInfo) info; + Optional commit = commitPersistence.findById(getContext().getProjectId(), commitId); + if (!commit.isPresent() ) { + throw new BadRequestException("commitId is invalid"); + } + Instant time = Instant.from(Formats.FORMATTER.parse(commit.get().getCreated())); //time of commit + List refCommitIds = null; //get it later if needed + for (String nodeId : info.getReqElementMap().keySet()) { + if (!existingNodeContainsNodeId(federatedInfo, nodeId)) { // nodeId not found + continue; + } + ElementJson indexElement = info.getExistingElementMap().get(nodeId); + Instant modified = Instant.from(Formats.FORMATTER.parse(indexElement.getModified())); + Instant created = Instant.from(Formats.FORMATTER.parse(indexElement.getCreated())); + + if (commitId.equals(indexElement.getCommitId())) { //exact match + addActiveElement(info, nodeId, indexElement); + } else if (created.isAfter(time)) { // element created after commit + rejectNotFound(info, nodeId); + } else if (modified.isAfter(time)) { // latest element is after commit + Optional tryExact = nodeIndex.getByCommitId(commitId, nodeId); + if (tryExact.isPresent()) { + addActiveElement(info, nodeId, tryExact.get()); + continue; // found exact match at commit + } + if (refCommitIds == null) { // need list of commitIds of current ref to filter on + refCommitIds = getRefCommitIds(time); + } + Optional e = nodeIndex.getElementLessThanOrEqualTimestamp(nodeId, + Formats.FORMATTER.format(time), refCommitIds); + if (e.isPresent()) { // found version of element at commit time + //TODO determine if element was deleted at the time? + addActiveElement(info, nodeId, e.get()); + } else { + rejectNotFound(info, nodeId); // element not found at commit time + } + } else if (federatedInfo.getExistingNodeMap().get(nodeId).isDeleted()) { // latest element is before commit, but deleted + rejectDeleted(info, nodeId, indexElement); + } else { // latest element version is version at commit, not deleted + addActiveElement(info, nodeId, indexElement); + } + } + return info; + } + + public boolean existingNodeContainsNodeId(FederatedNodeGetInfo info, String nodeId) { + if (!info.getExistingNodeMap().containsKey(nodeId)) { + rejectNotFound(info, nodeId); + return false; + } + return true; + } + + protected void addActiveElement(NodeGetInfo info, String nodeId, ElementJson indexElement) { + info.getActiveElementMap().put(nodeId, indexElement); + } + + protected List getRefCommitIds(Instant time) { + List commitIds = new ArrayList<>(); + + List refCommits = commitPersistence.findByProjectAndRefAndTimestampAndLimit(getContext().getProjectId(), getContext().getBranchId(), time, 0); + for (CommitJson c : refCommits) { + commitIds.add(c.getId()); + } + return commitIds; + } + + public void addExistingElements(NodeGetInfo info, List elements) { + super.addExistingElements(info, elements); + + if(info instanceof FederatedNodeGetInfo) { + FederatedNodeGetInfo federatedInfo = (FederatedNodeGetInfo)info; + Set elementIds = elements.stream().map(ElementJson::getId).collect(Collectors.toSet()); + List existingNodes = nodeDAO.findAllByNodeIds(elementIds); + //ToDo : could also be done in get functions in respective classes to prevent extra null checks and NPE + federatedInfo.setExistingNodeMap(new HashMap<>()); + federatedInfo.setReqIndexIds(new HashSet<>()); + for (Node node : existingNodes) { + federatedInfo.getReqIndexIds().add(node.getDocId()); + federatedInfo.getExistingNodeMap().put(node.getNodeId(), node); + federatedInfo.getReqElementMap().put(node.getNodeId(), new ElementJson().setId(node.getNodeId())); + } + } + } + + public NodeGetInfo createNodeGetInfo() { //ToDo :: check + return new FederatedNodeGetInfoImpl(); + } +} diff --git a/permissions/src/main/java/org/openmbee/mms/permissions/delegation/AbstractDefaultPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/AbstractDefaultPermissionsDelegate.java similarity index 95% rename from permissions/src/main/java/org/openmbee/mms/permissions/delegation/AbstractDefaultPermissionsDelegate.java rename to federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/AbstractDefaultPermissionsDelegate.java index b591fe2f9..6f36ecfd8 100644 --- a/permissions/src/main/java/org/openmbee/mms/permissions/delegation/AbstractDefaultPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/AbstractDefaultPermissionsDelegate.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.permissions.delegation; +package org.openmbee.mms.federatedpersistence.permissions; import org.openmbee.mms.core.delegation.PermissionsDelegate; import org.openmbee.mms.core.objects.PermissionUpdateRequest; @@ -60,7 +60,7 @@ protected Pair getGroupAndRole(PermissionUpdateRequest.Permission p Optional group = getGroupRepo().findByName(p.getName()); Optional role = getRoleRepo().findByName(p.getRole()); if (!role.isPresent()) { - return Pair.of(group.orElse(null), null); + return null; } if (!group.isPresent()) { group = Optional.of(new Group(p.getName())); diff --git a/permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultBranchPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java similarity index 92% rename from permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultBranchPermissionsDelegate.java rename to federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java index ded88a232..d3865862b 100644 --- a/permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultBranchPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java @@ -1,8 +1,6 @@ -package org.openmbee.mms.permissions.delegation; +package org.openmbee.mms.federatedpersistence.permissions; import org.apache.commons.lang3.builder.EqualsBuilder; -import org.openmbee.mms.core.builders.PermissionUpdateResponseBuilder; -import org.openmbee.mms.core.builders.PermissionUpdatesResponseBuilder; import org.openmbee.mms.core.config.AuthorizationConstants; import org.openmbee.mms.core.objects.PermissionResponse; import org.openmbee.mms.core.objects.PermissionUpdateRequest; @@ -17,7 +15,7 @@ import org.openmbee.mms.data.domains.global.ProjectUserPerm; import org.openmbee.mms.data.domains.global.Role; import org.openmbee.mms.data.domains.global.User; -import org.openmbee.mms.permissions.exceptions.PermissionException; +import org.openmbee.mms.federatedpersistence.permissions.exceptions.PermissionException; import org.openmbee.mms.rdb.repositories.BranchGroupPermRepository; import org.openmbee.mms.rdb.repositories.BranchRepository; import org.openmbee.mms.rdb.repositories.BranchUserPermRepository; @@ -85,6 +83,16 @@ public boolean hasPermission(String user, Set groups, String privilege) return false; } + @Override + public boolean hasGroupPermissions(String group, String privilege) { + for (BranchGroupPerm perm: branchGroupPermRepo.findAllByBranch(branch)) { + if (perm.getGroup().getName().equals(group) && perm.getRole().equals(privilege)) { + return true; + } + } + return false; + } + @Override public void initializePermissions(String creator) { initializePermissions(creator, false); @@ -103,7 +111,7 @@ public void initializePermissions(String creator, boolean inherit) { } branch.setInherit(inherit); - branchRepo.save(branch); + branch = branchRepo.save(branch); BranchUserPerm perm = new BranchUserPerm(branch, user.get(), role.get(), false); branchUserPermRepo.save(perm); @@ -119,6 +127,17 @@ public boolean setInherit(boolean isInherit) { return false; } + @Override + public PermissionResponse getInherit() { + PermissionResponse response = PermissionResponse.getDefaultResponse(); + if (branch.isInherit()) { + PermissionResponse.Permission permission = new PermissionResponse.Permission(); + permission.setInherited(branch.isInherit()); + response.getPermissions().add(permission); + } + return response; + } + @Override public void setPublic(boolean isPublic) { //Not supported @@ -126,7 +145,7 @@ public void setPublic(boolean isPublic) { @Override public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest req) { - PermissionUpdateResponseBuilder responseBuilder = new PermissionUpdateResponseBuilder(); + FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); switch(req.getAction()) { case MODIFY: @@ -192,13 +211,13 @@ public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest re @Override public PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest req) { - PermissionUpdateResponseBuilder responseBuilder = new PermissionUpdateResponseBuilder(); + FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); switch(req.getAction()) { case MODIFY: for (PermissionUpdateRequest.Permission p: req.getPermissions()) { Pair pair = getGroupAndRole(p); - if (pair.getFirst() == null || pair.getSecond() == null) { + if (pair == null) { continue; } Optional exist = branchGroupPermRepo.findByBranchAndGroupAndInheritedIsFalse(branch, pair.getFirst()); @@ -225,7 +244,7 @@ public PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest r branchGroupPermRepo.deleteByBranchAndInherited(branch, false); for (PermissionUpdateRequest.Permission p: req.getPermissions()) { Pair pair = getGroupAndRole(p); - if (pair.getFirst() == null || pair.getSecond() == null) { + if (pair == null) { continue; } BranchGroupPerm perm = new BranchGroupPerm(branch, pair.getFirst(), pair.getSecond(), false); @@ -365,7 +384,7 @@ public Collection getAll() { @Override public PermissionUpdatesResponse recalculateInheritedPerms() { - PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); + FederatedPermissionUpdatesResponseBuilder responseBuilder = new FederatedPermissionUpdatesResponseBuilder(); Collection branchUserPerms = branchUserPermRepo.findAllByBranchAndInherited(branch, true); branchUserPermRepo.deleteAll(branchUserPerms); diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultFederatedPermissionsDelegateFactory.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultFederatedPermissionsDelegateFactory.java new file mode 100644 index 000000000..234f21148 --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultFederatedPermissionsDelegateFactory.java @@ -0,0 +1,83 @@ +package org.openmbee.mms.federatedpersistence.permissions; + +import org.openmbee.mms.core.config.ContextHolder; +import org.openmbee.mms.data.dao.BranchGDAO; +import org.openmbee.mms.data.dao.OrgDAO; +import org.openmbee.mms.data.dao.ProjectDAO; +import org.openmbee.mms.core.delegation.PermissionsDelegate; +import org.openmbee.mms.core.delegation.PermissionsDelegateFactory; +import org.openmbee.mms.core.exceptions.NotFoundException; +import org.openmbee.mms.data.domains.global.Branch; +import org.openmbee.mms.data.domains.global.Organization; +import org.openmbee.mms.data.domains.global.Project; +import org.openmbee.mms.json.OrgJson; +import org.openmbee.mms.json.ProjectJson; +import org.openmbee.mms.json.RefJson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; + +import java.util.Optional; + +public class DefaultFederatedPermissionsDelegateFactory implements PermissionsDelegateFactory { + + private ApplicationContext applicationContext; + private ProjectDAO projectDAO; + private BranchGDAO branchDAO; + private OrgDAO orgDAO; + + @Autowired + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Autowired + public void setProjectDAO(ProjectDAO projectDAO) { + this.projectDAO = projectDAO; + } + + @Autowired + public void setBranchDAO(BranchGDAO branchDAO) { + this.branchDAO = branchDAO; + } + + @Autowired + public void setOrgDAO(OrgDAO orgDAO) { + this.orgDAO = orgDAO; + } + + @Override + public PermissionsDelegate getPermissionsDelegate(ProjectJson project) { + Optional projectOptional = projectDAO.findByProjectId(project.getProjectId()); + + if(projectOptional.isEmpty()) { + throw new NotFoundException("project not found"); + } + + return autowire(new DefaultProjectPermissionsDelegate(projectOptional.get())); + } + + @Override + public PermissionsDelegate getPermissionsDelegate(OrgJson organization) { + Optional orgOptional = orgDAO.findByOrganizationId(organization.getId()); + if(orgOptional.isEmpty()) { + throw new NotFoundException("org not found"); + } + return autowire(new DefaultOrgPermissionsDelegate(orgOptional.get())); + } + + @Override + public PermissionsDelegate getPermissionsDelegate(RefJson branch) { + ContextHolder.setContext(null); + Optional branchOptional = branchDAO.findByProject_ProjectIdAndBranchId(branch.getProjectId(), branch.getId()); + if(branchOptional.isEmpty()) { + throw new NotFoundException("branch not found"); + } + return autowire(new DefaultBranchPermissionsDelegate(branchOptional.get())); + } + + private PermissionsDelegate autowire(PermissionsDelegate permissionsDelegate) { + applicationContext.getAutowireCapableBeanFactory().autowireBean(permissionsDelegate); + return permissionsDelegate; + } + +} diff --git a/permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultOrgPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java similarity index 91% rename from permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultOrgPermissionsDelegate.java rename to federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java index 64ed7f179..0c61ad591 100644 --- a/permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultOrgPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java @@ -1,6 +1,5 @@ -package org.openmbee.mms.permissions.delegation; +package org.openmbee.mms.federatedpersistence.permissions; -import org.openmbee.mms.core.builders.PermissionUpdateResponseBuilder; import org.openmbee.mms.core.builders.PermissionUpdatesResponseBuilder; import org.openmbee.mms.core.config.AuthorizationConstants; import org.openmbee.mms.core.objects.PermissionResponse; @@ -14,7 +13,7 @@ import org.openmbee.mms.data.domains.global.Privilege; import org.openmbee.mms.data.domains.global.Role; import org.openmbee.mms.data.domains.global.User; -import org.openmbee.mms.permissions.exceptions.PermissionException; +import org.openmbee.mms.federatedpersistence.permissions.exceptions.PermissionException; import org.openmbee.mms.rdb.repositories.OrgGroupPermRepository; import org.openmbee.mms.rdb.repositories.OrgUserPermRepository; import org.openmbee.mms.rdb.repositories.OrganizationRepository; @@ -22,9 +21,7 @@ import org.springframework.data.util.Pair; import org.springframework.http.HttpStatus; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; +import java.util.*; public class DefaultOrgPermissionsDelegate extends AbstractDefaultPermissionsDelegate { @@ -71,6 +68,16 @@ public boolean hasPermission(String user, Set groups, String privilege) return false; } + @Override + public boolean hasGroupPermissions(String group, String privilege) { + for (OrgGroupPerm perm: orgGroupPermRepo.findAllByOrganization_OrganizationId(organization.getOrganizationId())) { + if (perm.getGroup().getName().equals(group) && perm.getRole().equals(privilege)) { + return true; + } + } + return false; + } + @Override public void initializePermissions(String creator) { initializePermissions(creator, false); @@ -103,6 +110,12 @@ public boolean setInherit(boolean isInherit) { return false; } + @Override + public PermissionResponse getInherit() { + //Orgs will not inherit + return PermissionResponse.getDefaultResponse(); + } + @Override public void setPublic(boolean isPublic) { organization.setPublic(isPublic); @@ -111,7 +124,7 @@ public void setPublic(boolean isPublic) { @Override public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest req) { - PermissionUpdateResponseBuilder responseBuilder = new PermissionUpdateResponseBuilder(); + FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); switch(req.getAction()) { case MODIFY: @@ -177,12 +190,12 @@ public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest re @Override public PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest req) { - PermissionUpdateResponseBuilder responseBuilder = new PermissionUpdateResponseBuilder(); + FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); switch(req.getAction()) { case MODIFY: for (PermissionUpdateRequest.Permission p: req.getPermissions()) { Pair pair = getGroupAndRole(p); - if (pair.getFirst() == null || pair.getSecond() == null) { + if (pair == null) { continue; } Optional exist = orgGroupPermRepo.findByOrganizationAndGroup(organization, pair.getFirst()); @@ -209,7 +222,7 @@ public PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest r orgGroupPermRepo.deleteByOrganization(organization); for (PermissionUpdateRequest.Permission p: req.getPermissions()) { Pair pair = getGroupAndRole(p); - if (pair.getFirst() == null || pair.getSecond() == null) { + if (pair == null) { continue; } OrgGroupPerm p1 = new OrgGroupPerm(organization, pair.getFirst(), pair.getSecond()); diff --git a/permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultProjectPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java similarity index 92% rename from permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultProjectPermissionsDelegate.java rename to federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java index 5244e15e1..f8ea02b4d 100644 --- a/permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultProjectPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java @@ -1,23 +1,14 @@ -package org.openmbee.mms.permissions.delegation; +package org.openmbee.mms.federatedpersistence.permissions; import org.apache.commons.lang3.builder.EqualsBuilder; -import org.openmbee.mms.core.builders.PermissionUpdateResponseBuilder; -import org.openmbee.mms.core.builders.PermissionUpdatesResponseBuilder; + import org.openmbee.mms.core.config.AuthorizationConstants; import org.openmbee.mms.core.objects.PermissionResponse; import org.openmbee.mms.core.objects.PermissionUpdateRequest; import org.openmbee.mms.core.objects.PermissionUpdateResponse; import org.openmbee.mms.core.objects.PermissionUpdatesResponse; -import org.openmbee.mms.data.domains.global.Group; -import org.openmbee.mms.data.domains.global.OrgGroupPerm; -import org.openmbee.mms.data.domains.global.OrgUserPerm; -import org.openmbee.mms.data.domains.global.Privilege; -import org.openmbee.mms.data.domains.global.Project; -import org.openmbee.mms.data.domains.global.ProjectGroupPerm; -import org.openmbee.mms.data.domains.global.ProjectUserPerm; -import org.openmbee.mms.data.domains.global.Role; -import org.openmbee.mms.data.domains.global.User; -import org.openmbee.mms.permissions.exceptions.PermissionException; +import org.openmbee.mms.data.domains.global.*; +import org.openmbee.mms.federatedpersistence.permissions.exceptions.PermissionException; import org.openmbee.mms.rdb.repositories.OrgGroupPermRepository; import org.openmbee.mms.rdb.repositories.OrgUserPermRepository; import org.openmbee.mms.rdb.repositories.ProjectGroupPermRepository; @@ -88,6 +79,16 @@ public boolean hasPermission(String user, Set groups, String privilege) return false; } + @Override + public boolean hasGroupPermissions(String group, String privilege) { + for (ProjectGroupPerm perm: projectGroupPermRepo.findAllByProject_ProjectId(project.getProjectId())) { + if (perm.getGroup().getName().equals(group) && perm.getRole().equals(privilege)) { + return true; + } + } + return false; + } + @Override public void initializePermissions(String creator) { initializePermissions(creator, false); @@ -120,6 +121,18 @@ public boolean setInherit(boolean isInherit) { return false; } + @Override + public PermissionResponse getInherit() { + PermissionResponse response = PermissionResponse.getDefaultResponse(); + if (project.isInherit()) { + PermissionResponse.Permission permission = new PermissionResponse.Permission(); + permission.setInherited(project.isInherit()); + response.getPermissions().add(permission); + } + return response; + } + + @Override public void setPublic(boolean isPublic) { project.setPublic(isPublic); @@ -128,7 +141,7 @@ public void setPublic(boolean isPublic) { @Override public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest req) { - PermissionUpdateResponseBuilder responseBuilder = new PermissionUpdateResponseBuilder(); + FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); switch(req.getAction()) { case MODIFY: @@ -194,13 +207,13 @@ public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest re @Override public PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest req) { - PermissionUpdateResponseBuilder responseBuilder = new PermissionUpdateResponseBuilder(); + FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); switch(req.getAction()) { case MODIFY: for (PermissionUpdateRequest.Permission p: req.getPermissions()) { Pair pair = getGroupAndRole(p); - if (pair.getFirst() == null || pair.getSecond() == null) { + if (pair == null) { continue; } Optional exist = projectGroupPermRepo.findByProjectAndGroupAndInheritedIsFalse(project, pair.getFirst()); @@ -227,7 +240,7 @@ public PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest r projectGroupPermRepo.deleteByProjectAndInherited(project, false); for (PermissionUpdateRequest.Permission p: req.getPermissions()) { Pair pair = getGroupAndRole(p); - if (pair.getFirst() == null || pair.getSecond() == null) { + if (pair == null) { continue; } ProjectGroupPerm p1 = new ProjectGroupPerm(project, pair.getFirst(), pair.getSecond(), false); @@ -363,7 +376,7 @@ public Collection getAll() { @Override public PermissionUpdatesResponse recalculateInheritedPerms() { - PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); + FederatedPermissionUpdatesResponseBuilder responseBuilder = new FederatedPermissionUpdatesResponseBuilder(); List projectUserPermList = projectUserPermRepo.findAllByProjectAndInherited(project, true); projectUserPermRepo.deleteAll(projectUserPermList); diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionUpdatesResponseBuilder.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionUpdatesResponseBuilder.java new file mode 100644 index 000000000..03cd7271b --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionUpdatesResponseBuilder.java @@ -0,0 +1,76 @@ +package org.openmbee.mms.federatedpersistence.permissions; + +import org.openmbee.mms.core.builders.PermissionUpdatesResponseBuilder; +import org.openmbee.mms.core.objects.PermissionUpdateResponse; +import org.openmbee.mms.core.objects.PermissionUpdatesResponse; + +public class FederatedPermissionUpdatesResponseBuilder extends PermissionUpdatesResponseBuilder { + + private Boolean inherit; + private Boolean isPublic; + private FederatedPermissionsUpdateResponseBuilder usersBuilder = new FederatedPermissionsUpdateResponseBuilder(); + private FederatedPermissionsUpdateResponseBuilder groupsBuilder = new FederatedPermissionsUpdateResponseBuilder(); + + @Override + public FederatedPermissionUpdatesResponseBuilder setInherit(Boolean inherit) { + this.inherit = inherit; + return this; + } + + @Override + public FederatedPermissionUpdatesResponseBuilder setPublic(Boolean aPublic) { + isPublic = aPublic; + return this; + } + + @Override + public FederatedPermissionUpdatesResponseBuilder insert(PermissionUpdatesResponse permissionUpdatesResponse) { + this.inherit = or(this.inherit, permissionUpdatesResponse.getInherit()); + this.isPublic = or(this.isPublic, permissionUpdatesResponse.getPublic()); + this.insertUsers(permissionUpdatesResponse.getUsers()); + this.insertGroups(permissionUpdatesResponse.getGroups()); + return this; + } + + @Override + public FederatedPermissionUpdatesResponseBuilder insertUsers(PermissionUpdateResponse permissionUpdateResponse) { + usersBuilder.insert(permissionUpdateResponse); + return this; + } + + @Override + public FederatedPermissionUpdatesResponseBuilder insertGroups(PermissionUpdateResponse permissionUpdateResponse) { + groupsBuilder.insert(permissionUpdateResponse); + return this; + } + + @Override + public PermissionUpdatesResponse getPermissionUpdatesReponse() { + PermissionUpdatesResponse permissionUpdatesResponse = new PermissionUpdatesResponse(); + permissionUpdatesResponse.setInherit(this.inherit); + permissionUpdatesResponse.setPublic(this.isPublic); + permissionUpdatesResponse.setUsers(usersBuilder.getPermissionUpdateResponse()); + permissionUpdatesResponse.setGroups(groupsBuilder.getPermissionUpdateResponse()); + return permissionUpdatesResponse; + } + + @Override + public FederatedPermissionsUpdateResponseBuilder getUsers() { + return usersBuilder; + } + + @Override + public FederatedPermissionsUpdateResponseBuilder getGroups() { + return groupsBuilder; + } + + private Boolean or(Boolean a, Boolean b) { + if (a == null) { + return b; + } + if (a.equals(b)) { + return a; + } + return a || b; + } +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionsUpdateResponseBuilder.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionsUpdateResponseBuilder.java new file mode 100644 index 000000000..bde43a17e --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionsUpdateResponseBuilder.java @@ -0,0 +1,99 @@ +package org.openmbee.mms.federatedpersistence.permissions; + +import org.openmbee.mms.core.builders.PermissionUpdateResponseBuilder; +import org.openmbee.mms.core.objects.PermissionUpdateResponse; +import org.openmbee.mms.data.domains.global.*; + +import java.util.Collection; + +public class FederatedPermissionsUpdateResponseBuilder extends PermissionUpdateResponseBuilder { + + + public void insertPermissionUpdates_OrgUserPerm(PermissionUpdateResponse.Action action, Collection perms) { + perms.forEach(v -> insertPermissionUpdate(action, v)); + } + + public void insertPermissionUpdate(PermissionUpdateResponse.Action action, OrgUserPerm v) { + if(v == null) + return; + + PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( + action, v.getUser().getUsername(), v.getRole().getName(), v.getOrganization().getOrganizationId(), v.getOrganization().getOrganizationName(), + null, null, null, false); + doInsert(update); + } + + public void insertPermissionUpdates_OrgGroupPerm(PermissionUpdateResponse.Action action, Collection perms) { + perms.forEach(v -> insertPermissionUpdate(action, v)); + } + + public void insertPermissionUpdate(PermissionUpdateResponse.Action action, OrgGroupPerm v) { + if(v == null) + return; + + PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( + action, v.getGroup().getName(), v.getRole().getName(), v.getOrganization().getOrganizationId(), v.getOrganization().getOrganizationName(), + null, null, null, false); + doInsert(update); + } + + public void insertPermissionUpdates_ProjectUserPerm(PermissionUpdateResponse.Action action, Collection perms) { + perms.forEach(v -> insertPermissionUpdate(action, v)); + } + + public void insertPermissionUpdate(PermissionUpdateResponse.Action action, ProjectUserPerm v) { + if(v == null) + return; + + PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( + action, v.getUser().getUsername(), v.getRole().getName(), v.getProject().getOrganization().getOrganizationId(), + v.getProject().getOrganization().getOrganizationName(), v.getProject().getProjectId(), v.getProject().getProjectName(), + null, v.isInherited()); + doInsert(update); + } + + public void insertPermissionUpdates_ProjectGroupPerm(PermissionUpdateResponse.Action action, Collection perms) { + perms.forEach(v -> insertPermissionUpdate(action, v)); + } + + public void insertPermissionUpdate(PermissionUpdateResponse.Action action, ProjectGroupPerm v) { + if(v == null) + return; + + PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( + action, v.getGroup().getName(), v.getRole().getName(), v.getProject().getOrganization().getOrganizationId(), + v.getProject().getOrganization().getOrganizationName(), v.getProject().getProjectId(), v.getProject().getProjectName(), + null, v.isInherited()); + doInsert(update); + } + + public void insertPermissionUpdates_BranchUserPerm(PermissionUpdateResponse.Action action, Collection perms) { + perms.forEach(v -> insertPermissionUpdate(action, v)); + } + + public void insertPermissionUpdate(PermissionUpdateResponse.Action action, BranchUserPerm v) { + if(v == null) + return; + + PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( + action, v.getUser().getUsername(), v.getRole().getName(), v.getBranch().getProject().getOrganization().getOrganizationId(), + v.getBranch().getProject().getOrganization().getOrganizationName(), v.getBranch().getProject().getProjectId(), + v.getBranch().getProject().getProjectName(), v.getBranch().getBranchId(), v.isInherited()); + doInsert(update); + } + + public void insertPermissionUpdates_BranchGroupPerm(PermissionUpdateResponse.Action action, Collection perms) { + perms.forEach(v -> insertPermissionUpdate(action, v)); + } + + public void insertPermissionUpdate(PermissionUpdateResponse.Action action, BranchGroupPerm v) { + if(v == null) + return; + + PermissionUpdateResponse.PermissionUpdate update = new PermissionUpdateResponse.PermissionUpdate( + action, v.getGroup().getName(), v.getRole().getName(), v.getBranch().getProject().getOrganization().getOrganizationId(), + v.getBranch().getProject().getOrganization().getOrganizationName(), v.getBranch().getProject().getProjectId(), + v.getBranch().getProject().getProjectName(),v.getBranch().getBranchId(), v.isInherited()); + doInsert(update); + } +} diff --git a/permissions/src/main/java/org/openmbee/mms/permissions/exceptions/PermissionException.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/exceptions/PermissionException.java similarity index 78% rename from permissions/src/main/java/org/openmbee/mms/permissions/exceptions/PermissionException.java rename to federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/exceptions/PermissionException.java index 5b24c67d5..cf7674088 100644 --- a/permissions/src/main/java/org/openmbee/mms/permissions/exceptions/PermissionException.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/exceptions/PermissionException.java @@ -1,4 +1,4 @@ -package org.openmbee.mms.permissions.exceptions; +package org.openmbee.mms.federatedpersistence.permissions.exceptions; import org.openmbee.mms.core.exceptions.MMSException; import org.springframework.http.HttpStatus; diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/utils/FederatedJsonUtils.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/utils/FederatedJsonUtils.java new file mode 100644 index 000000000..8f325a01c --- /dev/null +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/utils/FederatedJsonUtils.java @@ -0,0 +1,23 @@ +package org.openmbee.mms.federatedpersistence.utils; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +public class FederatedJsonUtils { + + private ObjectMapper objectMapper; + + @Autowired + public void setObjectMapper(ObjectMapper om) { + this.objectMapper = om; + } + + public Map convertToMap(Object obj) { + return objectMapper.convertValue(obj, new TypeReference>() {}); + } +} diff --git a/gradle.properties b/gradle.properties index 646fed7ca..b863c5ddc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,8 @@ springFrameworkVersion=5.3.20 springSecurityVersion=5.7.0 springDataVersion=2.7.0 jacksonVersion=2.13.3 -elasticVersion=7.8.1 \ No newline at end of file +elasticVersion=7.8.1 +jwtTokenVersion=0.10.5 + +org.gradle.daemon=true +org.gradle.parallel=true diff --git a/groups/groups.gradle b/groups/groups.gradle index db3d02ba1..e4dbb7fe3 100644 --- a/groups/groups.gradle +++ b/groups/groups.gradle @@ -1,4 +1,3 @@ dependencies { implementation project(':core') - implementation project(':rdb') } diff --git a/groups/src/main/java/org/openmbee/mms/groups/constants/GroupConstants.java b/groups/src/main/java/org/openmbee/mms/groups/constants/GroupConstants.java index 2e75c4027..148773d16 100644 --- a/groups/src/main/java/org/openmbee/mms/groups/constants/GroupConstants.java +++ b/groups/src/main/java/org/openmbee/mms/groups/constants/GroupConstants.java @@ -6,8 +6,7 @@ public class GroupConstants { public static final String GROUP_NOT_EMPTY = "Group is not empty"; public static final String GROUP_NOT_FOUND = "Group not found"; public static final String INVALID_ACTION = "Invalid action"; - public static final String INVALID_GROUP_NAME = "Invalid group name"; - public static final String NAME = "name"; + public static final String INVALID_GROUP_NAME= "Invalid group name"; public static final String NO_USERS_PROVIDED = "No users provided"; public static final String RESTRICTED_GROUP = "Restricted group"; diff --git a/groups/src/main/java/org/openmbee/mms/groups/controllers/LocalGroupsController.java b/groups/src/main/java/org/openmbee/mms/groups/controllers/LocalGroupsController.java index b4c957553..08a25c67e 100644 --- a/groups/src/main/java/org/openmbee/mms/groups/controllers/LocalGroupsController.java +++ b/groups/src/main/java/org/openmbee/mms/groups/controllers/LocalGroupsController.java @@ -3,22 +3,22 @@ import io.swagger.v3.oas.annotations.tags.Tag; import java.util.ArrayList; -import java.util.List; +import java.util.Collection; import java.util.stream.Collectors; import org.openmbee.mms.core.config.AuthorizationConstants; +import org.openmbee.mms.core.dao.GroupPersistence; +import org.openmbee.mms.core.dao.UserGroupsPersistence; +import org.openmbee.mms.core.dao.UserPersistence; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.exceptions.ConflictException; import org.openmbee.mms.core.exceptions.NotFoundException; -import org.openmbee.mms.data.domains.global.Group; -import org.openmbee.mms.data.domains.global.User; import org.openmbee.mms.groups.constants.GroupConstants; import org.openmbee.mms.groups.objects.*; import org.openmbee.mms.groups.services.GroupValidationService; -import org.openmbee.mms.rdb.repositories.GroupRepository; -import org.openmbee.mms.rdb.repositories.UserRepository; +import org.openmbee.mms.json.GroupJson; +import org.openmbee.mms.json.UserJson; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Sort; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -30,31 +30,31 @@ @Transactional public class LocalGroupsController { - private GroupRepository groupRepository; + private GroupPersistence groupPersistence; + private UserGroupsPersistence userGroupsPersistence; private GroupValidationService groupValidationService; - private UserRepository userRepository; @Autowired - public void setGroupRepository(GroupRepository groupRepository) { - this.groupRepository = groupRepository; + public void setGroupPersistence(GroupPersistence groupPersistence) { + this.groupPersistence = groupPersistence; } @Autowired - public void setGroupValidationService(GroupValidationService groupValidationService) { - this.groupValidationService = groupValidationService; + public void setUserGroupsPersistence(UserGroupsPersistence userGroupsPersistence) { + this.userGroupsPersistence = userGroupsPersistence; } @Autowired - public void setUserRepository(UserRepository userRepository) { - this.userRepository = userRepository; + public void setGroupValidationService(GroupValidationService groupValidationService) { + this.groupValidationService = groupValidationService; } @PutMapping("/{group}") @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) @ResponseBody public void createLocalGroup(@PathVariable String group) { - Group groupObj = groupRepository.findByName(group).orElse(null); - if (groupObj != null) { + GroupJson groupJson = groupPersistence.findByName(group).orElse(null); + if (groupJson != null) { throw new ConflictException(GroupConstants.GROUP_ALREADY_EXISTS); } @@ -62,17 +62,17 @@ public void createLocalGroup(@PathVariable String group) { throw new BadRequestException(GroupConstants.INVALID_GROUP_NAME); } - groupObj = new Group(); - groupObj.setName(group); - groupRepository.saveAndFlush(groupObj); + groupJson = new GroupJson(); + groupJson.setName(group); + groupPersistence.save(groupJson); } @GetMapping @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) public GroupsResponse getAllGroups() { - List groups = groupRepository.findAll(Sort.by(GroupConstants.NAME)); + Collection groups = groupPersistence.findAll(); GroupsResponse response = new GroupsResponse(); - response.setGroups(groups.stream().map(Group::getName).collect(Collectors.toList())); + response.setGroups(groups.stream().map(GroupJson::getName).collect(Collectors.toList())); return response; } @@ -83,7 +83,8 @@ public GroupResponse getGroup(@PathVariable String group) { if(groupValidationService.isRestrictedGroup(group)) { throw new BadRequestException(GroupConstants.RESTRICTED_GROUP); } - return new GroupResponse(groupRepository.findByName(group).orElseThrow(() -> new NotFoundException(GroupConstants.GROUP_NOT_FOUND))); + return new GroupResponse(groupPersistence.findByName(group).orElseThrow(() -> new NotFoundException(GroupConstants.GROUP_NOT_FOUND)), + userGroupsPersistence.findUsersInGroup(group).stream().map(UserJson::getUsername).collect(Collectors.toList())); } @DeleteMapping("/{group}") @@ -91,9 +92,9 @@ public GroupResponse getGroup(@PathVariable String group) { @ResponseBody @Transactional public void deleteLocalGroup(@PathVariable String group) { - Group groupObj = groupRepository.findByName(group).orElseThrow(() -> new NotFoundException(GroupConstants.GROUP_NOT_FOUND)); - if (groupValidationService.canDeleteGroup(groupObj)) { - this.groupRepository.delete(groupObj); + GroupJson groupJson = groupPersistence.findByName(group).orElseThrow(() -> new NotFoundException(GroupConstants.GROUP_NOT_FOUND)); + if (groupValidationService.canDeleteGroup(groupJson)) { + groupPersistence.delete(groupJson); } else { throw new BadRequestException(GroupConstants.GROUP_NOT_EMPTY); } @@ -118,36 +119,28 @@ public GroupUpdateResponse updateGroupUsers(@PathVariable String group, throw new BadRequestException(GroupConstants.RESTRICTED_GROUP); } - Group groupObj = groupRepository.findByName(group).orElseThrow(() -> new NotFoundException(GroupConstants.GROUP_NOT_FOUND)); + if(groupPersistence.findByName(group).isEmpty()) { + throw new NotFoundException(GroupConstants.GROUP_NOT_FOUND); + } GroupUpdateResponse response = new GroupUpdateResponse(); response.setAdded(new ArrayList<>()); response.setRemoved(new ArrayList<>()); response.setRejected(new ArrayList<>()); response.setGroup(group); - groupUpdateRequest.getUsers().forEach(newUser -> { - User user = userRepository.findByUsernameIgnoreCase(newUser).orElse(null); - if (user != null) { - - if (groupUpdateRequest.getAction() == Action.ADD) { - if(groupObj.getUsers().contains(user)){ - response.getRejected().add(newUser); - return; - } - user.getGroups().add(groupObj); - response.getAdded().add(user.getUsername()); - } else { //REMOVE - if(!groupObj.getUsers().contains(user)){ - response.getRejected().add(newUser); - return; - } - user.getGroups().remove(groupObj); - response.getRemoved().add(user.getUsername()); + groupUpdateRequest.getUsers().forEach(user -> { + if (groupUpdateRequest.getAction() == Action.ADD) { + if (!userGroupsPersistence.addUserToGroup(group, user)) { + response.getRejected().add(user); + return; + } + response.getAdded().add(user); + } else { //REMOVE + if (!userGroupsPersistence.removeUserFromGroup(group, user)) { + response.getRejected().add(user); + return; } - userRepository.save(user); - } else { - //Reject users that don't exist - response.getRejected().add(newUser); + response.getRemoved().add(user); } }); return response; diff --git a/groups/src/main/java/org/openmbee/mms/groups/objects/GroupResponse.java b/groups/src/main/java/org/openmbee/mms/groups/objects/GroupResponse.java index edc363694..d6317c14f 100644 --- a/groups/src/main/java/org/openmbee/mms/groups/objects/GroupResponse.java +++ b/groups/src/main/java/org/openmbee/mms/groups/objects/GroupResponse.java @@ -2,11 +2,9 @@ import io.swagger.v3.oas.annotations.media.Schema; -import java.util.List; -import java.util.stream.Collectors; +import java.util.Collection; -import org.openmbee.mms.data.domains.global.Group; -import org.openmbee.mms.data.domains.global.User; +import org.openmbee.mms.json.GroupJson; public class GroupResponse { @@ -14,13 +12,13 @@ public class GroupResponse { private String group; @Schema(nullable = true) - private List users; + private Collection users; public GroupResponse(){} - public GroupResponse(Group group){ + public GroupResponse(GroupJson group, Collection users){ this.group = group.getName(); - this.users = group.getUsers().stream().map(User::getUsername).collect(Collectors.toList()); + this.users = users; } public String getGroup() { @@ -31,11 +29,11 @@ public void setGroup(String group) { this.group = group; } - public List getUsers() { + public Collection getUsers() { return users; } - public void setUsers(List users) { + public void setUsers(Collection users) { this.users = users; } } diff --git a/groups/src/main/java/org/openmbee/mms/groups/services/GroupValidationService.java b/groups/src/main/java/org/openmbee/mms/groups/services/GroupValidationService.java index ae40f307c..01459d0dc 100644 --- a/groups/src/main/java/org/openmbee/mms/groups/services/GroupValidationService.java +++ b/groups/src/main/java/org/openmbee/mms/groups/services/GroupValidationService.java @@ -1,6 +1,8 @@ package org.openmbee.mms.groups.services; -import org.openmbee.mms.data.domains.global.Group; +import org.openmbee.mms.core.dao.UserGroupsPersistence; +import org.openmbee.mms.json.GroupJson; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Set; @@ -14,6 +16,12 @@ public class GroupValidationService { private static final Set RESTRICTED_NAMES = Set.of(MMSADMIN, EVERYONE); private final Pattern VALID_GROUP_NAME_PATTERN = Pattern.compile("^[ -~]+"); + private UserGroupsPersistence userGroupsPersistence; + + @Autowired + public void setUserGroupsPersistence(UserGroupsPersistence userGroupsPersistence) { + this.userGroupsPersistence = userGroupsPersistence; + } public boolean isRestrictedGroup(String groupName) { return RESTRICTED_NAMES.contains(groupName); @@ -25,8 +33,10 @@ public boolean isValidGroupName(String groupName) { VALID_GROUP_NAME_PATTERN.matcher(groupName).matches(); } - public boolean canDeleteGroup(Group group) { - return !isRestrictedGroup(group.getName()) && - (group.getUsers() == null || group.getUsers().isEmpty()); + public boolean canDeleteGroup(GroupJson group) { + if(isRestrictedGroup(group.getName())) { + return false; + } + return userGroupsPersistence.findUsersInGroup(group.getName()).isEmpty(); } } diff --git a/json/src/main/java/org/openmbee/mms/json/BaseJson.java b/json/src/main/java/org/openmbee/mms/json/BaseJson.java index 4c5b0e52f..0d628e022 100644 --- a/json/src/main/java/org/openmbee/mms/json/BaseJson.java +++ b/json/src/main/java/org/openmbee/mms/json/BaseJson.java @@ -22,6 +22,7 @@ public class BaseJson extends HashMap { public static final String CREATED = "_created"; public static final String COMMITID = "_commitId"; public static final String TYPE = "type"; + public static final String IS_DELETED = "deleted"; public String getId() { return (String) this.get(ID); @@ -157,6 +158,18 @@ public T setCommitId(String commitId) { return (T) this; } + @JsonProperty(IS_DELETED) + public String getIsDeleted() { + return (String) this.get(IS_DELETED); + } + + @SuppressWarnings("unchecked") + @JsonProperty(IS_DELETED) + public T setIsDeleted(String deleted) { + this.put(IS_DELETED, deleted); + return (T) this; + } + public boolean isPartialOf(Map o) { return isPartial(this, o); } diff --git a/json/src/main/java/org/openmbee/mms/json/GroupJson.java b/json/src/main/java/org/openmbee/mms/json/GroupJson.java new file mode 100644 index 000000000..f22a57a1b --- /dev/null +++ b/json/src/main/java/org/openmbee/mms/json/GroupJson.java @@ -0,0 +1,11 @@ +package org.openmbee.mms.json; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.v3.oas.annotations.media.Schema; + +@JsonIgnoreProperties({BaseJson.COMMITID, BaseJson.REFID, BaseJson.PROJECTID, BaseJson.TYPE, + BaseJson.ID, "empty"}) +@Schema(name = "Group", requiredProperties = {BaseJson.NAME}) +public class GroupJson extends BaseJson { + +} diff --git a/json/src/main/java/org/openmbee/mms/json/ProjectJson.java b/json/src/main/java/org/openmbee/mms/json/ProjectJson.java index a829788c3..7fdcd852a 100644 --- a/json/src/main/java/org/openmbee/mms/json/ProjectJson.java +++ b/json/src/main/java/org/openmbee/mms/json/ProjectJson.java @@ -10,6 +10,7 @@ public class ProjectJson extends BaseJson { public static final String ORGID = "orgId"; public static final String PROJECTTYPE = "schema"; + public static final String DELETED = "deleted"; @Override public String getProjectId() { diff --git a/json/src/main/java/org/openmbee/mms/json/UserJson.java b/json/src/main/java/org/openmbee/mms/json/UserJson.java new file mode 100644 index 000000000..24bc6b9fe --- /dev/null +++ b/json/src/main/java/org/openmbee/mms/json/UserJson.java @@ -0,0 +1,82 @@ +package org.openmbee.mms.json; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.v3.oas.annotations.media.Schema; + +@JsonIgnoreProperties({BaseJson.COMMITID, BaseJson.REFID, BaseJson.PROJECTID, BaseJson.TYPE, BaseJson.IS_DELETED, + BaseJson.NAME, BaseJson.ID, UserJson.PASSWORD, "empty"}) +@Schema(name = "User", requiredProperties = {UserJson.USERNAME}) +public class UserJson extends BaseJson { + + public static final String USERNAME = "username"; + public static final String FIRST_NAME = "firstName"; + public static final String LAST_NAME = "lastName"; + public static final String ADMIN = "admin"; + public static final String EMAIL = "email"; + public static final String PASSWORD = "password"; + public static final String ENABLED = "enabled"; + + public String getUsername() { + return (String) get(USERNAME); + } + + public UserJson setUsername(String username) { + put(USERNAME, username); + return this; + } + + public String getFirstName() { + return (String) get(FIRST_NAME); + } + + public UserJson setFirstName(String firstName) { + put(FIRST_NAME, firstName); + return this; + } + + public String getLastName() { + return (String) get(LAST_NAME); + } + + public UserJson setLastName(String lastName) { + put(LAST_NAME, lastName); + return this; + } + + public Boolean isAdmin() { + return (Boolean) get(ADMIN); + } + + public UserJson setAdmin(Boolean admin) { + put(ADMIN, admin); + return this; + } + + public String getEmail() { + return (String) get(EMAIL); + } + + public UserJson setEmail(String email) { + put(EMAIL, email); + return this; + } + + public String getPassword() { + return (String) get(PASSWORD); + } + + public UserJson setPassword(String password) { + put(PASSWORD, password); + return this; + } + + public Boolean isEnabled() { + return (Boolean) get(ENABLED); + } + + public UserJson setEnabled(Boolean enabled) { + put(ENABLED, enabled); + return this; + } + +} diff --git a/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterHelper.java b/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterHelper.java index 44d50e208..795b0c9c5 100644 --- a/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterHelper.java +++ b/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterHelper.java @@ -4,11 +4,13 @@ import org.openmbee.mms.jupyter.JupyterConstants; import org.openmbee.mms.jupyter.JupyterNodeType; import org.springframework.stereotype.Component; +import org.openmbee.mms.core.utils.ElementUtils; @Component -public class JupyterHelper { +public class JupyterHelper implements ElementUtils{ - public static JupyterNodeType getNodeType(ElementJson e) { + @Override + public JupyterNodeType getNodeType(ElementJson e) { if (e.containsKey(JupyterConstants.CELLTYPE)) return JupyterNodeType.CELL; return JupyterNodeType.NOTEBOOK; diff --git a/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterNodeService.java b/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterNodeService.java index ab3796061..96e57b6f2 100644 --- a/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterNodeService.java +++ b/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterNodeService.java @@ -5,16 +5,13 @@ import java.util.Map; import java.util.UUID; -import org.openmbee.mms.core.config.ContextHolder; import org.openmbee.mms.core.objects.ElementsRequest; import org.openmbee.mms.core.objects.ElementsResponse; import org.openmbee.mms.core.objects.Rejection; import org.openmbee.mms.core.services.NodeChangeInfo; -import org.openmbee.mms.crud.services.NodeOperation; import org.openmbee.mms.json.ElementJson; import org.openmbee.mms.crud.services.DefaultNodeService; import org.openmbee.mms.core.services.NodeService; -import org.openmbee.mms.data.domains.scoped.Node; import org.openmbee.mms.jupyter.JupyterConstants; import org.openmbee.mms.jupyter.JupyterNodeType; import org.openmbee.mms.jupyter.controllers.NotebooksRequest; @@ -32,21 +29,11 @@ public void setJupyterHelper(JupyterHelper jupyterHelper) { this.jupyterHelper = jupyterHelper; } - @Override - public void extraProcessPostedElement(ElementJson element, Node node, NodeChangeInfo info) { - node.setNodeType(JupyterHelper.getNodeType(element).getValue()); - } - public ElementsResponse readNotebooks(String projectId, String refId, String elementId, Map params) { ElementsRequest req = new ElementsRequest(); List reqs = new ArrayList<>(); if (elementId == null || elementId.isEmpty()) { - ContextHolder.setContext(projectId, refId); - List notebooks = this.nodeRepository - .findAllByDeletedAndNodeType(false, JupyterNodeType.NOTEBOOK.getValue()); - for (Node n : notebooks) { - reqs.add((new ElementJson()).setId(n.getNodeId())); - } + reqs.addAll(getNodePersistence().findAllByNodeType(projectId, refId, null,JupyterNodeType.NOTEBOOK.getValue())); } else { reqs.add((new ElementJson()).setId(elementId)); } @@ -72,7 +59,7 @@ public ElementsResponse readNotebooks(String projectId, String refId, ElementsRe ElementsRequest req2 = new ElementsRequest(); req2.setElements(req2s); ElementsResponse cells = this.read(projectId, refId, req2, params); - Map cellmap = NodeOperation.convertJsonToMap(cells.getElements()); + Map cellmap = convertJsonToMap(cells.getElements()); e.put(JupyterConstants.CELLS, order((List)e.get(JupyterConstants.CELLS), cellmap)); } res.getElements().removeAll(nonNotebooks); diff --git a/ldap/ldap.gradle b/ldap/ldap.gradle index 437b9aee2..588391d59 100644 --- a/ldap/ldap.gradle +++ b/ldap/ldap.gradle @@ -3,3 +3,9 @@ dependencies { implementation commonDependencies.'spring-security-ldap' } + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } +} \ No newline at end of file diff --git a/ldap/src/main/java/org/openmbee/mms/ldap/LdapSecurityConfig.java b/ldap/src/main/java/org/openmbee/mms/ldap/LdapSecurityConfig.java index 03ff63141..fa3666b3b 100644 --- a/ldap/src/main/java/org/openmbee/mms/ldap/LdapSecurityConfig.java +++ b/ldap/src/main/java/org/openmbee/mms/ldap/LdapSecurityConfig.java @@ -1,14 +1,14 @@ package org.openmbee.mms.ldap; +import org.openmbee.mms.core.config.AuthorizationConstants; +import org.openmbee.mms.core.dao.GroupPersistence; +import org.openmbee.mms.core.dao.UserGroupsPersistence; +import org.openmbee.mms.core.dao.UserPersistence; +import org.openmbee.mms.json.GroupJson; +import org.openmbee.mms.json.UserJson; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.*; - -import org.openmbee.mms.core.config.AuthorizationConstants; -import org.openmbee.mms.data.domains.global.Group; -import org.openmbee.mms.rdb.repositories.GroupRepository; -import org.openmbee.mms.rdb.repositories.UserRepository; -import org.openmbee.mms.data.domains.global.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -92,18 +92,23 @@ public class LdapSecurityConfig { @Value("${ldap.user.search.filter:(uid={0})}") private String userSearchFilter; + private UserPersistence userPersistence; + private GroupPersistence groupPersistence; + private UserGroupsPersistence userGroupsPersistence; - private UserRepository userRepository; - private GroupRepository groupRepository; + @Autowired + public void setUserPersistence(UserPersistence userPersistence) { + this.userPersistence = userPersistence; + } @Autowired - public void setUserRepository(UserRepository userRepository) { - this.userRepository = userRepository; + public void setGroupPersistence(GroupPersistence groupPersistence) { + this.groupPersistence = groupPersistence; } @Autowired - public void setGroupRepository(GroupRepository groupRepository) { - this.groupRepository = groupRepository; + public void setUserGroupsPersistence(UserGroupsPersistence userGroupsPersistence) { + this.userGroupsPersistence = userGroupsPersistence; } @Autowired @@ -154,16 +159,16 @@ private CustomLdapAuthoritiesPopulator(BaseLdapPathContextSource ldapContextSour public Collection getGrantedAuthorities( DirContextOperations userData, String username) { logger.debug("Populating authorities using LDAP"); - Optional userOptional = userRepository.findByUsernameIgnoreCase(username); + Optional userOptional = userPersistence.findByUsername(username); if (userOptional.isEmpty()) { logger.info("No user record for {} in the userRepository, creating...", userData.getDn()); - User newUser = createLdapUser(userData); + UserJson newUser = createLdapUser(userData); userOptional = Optional.of(newUser); } - User user = userOptional.get(); - if (user.getModified().isBefore(Instant.now().minus(userAttributesUpdate, ChronoUnit.HOURS))) { + UserJson user = userOptional.get(); + if (user.getModified() != null && Instant.parse(user.getModified()).isBefore(Instant.now().minus(userAttributesUpdate, ChronoUnit.HOURS))) { saveLdapUser(userData, user); } user.setPassword(null); @@ -175,11 +180,10 @@ public Collection getGrantedAuthorities( userDnBuilder.append(providerBase); } String userDn = userDnBuilder.toString(); - - List definedGroups = groupRepository.findAll(); + Collection definedGroups = groupPersistence.findAll(); OrFilter orFilter = new OrFilter(); - for (Group definedGroup : definedGroups) { + for (GroupJson definedGroup : definedGroups) { orFilter.or(new EqualsFilter(groupRoleAttribute, definedGroup.getName())); } @@ -196,28 +200,29 @@ public Collection getGrantedAuthorities( .searchForSingleAttributeValues(groupSearchBase, filter, new Object[]{""}, groupRoleAttribute); logger.debug("LDAP search result: {}", Arrays.toString(memberGroups.toArray())); - Set addGroups = new HashSet<>(); + userPersistence.save(user); + //Add groups to user + + Set addGroups = new HashSet<>(); + for (String memberGroup : memberGroups) { - Optional group = groupRepository.findByName(memberGroup); + Optional group = groupPersistence.findByName(memberGroup); + group.ifPresent(g -> userGroupsPersistence.addUserToGroup(g.getName(), user.getUsername())); group.ifPresent(addGroups::add); } if (logger.isDebugEnabled()) { if ((long) addGroups.size() > 0) { - addGroups.forEach(group -> { - logger.debug("Group received: {}", group.getName()); - }); + addGroups.forEach(group -> logger.debug("Group received: {}", group.getName())); } else { logger.debug("No configured groups returned from LDAP"); } } - user.setGroups(addGroups); - userRepository.save(user); List auths = AuthorityUtils .createAuthorityList(memberGroups.toArray(new String[0])); - if (user.isAdmin()) { + if (Boolean.TRUE.equals(user.isAdmin())) { auths.add(new SimpleGrantedAuthority(AuthorizationConstants.MMSADMIN)); } auths.add(new SimpleGrantedAuthority(AuthorizationConstants.EVERYONE)); @@ -265,7 +270,7 @@ public LdapContextSource contextSource() { return contextSource; } - private User saveLdapUser(DirContextOperations userData, User saveUser) { + private UserJson saveLdapUser(DirContextOperations userData, UserJson saveUser) { if (saveUser.getEmail() == null || !saveUser.getEmail().equals(userData.getStringAttribute(userAttributesEmail)) ) { @@ -285,15 +290,13 @@ private User saveLdapUser(DirContextOperations userData, User saveUser) { return saveUser; } - private User createLdapUser(DirContextOperations userData) { + private UserJson createLdapUser(DirContextOperations userData) { String username = userData.getStringAttribute(userAttributesUsername); logger.debug("Creating user for {} using LDAP", username); - User user = saveLdapUser(userData, new User()); + UserJson user = saveLdapUser(userData, new UserJson()); user.setUsername(username); user.setEnabled(true); user.setAdmin(false); - userRepository.save(user); - - return user; + return userPersistence.save(user); } } \ No newline at end of file diff --git a/localuser/localuser.gradle b/localuser/localuser.gradle index 79103fa6f..eefabc146 100644 --- a/localuser/localuser.gradle +++ b/localuser/localuser.gradle @@ -1,3 +1,9 @@ dependencies { - implementation project(':rdb') + implementation project(':core') } + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } +} \ No newline at end of file diff --git a/localuser/src/main/java/org/openmbee/mms/localuser/config/AuthProviderConfig.java b/localuser/src/main/java/org/openmbee/mms/localuser/config/AuthProviderConfig.java index b96c10577..eb8b08e4e 100644 --- a/localuser/src/main/java/org/openmbee/mms/localuser/config/AuthProviderConfig.java +++ b/localuser/src/main/java/org/openmbee/mms/localuser/config/AuthProviderConfig.java @@ -15,7 +15,7 @@ @Configuration public class AuthProviderConfig { - private static Logger logger = LoggerFactory.getLogger(LocalUserSecurityConfig.class); + private static final Logger logger = LoggerFactory.getLogger(AuthProviderConfig.class); private UserDetailsServiceImpl userDetailsService; private PasswordEncoder passwordEncoder; diff --git a/localuser/src/main/java/org/openmbee/mms/localuser/config/UserPasswordRulesConfig.java b/localuser/src/main/java/org/openmbee/mms/localuser/config/UserPasswordRulesConfig.java new file mode 100644 index 000000000..05cf7cffc --- /dev/null +++ b/localuser/src/main/java/org/openmbee/mms/localuser/config/UserPasswordRulesConfig.java @@ -0,0 +1,16 @@ +package org.openmbee.mms.localuser.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@PropertySource(value = "classpath:application.properties", ignoreResourceNotFound = true) +public class UserPasswordRulesConfig { + @Value("${user.password.allow_self_set_when_blank:false}") + private boolean allowSelfSetPasswordsWhenBlank; + + public boolean isAllowSelfSetPasswordsWhenBlank() { + return allowSelfSetPasswordsWhenBlank; + } +} diff --git a/localuser/src/main/java/org/openmbee/mms/localuser/controllers/LocalUserController.java b/localuser/src/main/java/org/openmbee/mms/localuser/controllers/LocalUserController.java index 4b67a4fc1..5dfcba1cc 100644 --- a/localuser/src/main/java/org/openmbee/mms/localuser/controllers/LocalUserController.java +++ b/localuser/src/main/java/org/openmbee/mms/localuser/controllers/LocalUserController.java @@ -6,7 +6,7 @@ import org.openmbee.mms.core.exceptions.NotFoundException; import org.openmbee.mms.core.exceptions.UnauthorizedException; import org.openmbee.mms.core.utils.AuthenticationUtils; -import org.openmbee.mms.data.domains.global.User; +import org.openmbee.mms.json.UserJson; import org.openmbee.mms.localuser.security.UserCreateRequest; import org.openmbee.mms.localuser.security.UserDetailsServiceImpl; import org.openmbee.mms.localuser.security.UsersResponse; @@ -15,10 +15,14 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; -import java.util.List; +import java.util.Collection; @RestController @Tag(name = "Auth") @@ -44,10 +48,10 @@ public UserCreateRequest createUser(@RequestBody UserCreateRequest req) { } @GetMapping(value = "/users") - @PreAuthorize("isAuthenticated()") + @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) public UsersResponse getUsers(@RequestParam(required = false) String user) { UsersResponse res = new UsersResponse(); - List users = new ArrayList<>(); + Collection users = new ArrayList<>(); if (user != null) { users.add(userDetailsService.loadUserByUsername(user).getUser()); } else { diff --git a/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsImpl.java b/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsImpl.java index f69f9a705..038e5d4da 100644 --- a/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsImpl.java +++ b/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsImpl.java @@ -4,31 +4,33 @@ import java.util.Collection; import org.openmbee.mms.core.config.AuthorizationConstants; -import org.openmbee.mms.data.domains.global.Group; -import org.openmbee.mms.data.domains.global.User; +import org.openmbee.mms.json.GroupJson; +import org.openmbee.mms.json.UserJson; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; public class UserDetailsImpl implements UserDetails { - private final User user; + private final UserJson user; + private final Collection groups; - public UserDetailsImpl(User user) { + public UserDetailsImpl(UserJson user, Collection groups) { this.user = user; + this.groups = groups; } @Override public Collection getAuthorities() { - Collection groups = user.getGroups(); + Collection authorities = new ArrayList<>(); if (groups != null) { - for (Group group : groups) { + for (GroupJson group : groups) { authorities.add(new SimpleGrantedAuthority(group.getName())); } } - if (user.isAdmin()) { + if (Boolean.TRUE.equals(user.isAdmin())) { authorities.add(new SimpleGrantedAuthority(AuthorizationConstants.MMSADMIN)); } authorities.add(new SimpleGrantedAuthority(AuthorizationConstants.EVERYONE)); @@ -62,10 +64,10 @@ public boolean isCredentialsNonExpired() { @Override public boolean isEnabled() { - return user.getEnabled(); + return user.isEnabled(); } - public User getUser() { + public UserJson getUser() { return user; } diff --git a/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsServiceImpl.java b/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsServiceImpl.java index cd9bc1105..e47bbd02c 100644 --- a/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsServiceImpl.java +++ b/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsServiceImpl.java @@ -1,11 +1,13 @@ package org.openmbee.mms.localuser.security; -import java.util.List; +import java.util.Collection; import java.util.Optional; +import org.openmbee.mms.core.dao.UserGroupsPersistence; +import org.openmbee.mms.core.dao.UserPersistence; import org.openmbee.mms.core.exceptions.ForbiddenException; -import org.openmbee.mms.rdb.repositories.UserRepository; -import org.openmbee.mms.data.domains.global.User; +import org.openmbee.mms.json.UserJson; +import org.openmbee.mms.localuser.config.UserPasswordRulesConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -16,12 +18,19 @@ @Service public class UserDetailsServiceImpl implements UserDetailsService { - private UserRepository userRepository; + private UserPersistence userPersistence; + private UserGroupsPersistence userGroupsPersistence; private PasswordEncoder passwordEncoder; + private UserPasswordRulesConfig userPasswordRulesConfig; @Autowired - public void setUserRepository(UserRepository userRepository) { - this.userRepository = userRepository; + public void setUserPersistence(UserPersistence userPersistence) { + this.userPersistence = userPersistence; + } + + @Autowired + public void setUserGroupsPersistence(UserGroupsPersistence userGroupsPersistence) { + this.userGroupsPersistence = userGroupsPersistence; } @Autowired @@ -29,53 +38,59 @@ public void setPasswordEncoder(PasswordEncoder passwordEncoder) { this.passwordEncoder = passwordEncoder; } + @Autowired + public void setUserPasswordRulesConfig(UserPasswordRulesConfig userPasswordRulesConfig) { + this.userPasswordRulesConfig = userPasswordRulesConfig; + } + @Override public UserDetailsImpl loadUserByUsername(String username) throws UsernameNotFoundException { - Optional user = userRepository.findByUsernameIgnoreCase(username); + Optional user = userPersistence.findByUsername(username); - if (!user.isPresent()) { + if (user.isEmpty()) { throw new UsernameNotFoundException( String.format("No user found with username '%s'.", username)); } - return new UserDetailsImpl(user.get()); + return new UserDetailsImpl(user.get(), userGroupsPersistence.findGroupsAssignedToUser(username)); } - public List getUsers() { - return userRepository.findAll(); + public Collection getUsers() { + return userPersistence.findAll(); } @Transactional - public User register(UserCreateRequest req) { - User user = new User(); + public UserJson register(UserCreateRequest req) { + UserJson user = new UserJson(); + user.setUsername(req.getUsername()); user.setEmail(req.getEmail()); user.setFirstName(req.getFirstname()); user.setLastName(req.getLastname()); - user.setUsername(req.getUsername()); user.setPassword(encodePassword(req.getPassword())); user.setEnabled(true); user.setAdmin(req.isAdmin()); - return userRepository.save(user); + return userPersistence.save(user); } @Transactional public void changeUserPassword(String username, String password, boolean asAdmin) { - Optional userOptional = userRepository.findByUsernameIgnoreCase(username); - if(! userOptional.isPresent()) { + Optional userOptional = userPersistence.findByUsername(username); + if(userOptional.isEmpty()) { throw new UsernameNotFoundException( String.format("No user found with username '%s'.", username)); } - User user = userOptional.get(); - if(!asAdmin && (user.getPassword() == null || user.getPassword().isEmpty())) { + UserJson user = userOptional.get(); + if(!asAdmin && !userPasswordRulesConfig.isAllowSelfSetPasswordsWhenBlank() && + (user.getPassword() == null || user.getPassword().isBlank())) { throw new ForbiddenException("Cannot change or set passwords for external users."); } //TODO password strength test? user.setPassword(encodePassword(password)); - userRepository.save(user); + userPersistence.save(user); } private String encodePassword(String password) { - return (password != null && !password.isEmpty()) ? passwordEncoder.encode(password) : null; + return (password != null && !password.isBlank()) ? passwordEncoder.encode(password) : null; } } diff --git a/localuser/src/main/java/org/openmbee/mms/localuser/security/UsersResponse.java b/localuser/src/main/java/org/openmbee/mms/localuser/security/UsersResponse.java index f119e7cf8..db931cb8d 100644 --- a/localuser/src/main/java/org/openmbee/mms/localuser/security/UsersResponse.java +++ b/localuser/src/main/java/org/openmbee/mms/localuser/security/UsersResponse.java @@ -1,17 +1,17 @@ package org.openmbee.mms.localuser.security; -import java.util.List; -import org.openmbee.mms.data.domains.global.User; +import java.util.Collection; +import org.openmbee.mms.json.UserJson; public class UsersResponse { - private List users; + private Collection users; - public List getUsers() { + public Collection getUsers() { return users; } - public void setUsers(List users) { + public void setUsers(Collection users) { this.users = users; } } diff --git a/msosa/README.md b/msosa/README.md new file mode 100644 index 000000000..85cf02eb7 --- /dev/null +++ b/msosa/README.md @@ -0,0 +1 @@ +## MSOSA \ No newline at end of file diff --git a/msosa/msosa.gradle b/msosa/msosa.gradle new file mode 100644 index 000000000..8884015e7 --- /dev/null +++ b/msosa/msosa.gradle @@ -0,0 +1,6 @@ +dependencies { + implementation project(':crud') + implementation project(':view') + + testImplementation commonDependencies.'spring-boot-starter-test' +} diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/MsosaConstants.java b/msosa/src/main/java/org/openmbee/mms/msosa/MsosaConstants.java new file mode 100644 index 000000000..86e9ae2b9 --- /dev/null +++ b/msosa/src/main/java/org/openmbee/mms/msosa/MsosaConstants.java @@ -0,0 +1,136 @@ +package org.openmbee.mms.msosa; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class MsosaConstants { + + private MsosaConstants() { + throw new IllegalStateException("msosa Constants"); + } + + public static final String AGGREGATION = "aggregation"; + public static final String ASSOCIATIONENDID = "associationEndId"; + public static final String ASSOCIATIONID = "associationId"; + public static final String CLASSIFIERIDS = "classifierIds"; + public static final String CLIENTDEPENDENCYIDS = "clientDependencyIds"; + public static final String COLLABORATIONUSEIDS = "collaborationUseIds"; + public static final String COMMITID = "commitId"; + public static final String DATATYPEID = "datatypeId"; + public static final String DEFAULTVALUE = "defaultValue"; + public static final String DEFININGFEATUREID = "definingFeatureId"; + public static final String DEPLOYMENTIDS = "deploymentIds"; + public static final String DESCRIPTION = "description"; + public static final String DOCUMENTATION = "documentation"; + public static final String ELEMENTIMPORTIDS = "elementImportIds"; + public static final String ENDAPATHIDS = "endAPathIds"; + public static final String ENDBPATHIDS = "endBPathIds"; + public static final String ENDIDS = "endIds"; + public static final String GENERALIZATIONIDS = "generalizationIds"; + public static final String INTERFACEID = "interfaceId"; + public static final String ISABSTRACT = "isAbstract"; + public static final String ISDERIVED = "isDerived"; + public static final String ISDERIVEDUNION = "isDerivedUnion"; + public static final String ISFINALSPECIALIZATION = "isFinalSpecialization"; + public static final String ISID = "isID"; + public static final String ISLEAF = "isLeaf"; + public static final String ISORDERED = "isOrdered"; + public static final String ISREADONLY = "isReadOnly"; + public static final String ISSTATIC = "isStatic"; + public static final String ISUNIQUE = "isUnique"; + public static final String LOWERVALUE = "lowerValue"; + public static final String KEYWORDS = "keywords"; + public static final String MDEXTENSIONSIDS = "mdExtensionsIds"; + public static final String MEMBERENDIDS = "memberEndIds"; + public static final String METADATA = "metadata"; + public static final String MOUNTEDELEMENTPROJECTID = "mountedElementProjectId"; + public static final String MOUNTEDREFID = "mountedRefId"; + public static final String NAME = "name"; + public static final String NAMEEXPRESSION = "nameExpression"; + public static final String NAVIGABLEOWNEDENDIDS = "navigableOwnedEndIds"; + public static final String NONE = "none"; + public static final String OWNEDATTRIBUTEIDS = "ownedAttributeIds"; + public static final String OWNEDENDIDS = "ownedEndIds"; + public static final String OWNERID = "ownerId"; + public static final String PACKAGEIMPORTIDS = "packageImportIds"; + public static final String PACKAGEMERGEIDS = "packageMergeIds"; + public static final String PARENTID = "_parentId"; + public static final String POWERTYPEEXTENTIDS = "powertypeExtentIds"; + public static final String PROPERTY = "Property"; + public static final String PROPERTYID = "propertyId"; + public static final String PROPERTYTYPE = "propertyType"; + public static final String PROFILEAPPLICATIONIDS = "profileApplicationIds"; + public static final String QUALIFIERIDS = "qualifierIds"; + public static final String REDEFINEDCLASSIFIERIDS = "redefinedClassifierIds"; + public static final String REDEFINEDPROPERTYIDS = "redefinedPropertyIds"; + public static final String REPRESENTATIONID = "representationId"; + public static final String SPECIFICATION = "specification"; + public static final String STEREOTYPEDELEMENTID = "stereotypedElementId"; + public static final String SUBSETTEDPROPERTYIDS = "subsettedPropertyIds"; + public static final String SUBSTITUTIONIDS = "substitutionIds"; + public static final String SUPPLIERDEPENDENCYIDS = "supplierDependencyIds"; + public static final String SYNCELEMENTID = "syncElementId"; + public static final String TARGET = "targetId"; + public static final String TEMPLATEBINDINGIDS = "templateBindingIds"; + public static final String TEMPLATEPARAMETERID = "templateParameterId"; + public static final String TYPE = "type"; + public static final String TYPEID = "typeId"; + public static final String UPPERVALUE = "upperValue"; + public static final String URI = "URI"; + public static final String USECASEIDS = "useCaseIds"; + public static final String VALUEID = "valueId"; + public static final String VISIBILITY = "visibility"; + public static final String APPLIEDSTEREOTYPEIDS = "_appliedStereotypeIds"; + public static final String CHILDVIEWS = "_childViews"; + public static final String CONTENTS = "_contents"; + public static final String ISGROUP = "_isGroup"; + public static final String MOUNTS = "_mounts"; + public static final String PARENTVIEWS = "_parentViews"; + public static final String PROPERTIES = "_properties"; + public static final String QUALIFIEDID = "_qualifiedId"; + public static final String QUALIFIEDNAME = "_qualifiedName"; + public static final String RELATEDDOCUMENTS = "_relatedDocuments"; + public static final String SITECHARACTERIZATIONID = "_groupId"; + public static final String SITES = "_sites"; + public static final String HOLDING_BIN_PREFIX = "holding_bin_"; + public static final String VIEW_INSTANCES_BIN_PREFIX = "view_instances_bin_"; + public static final String PACKAGE_TYPE = "Package"; + public static final String PUBLIC_VISIBILITY = "public"; + public static final String DOCUMENTSID = "_17_0_2_3_87b0275_1371477871400_792964_43374"; + + public static final Map STEREOTYPEIDS; + static { + STEREOTYPEIDS = new HashMap<>(); + STEREOTYPEIDS.put(DOCUMENTSID, "document"); + STEREOTYPEIDS.put("_17_0_1_232f03dc_1325612611695_581988_21583", "view"); + STEREOTYPEIDS.put("_11_5EAPbeta_be00301_1147420760998_43940_227", "view"); + STEREOTYPEIDS.put("_18_0beta_9150291_1392290067481_33752_4359", "view"); + STEREOTYPEIDS.put("_17_0_1_407019f_1332453225141_893756_11936", "view"); + STEREOTYPEIDS.put("_17_0_2_3_407019f_1389807639137_860750_29082", "conforms"); + STEREOTYPEIDS.put("_16_5_4_409a058d_1259862803278_226185_1083", "exposes"); + STEREOTYPEIDS.put("_17_0_5_1_8660276_1407362513794_939259_26181", "characterizes"); + } + + public static final Map PROPERTYSIDS; + static { + PROPERTYSIDS = new HashMap<>(); + PROPERTYSIDS.put("composite", "_15_0_be00301_1199377756297_348405_2678"); + PROPERTYSIDS.put("none", "_15_0_be00301_1199378032543_992832_3096"); + PROPERTYSIDS.put("shared", "_15_0_be00301_1199378020836_340320_3071"); + } + + public static final String VALUEPROPERTY = "_12_0_be00301_1164123483951_695645_2041"; + public static final String CONSTRAINTPROPERTY = "_11_5EAPbeta_be00301_1147767840464_372327_467"; + + public static final Set VIEWSIDS; + static { + VIEWSIDS = new HashSet<>(); + VIEWSIDS.add(DOCUMENTSID); + VIEWSIDS.add("_17_0_1_232f03dc_1325612611695_581988_21583"); + VIEWSIDS.add("_11_5EAPbeta_be00301_1147420760998_43940_227"); + VIEWSIDS.add("_18_0beta_9150291_1392290067481_33752_4359"); + VIEWSIDS.add("_17_0_1_407019f_1332453225141_893756_11936"); + } +} diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/MsosaNodeType.java b/msosa/src/main/java/org/openmbee/mms/msosa/MsosaNodeType.java new file mode 100644 index 000000000..5ddcf3e62 --- /dev/null +++ b/msosa/src/main/java/org/openmbee/mms/msosa/MsosaNodeType.java @@ -0,0 +1,47 @@ +package org.openmbee.mms.msosa; + +import java.util.HashMap; +import java.util.Map; + +public enum MsosaNodeType { + ELEMENT(1), + SITE(2), + PROJECT(3), + DOCUMENT(4), + COMMENT(5), + CONSTRAINT(6), + INSTANCESPECIFICATION(7), + OPERATION(8), + PACKAGE(9), + PROPERTY(10), + PARAMETER(11), + VIEW(12), + VIEWPOINT(13), + GROUP(14), + HOLDINGBIN(15), + PROJECTUSAGE(16); + + private static Map map = new HashMap<>(); + + static { + for (MsosaNodeType n : MsosaNodeType.values()) { + map.put(n.value, n); + } + } + + private int value; + + MsosaNodeType(int value) { + this.value = value; + } + + public static MsosaNodeType valueOf(int n) { + return map.get(n); + } + + public int getValue() { + return value; + } +} + + diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/MsosaSchemaConfig.java b/msosa/src/main/java/org/openmbee/mms/msosa/MsosaSchemaConfig.java new file mode 100644 index 000000000..7162f3fb6 --- /dev/null +++ b/msosa/src/main/java/org/openmbee/mms/msosa/MsosaSchemaConfig.java @@ -0,0 +1,15 @@ +package org.openmbee.mms.msosa; + +import org.openmbee.mms.core.config.ProjectSchemas; +import org.openmbee.mms.json.SchemaJson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MsosaSchemaConfig { + + @Autowired + public void registerSchema(ProjectSchemas schemas) { + schemas.addSchema(new SchemaJson().setName("msosa").setDescription("msosa/md json handling, creates holding bins")); + } +} diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaCommitService.java b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaCommitService.java new file mode 100644 index 000000000..b8cb8e26c --- /dev/null +++ b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaCommitService.java @@ -0,0 +1,17 @@ +package org.openmbee.mms.msosa.services; + +import org.openmbee.mms.core.services.CommitService; +import org.openmbee.mms.crud.services.DefaultCommitService; +import org.openmbee.mms.json.CommitJson; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service("msosaCommitService") +public class MsosaCommitService extends DefaultCommitService implements CommitService { + @Override + public boolean isProjectNew(String projectId) { + List commits = commitPersistence.findAllByProjectId(projectId); + return commits == null || commits.size() <= 1; // a msosa project gets 1 auto commit, so its still "new" + } +} diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaHelper.java b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaHelper.java new file mode 100644 index 000000000..ac8b62272 --- /dev/null +++ b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaHelper.java @@ -0,0 +1,161 @@ +package org.openmbee.mms.msosa.services; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.openmbee.mms.msosa.MsosaConstants; +import org.openmbee.mms.msosa.MsosaNodeType; +import org.openmbee.mms.core.utils.ElementUtils; +import org.openmbee.mms.json.ElementJson; +import org.springframework.stereotype.Component; + +@Component +public class MsosaHelper implements ElementUtils{ + + @Override + public MsosaNodeType getNodeType(ElementJson e) { + if (isDocument(e)) { + return MsosaNodeType.DOCUMENT; + } + if (isView(e)) { + return MsosaNodeType.VIEW; + } + if (isGroup(e)) { + return MsosaNodeType.GROUP; + } + if (e.getId().contains("holding_bin") || e.getId().contains("view_instances_bin")) { + return MsosaNodeType.HOLDINGBIN; + } + String type = e.getType(); + if (type == null) { + return MsosaNodeType.ELEMENT; + } + switch (type) { + case "Mount": + return MsosaNodeType.PROJECTUSAGE; + case "InstanceSpecification": + return MsosaNodeType.INSTANCESPECIFICATION; + case "Constraint": + return MsosaNodeType.CONSTRAINT; + case "Package": + return MsosaNodeType.PACKAGE; + case "Property": + return MsosaNodeType.PROPERTY; + case "Parameter": + return MsosaNodeType.PARAMETER; + default: + } + return MsosaNodeType.ELEMENT; + } + + public boolean isView(ElementJson e) { + List sids = (List)e.get(MsosaConstants.APPLIEDSTEREOTYPEIDS); + if (sids != null && !sids.isEmpty()) { + Set ids = new HashSet<>(sids); + ids.retainAll(MsosaConstants.VIEWSIDS); + return !ids.isEmpty(); + } + return false; + } + + public boolean isDocument(ElementJson e) { + List sids = (List)e.get(MsosaConstants.APPLIEDSTEREOTYPEIDS); + if (sids != null && sids.contains(MsosaConstants.DOCUMENTSID)) { + return true; + } + return false; + } + + public boolean isGroup(ElementJson e) { + Boolean isGroup = (Boolean)e.get(MsosaConstants.ISGROUP); + if (isGroup != null) { + return isGroup; + } + return false; + } + + public ElementJson createProperty(String id, String name, String ownerId, String aggregation, String typeId, String assocId) { + ElementJson res = new ElementJson(); + res.put(ElementJson.ID, id); + res.put(MsosaConstants.NAME, name); + res.put(MsosaConstants.NAMEEXPRESSION, null); + res.put(MsosaConstants.TYPE, "Property"); + res.put(MsosaConstants.OWNERID, ownerId); + res.put(MsosaConstants.TYPEID, typeId); + res.put(MsosaConstants.AGGREGATION, aggregation); + res.put(MsosaConstants.ASSOCIATIONID, assocId); + List asIds = new ArrayList<>(); + res.put(MsosaConstants.APPLIEDSTEREOTYPEIDS, asIds); + res.put(MsosaConstants.DOCUMENTATION, ""); + res.put(MsosaConstants.MDEXTENSIONSIDS, new ArrayList()); + res.put(MsosaConstants.SYNCELEMENTID, null); + res.put(MsosaConstants.CLIENTDEPENDENCYIDS, new ArrayList()); + res.put(MsosaConstants.SUPPLIERDEPENDENCYIDS, new ArrayList()); + res.put(MsosaConstants.VISIBILITY, "private"); + res.put(MsosaConstants.ISLEAF, false); + res.put(MsosaConstants.ISSTATIC, false); + res.put(MsosaConstants.ISORDERED, false); + res.put(MsosaConstants.ISUNIQUE, true); + res.put(MsosaConstants.LOWERVALUE, null); + res.put(MsosaConstants.UPPERVALUE, null); + res.put(MsosaConstants.ISREADONLY, false); + res.put(MsosaConstants.TEMPLATEPARAMETERID, null); + res.put(MsosaConstants.ENDIDS, new ArrayList()); + res.put(MsosaConstants.DEPLOYMENTIDS, new ArrayList()); + res.put(MsosaConstants.ASSOCIATIONENDID, null); + res.put(MsosaConstants.QUALIFIERIDS, new ArrayList()); + res.put(MsosaConstants.DATATYPEID, null); + res.put(MsosaConstants.DEFAULTVALUE, null); + res.put(MsosaConstants.INTERFACEID, null); + res.put(MsosaConstants.ISDERIVED, false); + res.put(MsosaConstants.ISDERIVEDUNION, false); + res.put(MsosaConstants.ISID, false); + res.put(MsosaConstants.REDEFINEDPROPERTYIDS, new ArrayList()); + res.put(MsosaConstants.SUBSETTEDPROPERTYIDS, new ArrayList()); + return res; + } + + public ElementJson createAssociation(String id, String ownerId, String ownedEnd, String memberEnd2) { + ElementJson association = new ElementJson(); + List memberEndIds = new ArrayList<>(); + memberEndIds.add(ownedEnd); + memberEndIds.add(memberEnd2); + List ownedEndIds = new ArrayList<>(); + ownedEndIds.add(ownedEnd); + + association.put(ElementJson.ID, id); + association.put(MsosaConstants.NAME, ""); + association.put(MsosaConstants.NAMEEXPRESSION, null); + association.put(MsosaConstants.TYPE, "Association"); + association.put(MsosaConstants.OWNERID, ownerId); + association.put(MsosaConstants.MEMBERENDIDS, memberEndIds); + association.put(MsosaConstants.OWNEDENDIDS, ownedEndIds); + // Default Fields + association.put(MsosaConstants.DOCUMENTATION, ""); + association.put(MsosaConstants.MDEXTENSIONSIDS, new ArrayList()); + association.put(MsosaConstants.SYNCELEMENTID, null); + association.put(MsosaConstants.APPLIEDSTEREOTYPEIDS, new ArrayList()); + association.put(MsosaConstants.CLIENTDEPENDENCYIDS, new ArrayList()); + association.put(MsosaConstants.SUPPLIERDEPENDENCYIDS, new ArrayList()); + association.put(MsosaConstants.NAMEEXPRESSION, null); + association.put(MsosaConstants.VISIBILITY, "public"); + association.put(MsosaConstants.TEMPLATEPARAMETERID, null); + association.put(MsosaConstants.ELEMENTIMPORTIDS, new ArrayList()); + association.put(MsosaConstants.PACKAGEIMPORTIDS, new ArrayList()); + association.put(MsosaConstants.ISLEAF, false); + association.put(MsosaConstants.TEMPLATEBINDINGIDS, new ArrayList()); + association.put(MsosaConstants.USECASEIDS, new ArrayList()); + association.put(MsosaConstants.REPRESENTATIONID, null); + association.put(MsosaConstants.COLLABORATIONUSEIDS, new ArrayList()); + association.put(MsosaConstants.GENERALIZATIONIDS, new ArrayList()); + association.put(MsosaConstants.POWERTYPEEXTENTIDS, new ArrayList()); + association.put(MsosaConstants.ISABSTRACT, false); + association.put(MsosaConstants.ISFINALSPECIALIZATION, false); + association.put(MsosaConstants.REDEFINEDCLASSIFIERIDS, new ArrayList()); + association.put(MsosaConstants.SUBSTITUTIONIDS, new ArrayList()); + association.put(MsosaConstants.ISDERIVED, false); + association.put(MsosaConstants.NAVIGABLEOWNEDENDIDS, new ArrayList()); + return association; + } +} diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaNodeService.java b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaNodeService.java new file mode 100644 index 000000000..0f43c54fe --- /dev/null +++ b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaNodeService.java @@ -0,0 +1,128 @@ +package org.openmbee.mms.msosa.services; + +import org.openmbee.mms.core.config.ContextHolder; +import org.openmbee.mms.core.config.Privileges; +import org.openmbee.mms.core.objects.ElementsRequest; +import org.openmbee.mms.core.objects.ElementsResponse; +import org.openmbee.mms.core.security.MethodSecurityService; +import org.openmbee.mms.core.services.HierarchicalNodeService; +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.crud.services.DefaultNodeService; +import org.openmbee.mms.json.ElementJson; +import org.openmbee.mms.json.MountJson; +import org.openmbee.mms.msosa.MsosaConstants; +import org.openmbee.mms.msosa.MsosaNodeType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.util.Pair; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service("msosaNodeService") +public class MsosaNodeService extends DefaultNodeService implements HierarchicalNodeService { + + protected MsosaHelper msosaHelper; + private MethodSecurityService mss; + + @Autowired + public void setMsosaHelper(MsosaHelper msosaHelper) { + this.msosaHelper = msosaHelper; + } + + @Autowired + public void setMss(MethodSecurityService mss) { + this.mss = mss; + } + + @Override + public ElementsResponse read(String projectId, String refId, ElementsRequest req, + Map params) { + + String commitId = params.getOrDefault(MsosaConstants.COMMITID, null); + NodeGetInfo info = nodePersistence.findAll(projectId, refId, commitId, req.getElements()); + + if (!info.getRejected().isEmpty()) { + //continue looking in visible mounted projects for elements if not all found + NodeGetInfo curInfo = info; + List> usages = new ArrayList<>(); + getProjectUsages(projectId, refId, commitId, usages, true); + + int i = 1; //0 is entry project, already gotten + while (!curInfo.getRejected().isEmpty() && i < usages.size()) { + ElementsRequest reqNext = buildRequest(curInfo.getRejected().keySet()); + //TODO use the right commitId in child if commitId is present in params + curInfo = getNodePersistence().findAll(usages.get(i).getFirst(), usages.get(i).getSecond(), "", reqNext.getElements()); + info.getActiveElementMap().putAll(curInfo.getActiveElementMap()); + curInfo.getActiveElementMap().forEach((id, json) -> info.getRejected().remove(id)); + curInfo.getRejected().forEach((id, rejection) -> { + if (info.getRejected().containsKey(id) && rejection.getCode() == 410) { + info.getRejected().put(id, rejection); //deleted element is better than not found + } + }); + i++; + } + } + + ElementsResponse response = new ElementsResponse(); + response.getElements().addAll(info.getActiveElementMap().values()); + response.setRejected(new ArrayList<>(info.getRejected().values())); + return response; + } + + @Override + public void extraProcessPostedElement(NodeChangeInfo info, ElementJson element) { + //remove _childViews if posted + element.remove(MsosaConstants.CHILDVIEWS); + } + + public MountJson getProjectUsages(String projectId, String refId, String commitId, List> saw, + boolean restrictOnPermissions) { + ContextHolder.setContext(projectId, refId); + saw.add(Pair.of(projectId, refId)); + List mountsJson = nodePersistence.findAllByNodeType(projectId,refId,commitId,MsosaNodeType.PROJECTUSAGE.getValue()); + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + List mountValues = new ArrayList<>(); + + for (ElementJson mount: mountsJson) { + String mountedProjectId = (String)mount.get(MsosaConstants.MOUNTEDELEMENTPROJECTID); + String mountedRefId = (String)mount.get(MsosaConstants.MOUNTEDREFID); + if(mountedProjectId == null){ + logger.error("Could not find Mounted Project Id"); + continue; + } + if(mountedRefId == null){ + logger.error("Could not find Mounted Ref Id for Project ID: {}" ,mountedProjectId); + continue; + } + if (saw.contains(Pair.of(mountedProjectId, mountedRefId))) { + //prevent circular dependencies or dups - should it be by project or by project and ref? + continue; + } + try { + if (restrictOnPermissions && !mss.hasBranchPrivilege(auth, mountedProjectId, mountedRefId, + Privileges.BRANCH_READ.name(), true)) { + //should permission be considered here? + continue; + } + } catch (Exception e) { + continue; + } + //doing a depth first traversal TODO get appropriate commitId + try { + mountValues.add(getProjectUsages(mountedProjectId, mountedRefId, "", saw, restrictOnPermissions)); + } catch (Exception e) { + //log the error and move on + logger.debug(String.format("Could not get project usages from nested project %s" , mountedProjectId), e); + } + } + MountJson res = new MountJson(); + res.setId(projectId); + res.setProjectId(projectId); + res.setRefId(refId); + res.put(MsosaConstants.MOUNTS, mountValues); + return res; + } +} diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaProjectService.java b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaProjectService.java new file mode 100644 index 000000000..bd453ea3d --- /dev/null +++ b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaProjectService.java @@ -0,0 +1,67 @@ +package org.openmbee.mms.msosa.services; + +import org.openmbee.mms.msosa.MsosaConstants; +import org.openmbee.mms.core.config.Constants; +import org.openmbee.mms.core.objects.ElementsRequest; +import org.openmbee.mms.core.services.NodeService; +import org.openmbee.mms.core.services.ProjectService; +import org.openmbee.mms.crud.services.DefaultProjectService; +import org.openmbee.mms.json.ElementJson; +import org.openmbee.mms.json.ProjectJson; +import org.openmbee.mms.json.RefJson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; + + +@Service("msosaProjectService") +public class MsosaProjectService extends DefaultProjectService implements ProjectService { + + private NodeService nodeService; + + @Autowired + @Qualifier("msosaNodeService") + public void setNodeService(NodeService nodeService) { + this.nodeService = nodeService; + } + + @Override + public ProjectJson create(ProjectJson project) { + ProjectJson projectJson = super.create(project); + + ElementJson projectHoldingBin = createNode(MsosaConstants.HOLDING_BIN_PREFIX + projectJson.getProjectId(), + "Holding Bin", projectJson); + + ElementJson viewInstanceBin = createNode(MsosaConstants.VIEW_INSTANCES_BIN_PREFIX + projectJson.getProjectId(), + "View Instances Bin", projectJson); + + ElementsRequest elementsRequest = new ElementsRequest(); + elementsRequest.setElements(List.of(projectHoldingBin, viewInstanceBin)); + nodeService.createOrUpdate(projectJson.getProjectId(), Constants.MASTER_BRANCH, elementsRequest, + Collections.EMPTY_MAP, projectJson.getCreator()); + + return projectJson; + } + + public RefJson createMasterRefJson(ProjectJson project){ + RefJson branchJson = super.createMasterRefJson(project); + branchJson.put("twcId",Constants.MASTER_BRANCH); + return branchJson; + + } + + private static ElementJson createNode(String id, String name, ProjectJson projectJson) { + ElementJson e = new ElementJson(); + e.setId(id); + e.setName(name); + e.put(MsosaConstants.OWNERID, projectJson.getProjectId()); + e.put(MsosaConstants.TYPE, MsosaConstants.PACKAGE_TYPE); + e.put(MsosaConstants.ISGROUP, false); + e.put(MsosaConstants.DOCUMENTATION, ""); + e.put(MsosaConstants.VISIBILITY, MsosaConstants.PUBLIC_VISIBILITY); + return e; + } +} diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaViewService.java b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaViewService.java new file mode 100644 index 000000000..55f7c0f83 --- /dev/null +++ b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaViewService.java @@ -0,0 +1,258 @@ +package org.openmbee.mms.msosa.services; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import org.openmbee.mms.msosa.MsosaConstants; +import org.openmbee.mms.msosa.MsosaNodeType; +import org.openmbee.mms.core.config.ContextHolder; +import org.openmbee.mms.core.objects.ElementsRequest; +import org.openmbee.mms.core.objects.ElementsResponse; +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.json.ElementJson; +import org.openmbee.mms.view.services.PropertyData; +import org.openmbee.mms.view.services.ViewService; +import org.springframework.stereotype.Service; + +@Service("msosaViewService") +public class MsosaViewService extends MsosaNodeService implements ViewService { + + @Override + public ElementsResponse getDocuments(String projectId, String refId, Map params) { + ContextHolder.setContext(projectId, refId); + String commitId = params.getOrDefault(MsosaConstants.COMMITID, null); + List documents = getNodePersistence().findAllByNodeType(projectId, refId, commitId, MsosaNodeType.DOCUMENT.getValue()); + for (ElementJson e: documents) { + Optional parent = getFirstRelationshipOfType(projectId, refId, commitId, e, + Arrays.asList(MsosaNodeType.GROUP.getValue()), MsosaConstants.OWNERID); + if (parent.isPresent()) { + e.put(MsosaConstants.SITECHARACTERIZATIONID, parent.get().getId()); + } + } + return new ElementsResponse().setElements(documents); + } + + @Override + public ElementsResponse getView(String projectId, String refId, String elementId, Map params) { + return this.getViews(projectId, refId, buildRequest(elementId), params); + } + + @Override + public ElementsResponse getViews(String projectId, String refId, ElementsRequest req, Map params) { + ElementsResponse res = this.read(projectId, refId, req, params); + addChildViews(res, params); + return res; + } + + @Override + public void addChildViews(ElementsResponse res, Map params) { + for (ElementJson element: res.getElements()) { + if (msosaHelper.isView(element)) { + List ownedAttributeIds = (List) element.get(MsosaConstants.OWNEDATTRIBUTEIDS); + ElementsResponse ownedAttributes = this.read(element.getProjectId(), element.getRefId(), + buildRequest(ownedAttributeIds), params); + List filtered = filter(ownedAttributeIds, ownedAttributes.getElements()); + List childViews = new ArrayList<>(); + for (ElementJson attr : filtered) { + String childId = (String) attr.get(MsosaConstants.TYPEID); + if (MsosaConstants.PROPERTY.equals(attr.getType()) && childId != null && !childId.isEmpty()) { + Map child = new HashMap<>(); + child.put(ElementJson.ID, childId); + child.put(MsosaConstants.AGGREGATION, (String) attr.get(MsosaConstants.AGGREGATION)); + child.put(MsosaConstants.PROPERTYID, attr.getId()); + childViews.add(child); + } + } + element.put(MsosaConstants.CHILDVIEWS, childViews); + } + } + } + + @Override + public ElementsResponse getGroups(String projectId, String refId, Map params) { + ContextHolder.setContext(projectId, refId); + String commitId = params.getOrDefault(MsosaConstants.COMMITID, null); + List groups = getNodePersistence().findAllByNodeType(projectId, refId, commitId, + MsosaNodeType.GROUP.getValue()); + ElementsResponse res = this.read(projectId, refId, buildRequestFromJsons(groups), params); + for (ElementJson e: res.getElements()) { + Optional parent = getFirstRelationshipOfType(projectId, refId, commitId, e, + Arrays.asList(MsosaNodeType.GROUP.getValue()), MsosaConstants.OWNERID); + if (parent.isPresent()) { + e.put(MsosaConstants.PARENTID, parent.get().getId()); + } + } + return res; +} + + @Override + public void extraProcessPostedElement(NodeChangeInfo info, ElementJson element) { + //handle _childViews + List> newChildViews = (List)element.remove(MsosaConstants.CHILDVIEWS); + if (newChildViews == null) { + super.extraProcessPostedElement(info, element); + return; + } + //gather data on "old" attributes + List oldOwnedAttributeIds = (List)element.get(MsosaConstants.OWNEDATTRIBUTEIDS); + //use helper to get access to Nodes + String projectId = info.getCommitJson().getProjectId(); + String refId = info.getCommitJson().getRefId(); + NodeGetInfo oldInfo = getNodePersistence().findAll(projectId, refId, null, buildRequest(oldOwnedAttributeIds).getElements()); + List oldProperties = new ArrayList<>(); + Map oldPropertiesTypeMapping = new HashMap<>(); //typeId to PropertyData + for (String oldOwnedAttributeId: oldOwnedAttributeIds) { + if (!oldInfo.getActiveElementMap().containsKey(oldOwnedAttributeId)) { + continue; //property doesn't exist anymore? indicates existing model inconsistency + } + //TODO This probably breaks view editor. move to federated domain somehow + //Node oldNode = oldInfo.getExistingNodeMap().get(oldOwnedAttributeId); + ElementJson oldJson = oldInfo.getActiveElementMap().get(oldOwnedAttributeId); + PropertyData oldData = new PropertyData(); + oldData.setPropertyJson(oldJson); + //TODO This probably breaks view editor. move to federated domain somehow + //oldData.setPropertyNode(oldNode); + oldProperties.add(oldData); + String typeId = (String)oldJson.get(MsosaConstants.TYPEID); + if (typeId == null || typeId.isEmpty()) { + continue; //property has no type + } + oldPropertiesTypeMapping.put(typeId, oldData); + } + //include project usages when finding types + ElementsResponse oldTypeJsons = this.read(element.getProjectId(), element.getRefId(), + buildRequest(oldPropertiesTypeMapping.keySet()), Collections.emptyMap()); + for (ElementJson oldType: oldTypeJsons.getElements()) { + oldPropertiesTypeMapping.get(oldType.getId()).setTypeJson(oldType); + oldPropertiesTypeMapping.get(oldType.getId()).setView(msosaHelper.isView(oldType)); + } + //now oldProperties is a list of existing property data in existing order (can include ones with + // no type or types that're not views + //reset context since previous type finding could have looked in submodules + ContextHolder.setContext(element.getProjectId(), element.getRefId()); + //go through requested _childView changes + //get the first package element that's in the owner chain of parent class + // msosa/sysml1 requires associations to be placed in the first owning package, is this rule still valid? + Optional p = getFirstRelationshipOfType(projectId, refId, null, element, + Arrays.asList(MsosaNodeType.PACKAGE.getValue(), MsosaNodeType.GROUP.getValue()), MsosaConstants.OWNERID); + String packageId = p.isPresent() ? p.get().getId() : MsosaConstants.HOLDING_BIN_PREFIX + element.getProjectId(); + List newProperties = new ArrayList<>(); + List newAttributeIds = new ArrayList<>(); + for (Map newChildView: newChildViews) { + String typeId = newChildView.get(ElementJson.ID); + if (oldPropertiesTypeMapping.containsKey(typeId)) { + //existing property and type, reuse + PropertyData data = oldPropertiesTypeMapping.get(typeId); + newProperties.add(data); + newAttributeIds.add(data.getPropertyJson().getId()); + continue; + } + //create new properties and association + PropertyData newProperty = createElementsForView(info, newChildView.get(MsosaConstants.AGGREGATION), typeId, element.getId(), packageId); + newProperties.add(newProperty); + newAttributeIds.add(newProperty.getPropertyJson().getId()); + } + //go through old attributes and add back any that wasn't to a view and delete ones that's to a view but not in newProperties + List toDelete = new ArrayList<>(); + for (PropertyData oldProperty: oldProperties) { + if (newProperties.contains(oldProperty)) { + continue; //already added + } + if (!oldProperty.isView()) { + newProperties.add(oldProperty); + newAttributeIds.add(oldProperty.getPropertyJson().getId()); + continue; + } + toDelete.add(oldProperty); + } + deletePropertyElements(projectId, refId, toDelete, info); + //new derived ownedAttributeIds based on changes + element.put(MsosaConstants.OWNEDATTRIBUTEIDS, newAttributeIds); + super.extraProcessPostedElement(info, element); + } + + private PropertyData createElementsForView(NodeChangeInfo info, String aggregation, String typeId, String parentId, String packageId) { + //create new properties and association + String newPropertyId = UUID.randomUUID().toString(); + String newAssocId = UUID.randomUUID().toString(); + String newAssocPropertyId = UUID.randomUUID().toString(); + ElementJson newPropertyJson = msosaHelper.createProperty(newPropertyId, "", parentId, + aggregation, typeId, newAssocId); + ElementJson newAssocJson = msosaHelper.createAssociation(newAssocId, packageId, + newAssocPropertyId, newPropertyId); + ElementJson newAssocPropertyJson = msosaHelper.createProperty(newAssocPropertyId, "", + newAssocId, MsosaConstants.NONE, parentId, newAssocId); + + getNodePersistence().prepareAddsUpdates(info, List.of(newPropertyJson, newAssocJson, newAssocPropertyJson)); + super.extraProcessPostedElement(info, newPropertyJson); + super.extraProcessPostedElement(info, newAssocJson); + super.extraProcessPostedElement(info, newAssocPropertyJson); + PropertyData newProperty = new PropertyData(); + newProperty.setAssocJson(newAssocJson); + newProperty.setPropertyJson(newPropertyJson); + newProperty.setAssocPropertyJson(newAssocPropertyJson); + newProperty.setView(true); + return newProperty; + } + + private void deletePropertyElements(String projectId, String refId, List properties, NodeChangeInfo info) { + Set assocToDelete = new HashSet<>(); + for (PropertyData oldProperty: properties) { + ElementJson oldPropertyJson = oldProperty.getPropertyJson(); + getNodePersistence().prepareDeletes(info, List.of(oldPropertyJson)); + String assocId = (String)oldPropertyJson.get(MsosaConstants.ASSOCIATIONID); + if (assocId == null) { + continue; + } + assocToDelete.add(assocId); + } + Set assocPropToDelete = new HashSet<>(); + NodeGetInfo assocInfo = getNodePersistence().findAll(projectId, refId, null, buildRequest(assocToDelete).getElements()); + for (ElementJson assocJson: assocInfo.getActiveElementMap().values()) { + getNodePersistence().prepareDeletes(info, List.of(assocJson)); + List ownedEndIds = (List)assocJson.get(MsosaConstants.OWNEDENDIDS); + if (ownedEndIds == null) { + continue; + } + assocPropToDelete.addAll(ownedEndIds); + } + NodeGetInfo assocPropInfo = getNodePersistence().findAll(projectId, refId, null, buildRequest(assocPropToDelete).getElements()); + getNodePersistence().prepareDeletes(info, assocPropInfo.getActiveElementMap().values()); + } + + // find first element of type in types following e's relkey (assuming relkey's + // value is an element id) + private Optional getFirstRelationshipOfType(String projectId, String refId, String commitId, + ElementJson e, List types, String relkey) { + // only for latest graph + String nextId = (String) e.get(relkey); + if (nextId == null || nextId.isEmpty()) { + return Optional.empty(); + } + NodeGetInfo getInfo = nodePersistence.findById(projectId, refId, commitId, nextId); + Optional next = Optional.of(getInfo.getActiveElementMap().get(nextId)); + + while (next.isPresent()) { + if (types.contains(msosaHelper.getNodeType(next.get()).getValue())) { + return next; + } + nextId = (String)next.get().get(relkey); + if (nextId == null || nextId.isEmpty()) { + return Optional.empty(); + } + getInfo = nodePersistence.findById(projectId, refId, commitId, nextId); + next = Optional.of(getInfo.getActiveElementMap().get(nextId)); + } + return Optional.empty(); + } + +} diff --git a/oauth/oauth.gradle b/oauth/oauth.gradle new file mode 100644 index 000000000..b9c0cc588 --- /dev/null +++ b/oauth/oauth.gradle @@ -0,0 +1,18 @@ +apply plugin: 'java-library' +apply plugin: 'io.spring.dependency-management' + +Map commonDependencies = rootProject.ext.commonDependencies + +dependencies { + implementation project(':core') + implementation project(':crud') + + implementation 'com.google.code.gson:gson:2.8.2' + implementation 'javax.servlet:javax.servlet-api:4.0.1' + implementation 'org.apache.httpcomponents:httpclient:4.5.3' + + implementation commonDependencies.'spring-security-config' + implementation commonDependencies.'spring-security-web' + + testImplementation commonDependencies.'spring-boot-starter-test' +} diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/config/OAuthSecurityConfig.java b/oauth/src/main/java/org/openmbee/mms/oauth/config/OAuthSecurityConfig.java new file mode 100644 index 000000000..d2a808ad1 --- /dev/null +++ b/oauth/src/main/java/org/openmbee/mms/oauth/config/OAuthSecurityConfig.java @@ -0,0 +1,32 @@ +package org.openmbee.mms.oauth.config; + +import org.openmbee.mms.oauth.security.OAuthAuthenticationFilter; +import org.openmbee.mms.oauth.security.OAuthProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; + +@Configuration +public class OAuthSecurityConfig { + + private static Logger logger = LoggerFactory.getLogger(OAuthSecurityConfig.class); + + public OAuthSecurityConfig() { + } + + public void setAuthConfig(HttpSecurity http) throws Exception { + + } + + @Bean + public OAuthAuthenticationFilter oAuthAuthenticationFilter() throws Exception { + return new OAuthAuthenticationFilter(); + } + + @Bean + public OAuthProcessor oAuthProcessor() throws Exception { + return new OAuthProcessor(); + } +} diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/constants/OAuthConstants.java b/oauth/src/main/java/org/openmbee/mms/oauth/constants/OAuthConstants.java new file mode 100644 index 000000000..e83e0fa45 --- /dev/null +++ b/oauth/src/main/java/org/openmbee/mms/oauth/constants/OAuthConstants.java @@ -0,0 +1,22 @@ +package org.openmbee.mms.oauth.constants; + +public class OAuthConstants { + + public static final String AUTHORIZATION = "Authorization"; + public static final String ACCESS_TOKEN = "access_token"; + public static final String BEARER = "Bearer "; + public static final String SAMA_ACCOUNT_NAME = "sAMAccountName"; + public static final String TOKEN = "token"; + public static final String CLIENT_ID = "client_id"; + public static final String JKS = "jks"; + + + public static final String GRANT_LOA = "AuthnLevel"; + public static final String GRANT_TYPE = "grant_type"; + public static final String GRANT_TYPE_VALIDATE_BEARER = "urn:pingidentity.com:oauth2:grant_type:validate_bearer"; + + public static final String MEDIATYPE = "MediaType"; + public static final String TLS_VERSION = "TLSv1.2"; + public static final String MEDIATYPE_URL_ENCODED = "application/x-www-form-urlencoded"; + +} diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/constants/OAuthErrorConstants.java b/oauth/src/main/java/org/openmbee/mms/oauth/constants/OAuthErrorConstants.java new file mode 100644 index 000000000..3d1c9ccdc --- /dev/null +++ b/oauth/src/main/java/org/openmbee/mms/oauth/constants/OAuthErrorConstants.java @@ -0,0 +1,15 @@ +package org.openmbee.mms.oauth.constants; + +public class OAuthErrorConstants { + public static final String HTTP_FAILED = "Http call failed"; + public static final String INSUFFICIENT_LOA = "User does not have proper LOA."; + public static final String INVALD_TOKEN = "Invalid bearer token !!"; + public static final String NO_GRANTS_PROVIDED = "No Grants provided for the user."; + public static final String NO_TOKEN_RECEIVED= "No access token received."; + public static final String PROBLEM_LOADING_CERTIFICATE = "Problem occurred when loading the application certificates : "; + public static final String UNKNOWN_LOA = "LOA value not provided therefore LOA can not be checked"; + public static final String UNKNOWN_TOKEN= "Token is either of an unknown type or not a Bearer token"; + public static final String UNKNOWN_USER = "Unknown User."; + public static final String CLIENT_ID_REQUIRED = "Client id is required."; + public static final String CLIENT_ID_NOT_WHITELISTED = "Client id %s is not in the client id whitelist."; +} \ No newline at end of file diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuth2Authentication.java b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuth2Authentication.java new file mode 100644 index 000000000..f1da0dcef --- /dev/null +++ b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuth2Authentication.java @@ -0,0 +1,55 @@ +package org.openmbee.mms.oauth.security; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +public class OAuth2Authentication extends AbstractAuthenticationToken { + + + private static final long serialVersionUID = 2928542619979639608L; + private String userId; + private String autherizationToken; + private UserDetails principal; + Map grants; + + public OAuth2Authentication() { + super(new ArrayList<>()); + this.setAuthenticated(false); + } + + public OAuth2Authentication(Map grants) { + super(new ArrayList<>()); + this.grants = grants; + this.setAuthenticated(false); + } + + public OAuth2Authentication(Collection authorities) { + super(authorities); + this.setAuthenticated(false); + } + + public OAuth2Authentication(String authorizationToken, Map grants, UserDetails userDetails) { + super(userDetails.getAuthorities()); + this.autherizationToken = authorizationToken; + this.grants = grants; + this.principal = userDetails; + this.setAuthenticated(false); + } + + @Override + public Object getCredentials() { + return null; + } + + @Override + public Object getPrincipal() { + return this.principal; + } + +} \ No newline at end of file diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthAuthenticationFilter.java b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthAuthenticationFilter.java new file mode 100644 index 000000000..40c11aa9e --- /dev/null +++ b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthAuthenticationFilter.java @@ -0,0 +1,53 @@ +package org.openmbee.mms.oauth.security; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openmbee.mms.oauth.constants.OAuthConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.web.filter.OncePerRequestFilter; + +public class OAuthAuthenticationFilter extends OncePerRequestFilter { + private static Logger logger = LoggerFactory.getLogger(OAuthAuthenticationFilter.class); + OAuthProcessor oAuthProcessor; + + @Autowired + public void setOAuthProcessor(OAuthProcessor oAuthProcessor ){ + this.oAuthProcessor = oAuthProcessor; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + try { + String authHeader = request.getHeader(OAuthConstants.AUTHORIZATION); + if((authHeader == null || authHeader.isEmpty()) && request.getHeader(OAuthConstants.ACCESS_TOKEN) != null){ + authHeader = OAuthConstants.BEARER+ request.getHeader(OAuthConstants.ACCESS_TOKEN); + } + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + // Require the Authorization: Bearer format for auth header + // Skip OAuth is already authenticated + if (authHeader != null && authHeader.startsWith(OAuthConstants.BEARER) && auth == null) { + String authToken = authHeader; + OAuth2Authentication authentication = oAuthProcessor.validateAuthToken(authToken); + if (authentication != null) { + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + } catch (Exception e) { + logger.error(e.getMessage()); + } finally { + filterChain.doFilter(request, response); + } + } +} diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthProcessor.java b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthProcessor.java new file mode 100644 index 000000000..aea6513b3 --- /dev/null +++ b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthProcessor.java @@ -0,0 +1,210 @@ +package org.openmbee.mms.oauth.security; + +import java.io.File; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.*; +import java.util.stream.Collectors; + +import javax.net.ssl.SSLContext; + + +import org.openmbee.mms.core.exceptions.UnauthorizedException; +import org.openmbee.mms.oauth.constants.OAuthErrorConstants; +import org.openmbee.mms.oauth.util.OAuthTokenUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.apache.http.HttpException; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.ssl.SSLContexts; +import org.openmbee.mms.oauth.constants.OAuthConstants; +import org.springframework.security.core.userdetails.UserDetails; + +public class OAuthProcessor { + + private static Logger logger = LoggerFactory.getLogger(OAuthProcessor.class); + + @Value("${oauth.rs_id}") + private String rs_id; + + @Value("${oauth.environment}") + private String environment; + + @Value("${oauth.keystoreLocation}") + private String keystoreLocation; + + @Value("${oauth.keystorePassword}") + private char[] keystorePassword; + + @Value("${oauth.certificatePassword}") + private String certificatePassword; + + @Value("${oauth.loa}") + private String loa; + + @Value("${oauth.clients}") + private String allowedClients; + + @Value("${oauth.baseUrl}") + private String oauthBaseUrl; + + @Value("${oauth.validationEndpoint}") + private String oauthValidationEndpoint; + + @Value("#{'${oauth.clientIdWhitelist}'.split(',')}") + private Set clientIdWhitelist; + + private File jks; + Map grants = new HashMap(); + + + private OAuthUserDetailsService userDetailsService; + + public OAuthUserDetailsService getUserDetailsService() { + return userDetailsService; + } + + @Autowired + public void setUserDetailsService(OAuthUserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + + OAuth2Authentication validateAuthToken(String accessToken) { + Boolean isAuthenticated = true; + OAuth2Authentication oauth = null; + try { + if (accessToken == null) { + throw new Exception(OAuthErrorConstants.NO_TOKEN_RECEIVED); + } + // check that the token is a Bearer Token + if (!accessToken.contains(OAuthConstants.BEARER) && !accessToken.contains(OAuthConstants.BEARER.toLowerCase())) { + throw new Exception(OAuthErrorConstants.UNKNOWN_TOKEN); + } + + ClassLoader classLoader = OAuthProcessor.class.getClassLoader(); + KeyStore keystore = KeyStore.getInstance(OAuthConstants.JKS); + SSLConnectionSocketFactory sslsf; + try (InputStream keystoreStream = classLoader.getResourceAsStream(keystoreLocation)) { // prevent leak of keystore stream + // Load the Keystore with Application Certificate + keystore.load(keystoreStream, keystorePassword); + + try { + // check to see if keystore is empty or not + SSLContext sslcontext; + if (keystore.size() <= 0) { + sslcontext = SSLContexts.custom().loadKeyMaterial(jks, keystorePassword, certificatePassword.toCharArray()).build(); + } else { + sslcontext = SSLContexts.custom().loadKeyMaterial(keystore, certificatePassword.toCharArray()).build(); + } + // Allow TLSv1.2 protocol only + sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { OAuthConstants.TLS_VERSION }, null, + SSLConnectionSocketFactory.getDefaultHostnameVerifier()); + } catch (GeneralSecurityException gse) { + throw new GeneralSecurityException( OAuthErrorConstants.PROBLEM_LOADING_CERTIFICATE + gse.getMessage()); + } + } + + // Create the client to use for the connection for the access token validation + // HTTP Post Call + CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build(); + HttpPost post = new HttpPost(oauthBaseUrl + oauthValidationEndpoint); + // Create a list of the values to add to the Post call as an HTTP Form + List values = new ArrayList(2); + values.add(new BasicNameValuePair(OAuthConstants.GRANT_TYPE, OAuthConstants.GRANT_TYPE_VALIDATE_BEARER)); + String[] tokenValue = accessToken.split(" "); + if (tokenValue.length == 2) { + values.add(new BasicNameValuePair(OAuthConstants.TOKEN, tokenValue[1])); + } else { + throw new Exception(OAuthErrorConstants.UNKNOWN_TOKEN); + } + values.add(new BasicNameValuePair(OAuthConstants.CLIENT_ID, rs_id)); + post.setEntity(new UrlEncodedFormEntity(values)); + post.addHeader(OAuthConstants.MEDIATYPE, OAuthConstants.MEDIATYPE_URL_ENCODED); + // Execute Post + + try (CloseableHttpResponse response = client.execute(post)){ + if (response == null) + throw new HttpException(OAuthErrorConstants.HTTP_FAILED); + switch (response.getStatusLine().getStatusCode()) { + case 200: + // A 200 indicates that the token was valid + String token = extractToken(response); + + // If Post successful get the return JSON object and parse if the GSON Java Lib + grants = OAuthTokenUtil.parsesOAuthRSResponse(token); + // If grants are null or empty throw an exception + if (grants == null || grants.isEmpty()) { + isAuthenticated = false; + throw new UnauthorizedException(OAuthErrorConstants.NO_GRANTS_PROVIDED); + } else { + String userName = grants.containsKey(OAuthConstants.SAMA_ACCOUNT_NAME) ? grants.get(OAuthConstants.SAMA_ACCOUNT_NAME) : ""; + if (!userName.isEmpty()) { + UserDetails userDetails = this.userDetailsService.loadUserByUsername(userName); + oauth = new OAuth2Authentication(accessToken,grants,userDetails); + } else { + throw new UnauthorizedException(OAuthErrorConstants.UNKNOWN_USER); + } + // if we did get those check to see if we are including authnlevel or not and + // make sure the LOA value is valid + if (grants.containsKey(OAuthConstants.GRANT_LOA)) { + double grantLoa = Double.parseDouble(grants.get(OAuthConstants.GRANT_LOA)); + if (grantLoa < Double.parseDouble(loa)) { + throw new IllegalAccessException(OAuthErrorConstants.INSUFFICIENT_LOA); + } + } else { + throw new IllegalAccessException(OAuthErrorConstants.UNKNOWN_LOA); + } + + if (grants.containsKey(OAuthConstants.CLIENT_ID)) { + String clientId = grants.get(OAuthConstants.CLIENT_ID); + if(! clientIdWhitelist.contains(clientId)) { + throw new IllegalAccessException(String.format(OAuthErrorConstants.CLIENT_ID_NOT_WHITELISTED, clientId)); + } + } else { + throw new IllegalAccessException(OAuthErrorConstants.CLIENT_ID_REQUIRED); + } + } + break; + case 400: + // 400 indicates that the bearer token was not valid + throw new Exception(OAuthErrorConstants.INVALD_TOKEN); + default: + throw new HttpResponseException(response.getStatusLine().getStatusCode(),response.getStatusLine().getReasonPhrase()); + } + } catch (Exception e) { + throw new Exception(e); + } + } catch (Exception e) { + isAuthenticated = false; + logger.error(e.getMessage(),e); + } + if (oauth != null) { + oauth.setAuthenticated(isAuthenticated); + } + return oauth; + } + + private String extractToken(CloseableHttpResponse response) { + try(BufferedReader buffer = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))){ + return buffer.lines().collect(Collectors.joining("\n")); + } catch (IOException ex) { + throw new UnauthorizedException(OAuthErrorConstants.NO_TOKEN_RECEIVED); + } + } + +} \ No newline at end of file diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetails.java b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetails.java new file mode 100644 index 000000000..d1b35ab13 --- /dev/null +++ b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetails.java @@ -0,0 +1,72 @@ +package org.openmbee.mms.oauth.security; + +import org.openmbee.mms.core.config.AuthorizationConstants; +import org.openmbee.mms.json.GroupJson; +import org.openmbee.mms.json.UserJson; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.ArrayList; +import java.util.Collection; + +public class OAuthUserDetails implements UserDetails { + + private final UserJson user; + private final Collection groups; + + public OAuthUserDetails(UserJson user, Collection groups) { + this.user = user; + this.groups = groups; + } + + @Override + public Collection getAuthorities() { + Collection authorities = new ArrayList<>(); + if (groups != null) { + for (GroupJson group : groups) { + authorities.add(new SimpleGrantedAuthority(group.getName())); + } + } + if (Boolean.TRUE.equals(user.isAdmin())) { + authorities.add(new SimpleGrantedAuthority(AuthorizationConstants.MMSADMIN)); + } + authorities.add(new SimpleGrantedAuthority(AuthorizationConstants.EVERYONE)); + return authorities; + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getUsername(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return user.isEnabled(); + } + + public UserJson getUser() { + return user; + } + +} diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetailsService.java b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetailsService.java new file mode 100644 index 000000000..f6f9667a4 --- /dev/null +++ b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetailsService.java @@ -0,0 +1,53 @@ +package org.openmbee.mms.oauth.security; + +import org.openmbee.mms.core.dao.UserGroupsPersistence; +import org.openmbee.mms.core.dao.UserPersistence; +import org.openmbee.mms.json.UserJson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +@Service +public class OAuthUserDetailsService implements UserDetailsService { + + private UserPersistence userPersistence; + private UserGroupsPersistence userGroupsPersistence; + + @Autowired + public void setUserPersistence(UserPersistence userPersistence) { + this.userPersistence = userPersistence; + } + + @Autowired + public void setUserGroupsPersistence(UserGroupsPersistence userGroupsPersistence) { + this.userGroupsPersistence = userGroupsPersistence; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + Optional userOptional = userPersistence.findByUsername(username); + + UserJson user; + if (userOptional.isEmpty()) { + user = addUser(username); + } else { + user = userOptional.get(); + } + return new OAuthUserDetails(user, userGroupsPersistence.findGroupsAssignedToUser(username)); + } + + @Transactional + public UserJson addUser(String username) { + UserJson user = new UserJson(); + user.setUsername(username); + //TODO: fill in user details from TWC + user.setEnabled(true); + return userPersistence.save(user); + } + +} diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/util/OAuthTokenUtil.java b/oauth/src/main/java/org/openmbee/mms/oauth/util/OAuthTokenUtil.java new file mode 100644 index 000000000..cbf084928 --- /dev/null +++ b/oauth/src/main/java/org/openmbee/mms/oauth/util/OAuthTokenUtil.java @@ -0,0 +1,82 @@ +package org.openmbee.mms.oauth.util; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; + +import java.lang.reflect.Type; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +public class OAuthTokenUtil { + + /** + * Parse a json object into a map with string keys and string values + * + * @param token the json object + * @return a Map of the json object with string keys and string values + * @throws Exception if there is an error parsing the json + */ + public static Map parseToken(String token) throws Exception { + Gson gson = new Gson(); + Type type = new TypeToken>() { + }.getType(); + return gson.fromJson(token, type); + } + + /** + * Parse a JWT Token that is base64 url encoded into a map with string keys and + * string values + * + * @param token JWT base64 url encode string + * @return a Map of the json object with string keys and string values + * @throws Exception if there is an error parsing the json + */ + public static Map parseJWT(String token) throws Exception { + byte[] decodedBytes = Base64.getUrlDecoder().decode(token); + String jwt = new String(decodedBytes, "UTF-8"); + Gson gson = new Gson(); + Type type = new TypeToken>() { + }.getType(); + return gson.fromJson(jwt, type); + } + + /** + * Converts token json object string into string Map that only contains the + * access token and not other info. + * + * @param token json + * @return + * @throws Exception + */ + /// + /// + /// + /// String dictionary with token contents. + public static Map parsesOAuthRSResponse(String token) throws Exception { + Map jsonToken = new HashMap(); + JsonElement jelement = new JsonParser().parse(token); + JsonObject jobject = jelement.getAsJsonObject(); + JsonObject result = jobject; + if (jobject.has("access_token")) { + result = jobject.getAsJsonObject("access_token"); + } + + if (jobject.has("client_id")) { + result.add("client_id", jobject.get("client_id")); + } + + if (jobject.has("scope")) { + result.add("scope", jobject.get("scope")); + } + + Gson gson = new Gson(); + // This gives a warning because the JSON object could contain a value that is + // not a string and has no toString method + // For our case it should be fine because we will always have string values + return (Map) gson.fromJson(result, jsonToken.getClass()); + } +} diff --git a/permissions/permissions.gradle b/permissions/permissions.gradle index 22c620275..fc847801c 100644 --- a/permissions/permissions.gradle +++ b/permissions/permissions.gradle @@ -1,5 +1,5 @@ dependencies { - implementation project(':rdb') + implementation project (":core") + implementation project (":data") implementation 'org.apache.commons:commons-lang3:3.10' - implementation commonDependencies.'spring-boot' } diff --git a/permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultPermissionsDelegateFactory.java b/permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultPermissionsDelegateFactory.java deleted file mode 100644 index 1eb2d0b93..000000000 --- a/permissions/src/main/java/org/openmbee/mms/permissions/delegation/DefaultPermissionsDelegateFactory.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.openmbee.mms.permissions.delegation; - -import org.openmbee.mms.core.delegation.PermissionsDelegate; -import org.openmbee.mms.core.delegation.PermissionsDelegateFactory; -import org.openmbee.mms.data.domains.global.Branch; -import org.openmbee.mms.data.domains.global.Organization; -import org.openmbee.mms.data.domains.global.Project; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; - -public class DefaultPermissionsDelegateFactory implements PermissionsDelegateFactory { - - @Autowired - ApplicationContext applicationContext; - - @Override - public PermissionsDelegate getPermissionsDelegate(Project project) { - return autowire(new DefaultProjectPermissionsDelegate(project)); - } - - @Override - public PermissionsDelegate getPermissionsDelegate(Organization organization) { - return autowire(new DefaultOrgPermissionsDelegate(organization)); - } - - @Override - public PermissionsDelegate getPermissionsDelegate(Branch branch) { - return autowire(new DefaultBranchPermissionsDelegate(branch)); - } - - private PermissionsDelegate autowire(PermissionsDelegate permissionsDelegate) { - applicationContext.getAutowireCapableBeanFactory().autowireBean(permissionsDelegate); - return permissionsDelegate; - } - -} diff --git a/rdb/rdb.gradle b/rdb/rdb.gradle index 38b68d6bd..b3eba4aa8 100644 --- a/rdb/rdb.gradle +++ b/rdb/rdb.gradle @@ -1,5 +1,6 @@ dependencies { api project(':core') + api project(':data') api commonDependencies.'spring-data-jpa' diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/config/DatabaseDefinitionService.java b/rdb/src/main/java/org/openmbee/mms/rdb/config/DatabaseDefinitionService.java index 1be5c9cc7..624a634e1 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/config/DatabaseDefinitionService.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/config/DatabaseDefinitionService.java @@ -43,8 +43,6 @@ public class DatabaseDefinitionService { private static final String COPY_SQL = "INSERT INTO \"%s\" SELECT * FROM \"%s\""; private static final String COPY_IDX = "SELECT SETVAL('%s_id_seq', COALESCE((SELECT MAX(id) FROM \"%s\"), 1), true)"; - private static final String INITIAL_REF = "INSERT INTO branches (id, branchid, branchname, tag, deleted, timestamp) VALUES (0, 'master', 'master', false, false, NOW());"; - protected final Logger logger = LoggerFactory.getLogger(getClass()); private CrudDataSources crudDataSources; private Environment env; @@ -65,6 +63,7 @@ public boolean createProjectDatabase(Project project) throws SQLException { crudDataSources.getDataSource(ContextHolder.getContext().getKey())); List created = new ArrayList<>(); try { + logger.info("Creating database for " + project.getProjectId()); jdbcTemplate.execute("CREATE DATABASE " + databaseProjectString(project)); //lgtm[java/sql-injection] created.add("Created Database"); @@ -72,7 +71,12 @@ public boolean createProjectDatabase(Project project) throws SQLException { created.add("Created Tables"); } catch (DataAccessException e) { - if (e.getCause().getLocalizedMessage().toLowerCase().contains("exists")) { + Throwable cause = e.getCause(); + if (cause != null) { + logger.error(cause.getLocalizedMessage()); + } + logger.error(e.getLocalizedMessage()); + if (cause != null && cause.getLocalizedMessage() != null && cause.getLocalizedMessage().toLowerCase().contains("exists")) { generateProjectSchemaFromModels(project); throw (new SQLException("Database already exists")); } else { @@ -83,15 +87,15 @@ public boolean createProjectDatabase(Project project) throws SQLException { return !created.isEmpty(); } - public void deleteProjectDatabase(Project project) throws SQLException { + public void deleteProjectDatabase(String projectId) throws SQLException { try (Connection connection = crudDataSources.getDataSource(ContextObject.DEFAULT_PROJECT).getConnection(); Statement statement = connection.createStatement()) { if ("org.postgresql.Driver".equals(env.getProperty("spring.datasource.driver-class-name"))) { statement.execute(connection.nativeSQL( "SELECT pid, pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '" - + databaseProjectString(project) + "';")); + + databaseProjectString(projectId) + "';")); } - statement.executeUpdate(connection.nativeSQL("DROP DATABASE " + databaseProjectString(project))); + statement.executeUpdate(connection.nativeSQL("DROP DATABASE " + databaseProjectString(projectId))); //TODO: if using PG 13, can use the following //statement.executeUpdate(connection.nativeSQL("DROP DATABASE " + databaseProjectString(project) + " WITH (FORCE)")); @@ -99,8 +103,12 @@ public void deleteProjectDatabase(Project project) throws SQLException { } private String databaseProjectString(Project project) { - String prefix = env.getProperty("rdb.project.prefix", ""); - return String.format("\"%s_%s\"", prefix, project.getProjectId()); + return databaseProjectString(project.getProjectId()); + } + + private String databaseProjectString(String projectId) { + String prefix = env.getProperty("rdb.project.prefix", ""); + return String.format("\"%s_%s\"", prefix, projectId); } public void createBranch() { @@ -132,12 +140,6 @@ public void generateProjectSchemaFromModels(Project project) throws SQLException .setDelimiter(";") .createOnly(EnumSet.of(TargetType.DATABASE), metadata.getMetadataBuilder().build()); - - try (Connection conn = crudDataSources.getDataSource(project).getConnection()) { - try (PreparedStatement ps = conn.prepareStatement(INITIAL_REF)) { - ps.execute(); - } - } } public void generateBranchSchemaFromModels() { diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/config/PersistenceJPAConfig.java b/rdb/src/main/java/org/openmbee/mms/rdb/config/PersistenceJPAConfig.java index df44030db..ff87171ef 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/config/PersistenceJPAConfig.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/config/PersistenceJPAConfig.java @@ -6,6 +6,8 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.util.Properties; import javax.sql.DataSource; + +import org.openmbee.mms.core.config.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -99,7 +101,7 @@ private Properties additionalProperties() { "org.hibernate.dialect.PostgreSQLDialect")); properties.setProperty("hibernate.jdbc.lob.non_contextual_creation", env.getProperty("spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation", - "true")); + Constants.TRUE)); return properties; } diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BaseDAOImpl.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BaseDAOImpl.java index 49a8c3218..cea6c82b3 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BaseDAOImpl.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BaseDAOImpl.java @@ -15,7 +15,7 @@ public abstract class BaseDAOImpl { private CrudDataSources crudDataSources; - public PlatformTransactionManager transactionManager; + private PlatformTransactionManager transactionManager; protected final Logger logger = LoggerFactory.getLogger(getClass()); @Autowired diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BranchGDAOImpl.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BranchGDAOImpl.java index 1f9a9ae4a..8ba974ff9 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BranchGDAOImpl.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BranchGDAOImpl.java @@ -1,7 +1,8 @@ package org.openmbee.mms.rdb.repositories; import java.util.Optional; -import org.openmbee.mms.core.dao.BranchGDAO; + +import org.openmbee.mms.data.dao.BranchGDAO; import org.openmbee.mms.data.domains.global.Branch; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/OrgDAOImpl.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/OrgDAOImpl.java index 0bfcb25a9..8f98541cf 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/OrgDAOImpl.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/OrgDAOImpl.java @@ -2,7 +2,8 @@ import java.util.List; import java.util.Optional; -import org.openmbee.mms.core.dao.OrgDAO; + +import org.openmbee.mms.data.dao.OrgDAO; import org.openmbee.mms.data.domains.global.Organization; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectDAOImpl.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectDAOImpl.java index 427cd2f81..6aec4672e 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectDAOImpl.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectDAOImpl.java @@ -3,8 +3,9 @@ import java.sql.SQLException; import java.util.List; import java.util.Optional; -import org.openmbee.mms.core.dao.ProjectDAO; + import org.openmbee.mms.core.exceptions.InternalErrorException; +import org.openmbee.mms.data.dao.ProjectDAO; import org.openmbee.mms.data.domains.global.Project; import org.openmbee.mms.rdb.config.DatabaseDefinitionService; import org.slf4j.Logger; @@ -60,14 +61,17 @@ public Project save(Project proj) { } @Override - public void delete(Project p) { - projectRepository.delete(p); + public void delete(String projectId) { + try { + projectRepository.findByProjectId(projectId).ifPresent(v -> projectRepository.delete(v)); + } catch (Exception ex) { + logger.error("Could not delete project from project Repository"); + } try { - projectOperations.deleteProjectDatabase(p); - } catch(SQLException ex) { + projectOperations.deleteProjectDatabase(projectId); + } catch (SQLException ex) { logger.error("DELETE PROJECT DATABASE EXCEPTION\nPotential connection issue, query statement mishap, or unexpected RDB behavior."); - throw new InternalErrorException(ex); } } diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectRepository.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectRepository.java index c5bf01dd2..25e3741a9 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectRepository.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectRepository.java @@ -2,8 +2,6 @@ import java.util.List; import java.util.Optional; - -import org.openmbee.mms.data.domains.global.Organization; import org.openmbee.mms.data.domains.global.Project; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/WebhookDAOImpl.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/WebhookDAOImpl.java index 1cf8d1a8b..b53f874b7 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/WebhookDAOImpl.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/WebhookDAOImpl.java @@ -1,6 +1,6 @@ package org.openmbee.mms.rdb.repositories; -import org.openmbee.mms.core.dao.WebhookDAO; +import org.openmbee.mms.data.dao.WebhookDAO; import org.openmbee.mms.data.domains.global.Webhook; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/branch/BranchDAOImpl.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/branch/BranchDAOImpl.java index 2a4e24230..bb236cc1c 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/branch/BranchDAOImpl.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/branch/BranchDAOImpl.java @@ -1,21 +1,24 @@ package org.openmbee.mms.rdb.repositories.branch; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.List; -import java.util.Optional; +import org.openmbee.mms.core.config.Constants; import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.dao.BranchDAO; +import org.openmbee.mms.data.dao.BranchDAO; import org.openmbee.mms.data.domains.scoped.Branch; -import org.openmbee.mms.rdb.repositories.BaseDAOImpl; import org.openmbee.mms.rdb.config.DatabaseDefinitionService; +import org.openmbee.mms.rdb.repositories.BaseDAOImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.PreparedStatementCreator; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Component; +import org.openmbee.mms.core.exceptions.NotFoundException; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.List; +import java.util.Optional; @Component public class BranchDAOImpl extends BaseDAOImpl implements BranchDAO { @@ -34,8 +37,10 @@ public Branch save(Branch branch) { if (branch.getId() == null) { ContextHolder.setContext(ContextHolder.getContext().getProjectId(), branch.getBranchId()); - branchesOperations.createBranch(); - branchesOperations.copyTablesFromParent(branch.getBranchId(), branch.getParentRefId(), null); + if(!Constants.MASTER_BRANCH.equals(branch.getBranchId())) { + branchesOperations.createBranch(); + branchesOperations.copyTablesFromParent(branch.getBranchId(), branch.getParentRefId(), null); + } KeyHolder keyHolder = new GeneratedKeyHolder(); @@ -46,8 +51,12 @@ public PreparedStatement createPreparedStatement(Connection connection) return prepareStatement(ps, branch); } }, keyHolder); - - branch.setId(keyHolder.getKey().longValue()); + if (keyHolder.getKey() != null) { + branch.setId(keyHolder.getKey().longValue()); + } else { + throw new NotFoundException("Key value was null"); + } + } else { getConn().update(new PreparedStatementCreator() { public PreparedStatement createPreparedStatement(Connection connection) @@ -94,7 +103,7 @@ private PreparedStatement prepareStatement(PreparedStatement ps, Branch branch) ps.setString(3, branch.getBranchId()); ps.setString(4, branch.getBranchName()); ps.setString(5, branch.getParentRefId()); - ps.setLong(6, branch.getParentCommit()); + ps.setLong(6, branch.getBranchId().equals(Constants.MASTER_BRANCH) ? 0L : branch.getParentCommit()); ps.setTimestamp(7, Timestamp.from(branch.getTimestamp())); ps.setBoolean(8, branch.isTag()); ps.setBoolean(9, branch.isDeleted()); diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/commit/CommitDAOImpl.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/commit/CommitDAOImpl.java index eb4e55fdc..dc15fa426 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/commit/CommitDAOImpl.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/commit/CommitDAOImpl.java @@ -10,8 +10,8 @@ import java.util.List; import java.util.Optional; -import org.openmbee.mms.core.dao.BranchDAO; -import org.openmbee.mms.core.dao.CommitDAO; +import org.openmbee.mms.data.dao.BranchDAO; +import org.openmbee.mms.data.dao.CommitDAO; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.data.domains.scoped.Branch; import org.openmbee.mms.data.domains.scoped.Commit; @@ -51,7 +51,7 @@ public PreparedStatement createPreparedStatement(Connection connection) } }, keyHolder); - if (keyHolder.getKeyList().isEmpty()) { + if (keyHolder.getKeyList().isEmpty() || keyHolder.getKey() == null) { logger.error("commit db save failed"); throw new InternalErrorException("Commit db save failed"); } diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/node/NodeDAOImpl.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/node/NodeDAOImpl.java index d40e9ef58..b9a42fd87 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/node/NodeDAOImpl.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/node/NodeDAOImpl.java @@ -6,7 +6,7 @@ import java.sql.SQLException; import java.util.*; -import org.openmbee.mms.core.dao.NodeDAO; +import org.openmbee.mms.data.dao.NodeDAO; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.data.domains.scoped.Node; import org.openmbee.mms.rdb.repositories.BaseDAOImpl; @@ -17,6 +17,7 @@ import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Component; +import org.openmbee.mms.core.exceptions.NotFoundException; @Component public class NodeDAOImpl extends BaseDAOImpl implements NodeDAO { @@ -37,8 +38,11 @@ public PreparedStatement createPreparedStatement(Connection connection) return prepareStatement(ps, node); } }, keyHolder); - - node.setId(keyHolder.getKey().longValue()); + if (keyHolder.getKey() != null) { + node.setId(keyHolder.getKey().longValue()); + } else { + throw new NotFoundException("Key value was null"); + } } else { getConn().update(new PreparedStatementCreator() { public PreparedStatement createPreparedStatement(Connection connection) diff --git a/storage/storage.gradle b/storage/storage.gradle index a83e25503..3fc3b6db7 100644 --- a/storage/storage.gradle +++ b/storage/storage.gradle @@ -7,3 +7,9 @@ dependencies { testImplementation commonDependencies.'spring-boot-starter-test' } + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } +} \ No newline at end of file diff --git a/twc/src/main/java/org/openmbee/mms/twc/TeamworkCloud.java b/twc/src/main/java/org/openmbee/mms/twc/TeamworkCloud.java index aff958cf1..bf35702e5 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/TeamworkCloud.java +++ b/twc/src/main/java/org/openmbee/mms/twc/TeamworkCloud.java @@ -70,25 +70,26 @@ public void setAdminPwd(String adminPwd) { this.adminPwd = adminPwd; } - public TeamworkCloudRolesMapping getRoles() { + synchronized public TeamworkCloudRolesMapping getRoles() { return roles; } - public void setRoles(TeamworkCloudRolesMapping roles) { + synchronized public void setRoles(TeamworkCloudRolesMapping roles) { this.roles = roles; } - public void setTwcmmsRolesMap(Map> twcmmsRolesMap) { + synchronized public void setTwcmmsRolesMap(Map> twcmmsRolesMap) { this.twcmmsRolesMap = twcmmsRolesMap; } synchronized public Set getKnownNames() { if(knownNames == null){ - knownNames = new HashSet<>(); - knownNames.add(url.toLowerCase()); + HashSet tempNames = new HashSet<>(); + tempNames.add(url.toLowerCase()); if(aliases != null) { - aliases.stream().map(String::toLowerCase).forEach(v -> knownNames.add(v)); + aliases.stream().map(String::toLowerCase).forEach(v -> tempNames.add(v)); } + knownNames = tempNames; } return knownNames; } diff --git a/twc/src/main/java/org/openmbee/mms/twc/TeamworkCloudEndpoints.java b/twc/src/main/java/org/openmbee/mms/twc/TeamworkCloudEndpoints.java index 24c1ee045..05c84cc71 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/TeamworkCloudEndpoints.java +++ b/twc/src/main/java/org/openmbee/mms/twc/TeamworkCloudEndpoints.java @@ -3,7 +3,7 @@ public enum TeamworkCloudEndpoints { LOGIN("login"), GETROLESID("resources/%s/roles"), - GETPROJECTUSERS("workspaces/%s/resources/%s/roles/%s/users"); + GETPROJECTUSERS("resources/%s/roles/%s/users"); private String path; diff --git a/twc/src/main/java/org/openmbee/mms/twc/config/TwcConfig.java b/twc/src/main/java/org/openmbee/mms/twc/config/TwcConfig.java index 9b573bfcd..3ddf51eb6 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/config/TwcConfig.java +++ b/twc/src/main/java/org/openmbee/mms/twc/config/TwcConfig.java @@ -42,8 +42,9 @@ public TwcAuthenticationProvider getAuthNProvider(String associatedTWC) { if(associatedTWC == null) return null; + String strippedHost = stripHost(associatedTWC); for(TeamworkCloud twc : getInstances()){ - if(twc.hasKnownName(associatedTWC)){ + if(twc.hasKnownName(strippedHost)){ return new TwcAuthenticationProvider(restUtils, twc); } } diff --git a/twc/src/main/java/org/openmbee/mms/twc/config/TwcPermissionsConfig.java b/twc/src/main/java/org/openmbee/mms/twc/config/TwcPermissionsConfig.java index 390526f3c..c7486e47d 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/config/TwcPermissionsConfig.java +++ b/twc/src/main/java/org/openmbee/mms/twc/config/TwcPermissionsConfig.java @@ -10,7 +10,7 @@ public class TwcPermissionsConfig { @Bean - @Order(-1) + @Order(-2) public PermissionsDelegateFactory getPermissionsDelegateFactory() { return new TwcPermissionsDelegateFactory(); } diff --git a/twc/src/main/java/org/openmbee/mms/twc/maintenance/TWCMaintenanceController.java b/twc/src/main/java/org/openmbee/mms/twc/maintenance/TWCMaintenanceController.java index 108cd36df..3866c9aa0 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/maintenance/TWCMaintenanceController.java +++ b/twc/src/main/java/org/openmbee/mms/twc/maintenance/TWCMaintenanceController.java @@ -1,10 +1,10 @@ package org.openmbee.mms.twc.maintenance; import org.openmbee.mms.core.config.AuthorizationConstants; +import org.openmbee.mms.core.dao.ProjectPersistence; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.core.exceptions.NotFoundException; -import org.openmbee.mms.data.domains.global.Project; -import org.openmbee.mms.rdb.repositories.ProjectRepository; +import org.openmbee.mms.json.ProjectJson; import org.openmbee.mms.twc.metadata.TwcMetadata; import org.openmbee.mms.twc.metadata.TwcMetadataService; import org.springframework.beans.factory.annotation.Autowired; @@ -20,12 +20,12 @@ @RequestMapping("/adm/maintenance") public class TWCMaintenanceController { - private ProjectRepository projectRepo; + private ProjectPersistence projectPersistence; private TwcMetadataService twcMetadataService; @Autowired - public void setProjectRepo(ProjectRepository projectRepo){ - this.projectRepo = projectRepo; + public void setProjectPersistence(ProjectPersistence projectPersistence) { + this.projectPersistence = projectPersistence; } @Autowired @@ -37,8 +37,8 @@ public void setTwcMetadataService(TwcMetadataService twcMetadataService){ @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) @GetMapping(value = "/project/twcmetadata/{id}") @ResponseBody - public TwcMetadata getProjectMetadata(@PathVariable String id){ - Project project = getProject(id); + public TwcMetadata getProjectMetadata(@PathVariable String id){ + ProjectJson project = getProject(id); return twcMetadataService.getTwcMetadata(project); } @@ -49,7 +49,7 @@ public TwcMetadata getProjectMetadata(@PathVariable String id){ @ResponseBody public void updateProjectMetadata(@PathVariable String id, @RequestBody TwcMetadata twcMetadata){ - Project project = getProject(id); + ProjectJson project = getProject(id); try{ twcMetadataService.updateTwcMetadata(project, twcMetadata); } @@ -63,15 +63,15 @@ public void updateProjectMetadata(@PathVariable String id, @RequestBody TwcMetad @DeleteMapping(value = "/project/twcmetadata/{id}") @ResponseBody public void deleteProjectMetadata(@PathVariable String id){ - Project project = getProject(id); + ProjectJson project = getProject(id); twcMetadataService.deleteTwcMetadata(project); } - private Project getProject(String projectId) { + private ProjectJson getProject(String projectId) { - Optional proj = projectRepo.findByProjectId(projectId); + Optional project = projectPersistence.findById(projectId); - return proj.orElseGet(() -> { + return project.orElseGet(() -> { String notFound = "Project id: " + projectId + " not found"; throw new NotFoundException(notFound); }); diff --git a/twc/src/main/java/org/openmbee/mms/twc/metadata/TwcMetadataService.java b/twc/src/main/java/org/openmbee/mms/twc/metadata/TwcMetadataService.java index b0894fb8c..899e64b82 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/metadata/TwcMetadataService.java +++ b/twc/src/main/java/org/openmbee/mms/twc/metadata/TwcMetadataService.java @@ -1,10 +1,8 @@ package org.openmbee.mms.twc.metadata; -import org.openmbee.mms.core.config.ContextHolder; -import org.openmbee.mms.core.dao.ProjectIndex; +import org.openmbee.mms.core.config.Constants; +import org.openmbee.mms.core.dao.ProjectPersistence; import org.openmbee.mms.twc.constants.TwcConstants; -import org.openmbee.mms.core.exceptions.NotFoundException; -import org.openmbee.mms.data.domains.global.Project; import org.openmbee.mms.json.ProjectJson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,25 +15,21 @@ public class TwcMetadataService { private static final Logger logger = LoggerFactory.getLogger(TwcMetadataService.class); - private ProjectIndex projectIndex; + private ProjectPersistence projectPersistence; @Autowired - public void setProjectIndex(ProjectIndex projectIndex) { - this.projectIndex = projectIndex; + public void setProjectIndex(ProjectPersistence projectPersistence) { + this.projectPersistence = projectPersistence; } - public void updateTwcMetadata(Project project, TwcMetadata metadata) { - ContextHolder.setContext(project.getProjectId()); - ProjectJson projectJson = getProjectJson(project); + public void updateTwcMetadata(ProjectJson projectJson, TwcMetadata metadata) { Map metadataMap = metadata.toMap(); - metadataMap.put(TwcConstants.ENABLED_KEY, "true"); + metadataMap.put(TwcConstants.ENABLED_KEY, Constants.TRUE); projectJson.put(TwcConstants.FOREIGN_PROJECT, metadataMap); - projectIndex.update(projectJson); + projectPersistence.update(projectJson); } - public TwcMetadata getTwcMetadata(Project project) { - ContextHolder.setContext(project.getProjectId()); - ProjectJson projectJson = getProjectJson(project); + public TwcMetadata getTwcMetadata(ProjectJson projectJson) { Map metadata = (Map)projectJson.get(TwcConstants.FOREIGN_PROJECT); if(metadata == null) { return null; @@ -48,24 +42,14 @@ public TwcMetadata getTwcMetadata(Project project) { return twcMetadata; } - public void deleteTwcMetadata(Project project) { - ContextHolder.setContext(project.getProjectId()); - ProjectJson projectJson = getProjectJson(project); + public void deleteTwcMetadata(ProjectJson projectJson) { Map metadata = (Map)projectJson.get(TwcConstants.FOREIGN_PROJECT); if(metadata == null) { return; } - metadata.put(TwcConstants.ENABLED_KEY, "false"); + metadata.put(TwcConstants.ENABLED_KEY, Constants.FALSE); projectJson.put(TwcConstants.FOREIGN_PROJECT, metadata); - projectIndex.update(projectJson); + projectPersistence.update(projectJson); } - private ProjectJson getProjectJson(Project project) { - ProjectJson projectJson = projectIndex.findById(project.getDocId()).orElse(null); - if(projectJson == null) { - logger.error("Could not locate project in project index: " + project.getDocId()); - throw new NotFoundException("Could not locate project in project index"); - } - return projectJson; - } } diff --git a/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcBranchPermissionsDelegate.java b/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcBranchPermissionsDelegate.java index c19e0fe16..795436b32 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcBranchPermissionsDelegate.java +++ b/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcBranchPermissionsDelegate.java @@ -5,7 +5,7 @@ import org.openmbee.mms.core.objects.PermissionUpdateRequest; import org.openmbee.mms.core.objects.PermissionUpdateResponse; import org.openmbee.mms.core.objects.PermissionUpdatesResponse; -import org.openmbee.mms.data.domains.global.Branch; +import org.openmbee.mms.json.RefJson; import org.openmbee.mms.twc.TeamworkCloud; import org.openmbee.mms.twc.exceptions.TwcConfigurationException; import org.openmbee.mms.twc.utilities.TwcPermissionUtils; @@ -16,7 +16,7 @@ public class TwcBranchPermissionsDelegate implements PermissionsDelegate{ - private Branch branch; + private RefJson branch; private TeamworkCloud teamworkCloud; private String workspaceId; private String resourceId; @@ -25,7 +25,7 @@ public class TwcBranchPermissionsDelegate implements PermissionsDelegate{ private TwcPermissionUtils twcPermissionUtils; - public TwcBranchPermissionsDelegate(Branch branch, TeamworkCloud teamworkCloud, String workspaceId, + public TwcBranchPermissionsDelegate(RefJson branch, TeamworkCloud teamworkCloud, String workspaceId, String resourceId) { this.branch = branch; this.teamworkCloud = teamworkCloud; @@ -46,6 +46,13 @@ public boolean hasPermission(String user, Set groups, String privilege) return hasPermission; } + @Override + public boolean hasGroupPermissions(String group, String privilege) { + throw new TwcConfigurationException(HttpStatus.BAD_REQUEST, + "Cannot Query Group Roles. Permissions for this branch are controlled by Teamwork Cloud (" + + teamworkCloud.getUrl() + ")"); + } + @Override public void initializePermissions(String creator) { //Do nothing, permissions are already initialized in TWC @@ -62,7 +69,12 @@ public boolean setInherit(boolean isInherit) { return false; } - @Override + @Override + public PermissionResponse getInherit() { + return PermissionResponse.getDefaultResponse(); + } + + @Override public void setPublic(boolean isPublic) { throw new TwcConfigurationException(HttpStatus.BAD_REQUEST, "Cannot Modify Permissions. Permissions for this branch are controlled by Teamwork Cloud (" @@ -103,7 +115,7 @@ public PermissionUpdatesResponse recalculateInheritedPerms() { return null; } - public Branch getBranch() { + public RefJson getBranch() { return branch; } diff --git a/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcPermissionsDelegateFactory.java b/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcPermissionsDelegateFactory.java index 176261ad0..f25d9a0f0 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcPermissionsDelegateFactory.java +++ b/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcPermissionsDelegateFactory.java @@ -1,12 +1,13 @@ package org.openmbee.mms.twc.permissions; +import org.openmbee.mms.core.dao.ProjectPersistence; import org.openmbee.mms.core.delegation.PermissionsDelegate; import org.openmbee.mms.core.delegation.PermissionsDelegateFactory; +import org.openmbee.mms.json.OrgJson; +import org.openmbee.mms.json.ProjectJson; +import org.openmbee.mms.json.RefJson; import org.openmbee.mms.twc.TeamworkCloud; import org.openmbee.mms.core.exceptions.NotFoundException; -import org.openmbee.mms.data.domains.global.Branch; -import org.openmbee.mms.data.domains.global.Organization; -import org.openmbee.mms.data.domains.global.Project; import org.openmbee.mms.twc.config.TwcConfig; import org.openmbee.mms.twc.exceptions.TwcConfigurationException; import org.openmbee.mms.twc.metadata.TwcMetadata; @@ -15,13 +16,14 @@ import org.springframework.context.ApplicationContext; import org.springframework.http.HttpStatus; +import java.util.Optional; public class TwcPermissionsDelegateFactory implements PermissionsDelegateFactory { private ApplicationContext applicationContext; private TwcConfig twcConfig; private TwcMetadataService twcMetadataService; - + private ProjectPersistence projectPersistence; @Autowired public void setApplicationContext(ApplicationContext applicationContext) { @@ -38,8 +40,13 @@ public void setTwcMetadataService(TwcMetadataService twcMetadataService) { this.twcMetadataService = twcMetadataService; } + @Autowired + public void setProjectPersistence(ProjectPersistence projectPersistence) { + this.projectPersistence = projectPersistence; + } + @Override - public PermissionsDelegate getPermissionsDelegate(Project project) { + public PermissionsDelegate getPermissionsDelegate(ProjectJson project) { if(!twcConfig.isUseAuthDelegation()) { return null; } @@ -54,7 +61,7 @@ public PermissionsDelegate getPermissionsDelegate(Project project) { } @Override - public PermissionsDelegate getPermissionsDelegate(Organization organization) { + public PermissionsDelegate getPermissionsDelegate(OrgJson organization) { if(!twcConfig.isUseAuthDelegation()) { return null; } @@ -64,17 +71,22 @@ public PermissionsDelegate getPermissionsDelegate(Organization organization) { } @Override - public PermissionsDelegate getPermissionsDelegate(Branch branch) { + public PermissionsDelegate getPermissionsDelegate(RefJson branch) { if(!twcConfig.isUseAuthDelegation()) { return null; } - TwcProjectDetails twcProjectDetails = getTwcDetails(branch.getProject()); + Optional projectOptional = projectPersistence.findById(branch.getProjectId()); + + if(projectOptional.isEmpty()) { + throw new NotFoundException("project not found"); + } + + TwcProjectDetails twcProjectDetails = getTwcDetails(projectOptional.get()); if(twcProjectDetails != null) { return autowire(new TwcBranchPermissionsDelegate(branch, twcProjectDetails.getTeamworkCloud(), twcProjectDetails.getWorkspaceId(), twcProjectDetails.getResourceId())); } - return null; } @@ -114,7 +126,7 @@ public void setResourceId(String resourceId) { } } - private TwcProjectDetails getTwcDetails(Project project) { + private TwcProjectDetails getTwcDetails(ProjectJson project) { TwcMetadata twcMetadata = null; try { twcMetadata = twcMetadataService.getTwcMetadata(project); @@ -129,7 +141,7 @@ private TwcProjectDetails getTwcDetails(Project project) { if(teamworkCloud == null) { throw new TwcConfigurationException(HttpStatus.FAILED_DEPENDENCY, - "Project " + project.getProjectId() + " (" + project.getProjectName() + "Project " + project.getProjectId() + " (" + project.getName() + ") is associated with an untrusted TWC host (" + twcMetadata.getHost() + ")"); } diff --git a/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcProjectPermissionsDelegate.java b/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcProjectPermissionsDelegate.java index 68a59d648..df4f9a068 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcProjectPermissionsDelegate.java +++ b/twc/src/main/java/org/openmbee/mms/twc/permissions/TwcProjectPermissionsDelegate.java @@ -5,8 +5,8 @@ import org.openmbee.mms.core.objects.PermissionUpdateRequest; import org.openmbee.mms.core.objects.PermissionUpdateResponse; import org.openmbee.mms.core.objects.PermissionUpdatesResponse; -import org.openmbee.mms.data.domains.global.Project; +import org.openmbee.mms.json.ProjectJson; import org.openmbee.mms.twc.TeamworkCloud; import org.openmbee.mms.twc.exceptions.TwcConfigurationException; import org.openmbee.mms.twc.utilities.TwcPermissionUtils; @@ -17,7 +17,7 @@ public class TwcProjectPermissionsDelegate implements PermissionsDelegate { - private Project project; + private ProjectJson project; private TeamworkCloud teamworkCloud; private String workspaceId; private String resourceId; @@ -25,7 +25,7 @@ public class TwcProjectPermissionsDelegate implements PermissionsDelegate { @Autowired private TwcPermissionUtils twcPermissionUtils; - public TwcProjectPermissionsDelegate(Project project, TeamworkCloud teamworkCloud, String workspaceId, + public TwcProjectPermissionsDelegate(ProjectJson project, TeamworkCloud teamworkCloud, String workspaceId, String resourceId) { this.project = project; this.teamworkCloud = teamworkCloud; @@ -47,6 +47,13 @@ public boolean hasPermission(String user, Set groups, String privilege) return hasPermission; } + @Override + public boolean hasGroupPermissions(String group, String privilege) { + throw new TwcConfigurationException(HttpStatus.BAD_REQUEST, + "Cannot Query Group Roles. Permissions for this project are controlled by Teamwork Cloud (" + + teamworkCloud.getUrl() + ")"); + } + @Override public void initializePermissions(String creator) { //Do nothing, permissions are already initialized in TWC @@ -63,7 +70,12 @@ public boolean setInherit(boolean isInherit) { return false; } - @Override + @Override + public PermissionResponse getInherit() { + return PermissionResponse.getDefaultResponse(); + } + + @Override public void setPublic(boolean isPublic) { throw new TwcConfigurationException(HttpStatus.BAD_REQUEST, "Cannot Modify Permissions. Permissions for this project are controlled by Teamwork Cloud (" @@ -107,7 +119,7 @@ public PermissionUpdatesResponse recalculateInheritedPerms() { return null; } - public Project getProject() { + public ProjectJson getProject() { return project; } diff --git a/twc/src/main/java/org/openmbee/mms/twc/security/TwcAuthenticationProvider.java b/twc/src/main/java/org/openmbee/mms/twc/security/TwcAuthenticationProvider.java index 9ba5bd164..5fe6da7f7 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/security/TwcAuthenticationProvider.java +++ b/twc/src/main/java/org/openmbee/mms/twc/security/TwcAuthenticationProvider.java @@ -14,13 +14,13 @@ public class TwcAuthenticationProvider { - RestUtils restUtils; + RestUtils restUtils; private TeamworkCloud twc; - + public TwcAuthenticationProvider(RestUtils restTemplateFactory, TeamworkCloud twc) { - this.restUtils = restTemplateFactory; - this.twc = twc; + this.restUtils = restTemplateFactory; + this.twc = twc; } public String getAuthentication(String authToken) { @@ -36,11 +36,12 @@ public String getAuthentication(String authToken) { String loggedInUser = restUtils.getCookieValue(respEntity, TwcConstants.TWCCURRENTUSER); return loggedInUser; } - private ResponseEntity checkAuthentication(String authToken) { - RestTemplate restTemplate = restUtils.getRestTemplate(); + RestTemplate restTemplate = restUtils.getRestTemplate(); HttpHeaders headers = new HttpHeaders(); + //2021x R2 - found iss of handling token format + authToken = authToken.replace(":", ""); headers.set(RestUtils.AUTHORIZATION, authToken); ResponseEntity respEntity = null; diff --git a/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetails.java b/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetails.java index 7500988e2..1076b7fe0 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetails.java +++ b/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetails.java @@ -1,8 +1,8 @@ package org.openmbee.mms.twc.security; import org.openmbee.mms.core.config.AuthorizationConstants; -import org.openmbee.mms.data.domains.global.Group; -import org.openmbee.mms.data.domains.global.User; +import org.openmbee.mms.json.GroupJson; +import org.openmbee.mms.json.UserJson; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -12,23 +12,24 @@ class TwcUserDetails implements UserDetails { - private final User user; + private final UserJson user; + private final Collection groups; - public TwcUserDetails(User user) { + public TwcUserDetails(UserJson user, Collection groups) { this.user = user; + this.groups = groups; } @Override public Collection getAuthorities() { - Collection groups = user.getGroups(); Collection authorities = new ArrayList<>(); if (groups != null) { - for (Group group : groups) { + for (GroupJson group : groups) { authorities.add(new SimpleGrantedAuthority(group.getName())); } } - if (user.isAdmin()) { + if (Boolean.TRUE.equals(user.isAdmin())) { authorities.add(new SimpleGrantedAuthority(AuthorizationConstants.MMSADMIN)); } authorities.add(new SimpleGrantedAuthority(AuthorizationConstants.EVERYONE)); @@ -62,10 +63,10 @@ public boolean isCredentialsNonExpired() { @Override public boolean isEnabled() { - return user.getEnabled(); + return user.isEnabled(); } - public User getUser() { + public UserJson getUser() { return user; } diff --git a/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetailsService.java b/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetailsService.java index 336d0830f..1eed02611 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetailsService.java +++ b/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetailsService.java @@ -1,7 +1,8 @@ package org.openmbee.mms.twc.security; -import org.openmbee.mms.data.domains.global.User; -import org.openmbee.mms.rdb.repositories.UserRepository; +import org.openmbee.mms.core.dao.UserGroupsPersistence; +import org.openmbee.mms.core.dao.UserPersistence; +import org.openmbee.mms.json.UserJson; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -14,34 +15,39 @@ @Service public class TwcUserDetailsService implements UserDetailsService { - private UserRepository userRepository; + private UserPersistence userPersistence; + private UserGroupsPersistence userGroupsPersistence; @Autowired - public void setUserRepository(UserRepository userRepository) { - this.userRepository = userRepository; + public void setUserPersistence(UserPersistence userPersistence) { + this.userPersistence = userPersistence; + } + + @Autowired + public void setUserGroupsPersistence(UserGroupsPersistence userGroupsPersistence) { + this.userGroupsPersistence = userGroupsPersistence; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - Optional user = userRepository.findByUsernameIgnoreCase(username); + Optional userOptional = userPersistence.findByUsername(username); - User u; - if (!user.isPresent()) { - u = addUser(username); + UserJson user; + if (userOptional.isEmpty()) { + user = addUser(username); } else { - u = user.get(); + user = userOptional.get(); } - return new TwcUserDetails(u); + return new TwcUserDetails(user, userGroupsPersistence.findGroupsAssignedToUser(username)); } @Transactional - public User addUser(String username) { - User user = new User(); + public UserJson addUser(String username) { + UserJson user = new UserJson(); user.setUsername(username); //TODO: fill in user details from TWC user.setEnabled(true); - userRepository.save(user); - return user; + return userPersistence.save(user); } } diff --git a/twc/src/main/java/org/openmbee/mms/twc/services/TwcRevisionMmsCommitMapService.java b/twc/src/main/java/org/openmbee/mms/twc/services/TwcRevisionMmsCommitMapService.java index 6361a60bd..56bdf20bd 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/services/TwcRevisionMmsCommitMapService.java +++ b/twc/src/main/java/org/openmbee/mms/twc/services/TwcRevisionMmsCommitMapService.java @@ -1,16 +1,14 @@ package org.openmbee.mms.twc.services; -import org.openmbee.mms.core.config.ContextHolder; import org.openmbee.mms.core.config.Formats; -import org.openmbee.mms.core.dao.BranchDAO; +import org.openmbee.mms.core.dao.BranchPersistence; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.core.exceptions.NotFoundException; import org.openmbee.mms.core.objects.CommitsResponse; import org.openmbee.mms.core.services.NodeService; import org.openmbee.mms.crud.services.DefaultNodeService; -import org.openmbee.mms.data.domains.scoped.Branch; -import org.openmbee.mms.data.domains.scoped.Commit; import org.openmbee.mms.json.CommitJson; +import org.openmbee.mms.json.RefJson; import org.openmbee.mms.twc.constants.TwcConstants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -20,11 +18,11 @@ @Service("twcRevisionMmsCommitMapService") public class TwcRevisionMmsCommitMapService extends DefaultNodeService implements NodeService { - private BranchDAO branchRepository; + private BranchPersistence branchPersistence; @Autowired - public void setBranchRepository(BranchDAO branchRepository) { - this.branchRepository = branchRepository; + public void setBranchPersistence(BranchPersistence branchPersistence) { + this.branchPersistence = branchPersistence; } /** @@ -39,13 +37,12 @@ public CommitsResponse updateTwcRevisionID(String projectId, String commitId, St if (revisionId == null || revisionId.isEmpty()) { return commitsResponse.addMessage("Revision id can not be empty"); } - ContextHolder.setContext(projectId); - Optional commitJsonDetails = commitIndex.findById(commitId); + Optional commitJsonDetails = commitPersistence.findById(projectId, commitId); try { if (commitJsonDetails.isPresent()) { CommitJson commitObj = commitJsonDetails.get(); commitObj.put(TwcConstants.TWCREVISIONID, revisionId); - commitsResponse.getCommits().add(this.commitIndex.update(commitObj)); + commitsResponse.getCommits().add(commitPersistence.update(commitObj)); } else { throw new NotFoundException(commitsResponse); } @@ -66,18 +63,12 @@ public CommitsResponse updateTwcRevisionID(String projectId, String commitId, St */ public List getTwcRevisionList(String projectId, String refId, Boolean reverseOrder, Integer limit) { List commits = new ArrayList<>(); - ContextHolder.setContext(projectId); - Optional ref = branchRepository.findByBranchId(refId); + Optional ref = branchPersistence.findById(projectId, refId); if (!ref.isPresent()) { throw new NotFoundException("Branch not found"); } try { - List refCommits = commitRepository.findByRefAndTimestampAndLimit(ref.get(), null, 0); - Set commitIds = new HashSet<>(); - refCommits.stream().forEach(commit -> { - commitIds.add(commit.getCommitId()); - }); - List commitJsonList = commitIndex.findAllById(commitIds); + List commitJsonList = commitPersistence.findByProjectAndRefAndTimestampAndLimit(projectId, refId, null, 0); if (null != commitJsonList && commitJsonList.size() > 0) { commitJsonList.stream().forEach(commitJsonData -> { if (commitJsonData.containsKey(TwcConstants.TWCREVISIONID)) { @@ -109,8 +100,8 @@ public CommitsComparator(Boolean reverseOrder) { @Override public int compare(CommitJson o, CommitJson t1) { try { - Date d1 = Formats.SDF.parse((String) o.get(CommitJson.CREATED)); - Date d2 = Formats.SDF.parse((String) t1.get(CommitJson.CREATED)); + Date d1 = Formats.SIMPLE_DATE_FORMAT.parse((String) o.get(CommitJson.CREATED)); + Date d2 = Formats.SIMPLE_DATE_FORMAT.parse((String) t1.get(CommitJson.CREATED)); return ascending ? d1.compareTo(d2) : d2.compareTo(d1); } catch (ParseException e) { logger.error("Error parsing commit dates: " + e.getMessage()); diff --git a/twc/src/main/java/org/openmbee/mms/twc/utilities/TwcPermissionUtils.java b/twc/src/main/java/org/openmbee/mms/twc/utilities/TwcPermissionUtils.java index 738dd2abc..d3dba2678 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/utilities/TwcPermissionUtils.java +++ b/twc/src/main/java/org/openmbee/mms/twc/utilities/TwcPermissionUtils.java @@ -1,9 +1,7 @@ package org.openmbee.mms.twc.utilities; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - +import org.json.JSONArray; +import org.json.JSONObject; import org.openmbee.mms.twc.TeamworkCloud; import org.openmbee.mms.twc.TeamworkCloudEndpoints; import org.openmbee.mms.twc.constants.TwcConstants; @@ -13,10 +11,11 @@ import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; - import org.springframework.web.client.RestTemplate; -import org.json.JSONArray; -import org.json.JSONObject; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; @Component public class TwcPermissionUtils { @@ -69,7 +68,7 @@ public boolean hasPermissionToAccessProject(String workspaceId, String resourceI for (int inx = 0; inx < twcRoles.size(); inx++) { roleId = projectRoleIdMap.get(twcRoles.get(inx)); - users = getUsersList(workspaceId, twc, resourceId, roleId); + users = getUsersList(twc, resourceId, roleId); if (users != null && users.contains(user)) return true; } @@ -86,13 +85,13 @@ public boolean hasPermissionToAccessProject(String workspaceId, String resourceI * @param roleId * @return */ - public List getUsersList(String workspaceId, TeamworkCloud twc, String resourceId, String roleId) { + public List getUsersList(TeamworkCloud twc, String resourceId, String roleId) { // TODO:: add distributed caching for performance List users = null; ResponseEntity respEntity = getRestResponse( - TeamworkCloudEndpoints.GETPROJECTUSERS.buildUrl(twc, workspaceId, resourceId, roleId), twc); + TeamworkCloudEndpoints.GETPROJECTUSERS.buildUrl(twc, resourceId, roleId), twc); if (respEntity == null || respEntity.getBody() == null) return null; @@ -123,11 +122,12 @@ public Map getTwcRolesForGivenResourceId(String resourceId, Team return null; JSONArray rolesJsonArray = jsonUtils.parseStringToJsonArray(respEntity.getBody()); - - for (int idx = 0; idx < rolesJsonArray.length(); idx++) { - JSONObject roleJsonObj = rolesJsonArray.getJSONObject(idx); - roleNameIDMap.put(roleJsonObj.getString(TwcConstants.NAME_JSONOBJECT), - roleJsonObj.getString(TwcConstants.ID_JSONOBJECT)); + if (rolesJsonArray != null) { + for (int idx = 0; idx < rolesJsonArray.length(); idx++) { + JSONObject roleJsonObj = rolesJsonArray.getJSONObject(idx); + roleNameIDMap.put(roleJsonObj.getString(TwcConstants.NAME_JSONOBJECT), + roleJsonObj.getString(TwcConstants.ID_JSONOBJECT)); + } } return roleNameIDMap; diff --git a/twc/src/test/java/org/openmbee/mms/twc/permissions/TwcPermissionsDelegateFactoryTest.java b/twc/src/test/java/org/openmbee/mms/twc/permissions/TwcPermissionsDelegateFactoryTest.java index cbe38dec3..e74973177 100644 --- a/twc/src/test/java/org/openmbee/mms/twc/permissions/TwcPermissionsDelegateFactoryTest.java +++ b/twc/src/test/java/org/openmbee/mms/twc/permissions/TwcPermissionsDelegateFactoryTest.java @@ -1,10 +1,11 @@ package org.openmbee.mms.twc.permissions; import org.junit.Test; +import org.openmbee.mms.core.dao.ProjectPersistence; import org.openmbee.mms.core.delegation.PermissionsDelegate; -import org.openmbee.mms.data.domains.global.Branch; -import org.openmbee.mms.data.domains.global.Organization; -import org.openmbee.mms.data.domains.global.Project; +import org.openmbee.mms.json.OrgJson; +import org.openmbee.mms.json.ProjectJson; +import org.openmbee.mms.json.RefJson; import org.openmbee.mms.twc.TeamworkCloud; import org.openmbee.mms.twc.config.TwcConfig; import org.openmbee.mms.twc.exceptions.TwcConfigurationException; @@ -13,6 +14,8 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.ApplicationContext; +import java.util.Optional; + import static org.junit.Assert.*; import static org.mockito.Mockito.*; @@ -23,6 +26,7 @@ public class TwcPermissionsDelegateFactoryTest { public void testTwcOrg() { ApplicationContext applicationContext = mock(ApplicationContext.class); TwcConfig twcConfig = mock(TwcConfig.class); + when(twcConfig.isUseAuthDelegation()).thenReturn(true); TwcMetadataService twcMetadataService = mock(TwcMetadataService.class); when(twcConfig.isUseAuthDelegation()).thenReturn(true); @@ -33,7 +37,7 @@ public void testTwcOrg() { twcPermissionsDelegateFactory.setTwcMetadataService(twcMetadataService); //These are not supported - assertNull( twcPermissionsDelegateFactory.getPermissionsDelegate(new Organization())); + assertNull( twcPermissionsDelegateFactory.getPermissionsDelegate(new OrgJson())); } @Test @@ -41,16 +45,15 @@ public void testTwcProject() { ApplicationContext applicationContext = mock(ApplicationContext.class); TwcConfig twcConfig = mock(TwcConfig.class); - TwcMetadataService twcMetadataService = mock(TwcMetadataService.class); - when(twcConfig.isUseAuthDelegation()).thenReturn(true); + TwcMetadataService twcMetadataService = mock(TwcMetadataService.class); TwcPermissionsDelegateFactory twcPermissionsDelegateFactory = new TwcPermissionsDelegateFactory(); twcPermissionsDelegateFactory.setApplicationContext(applicationContext); twcPermissionsDelegateFactory.setTwcConfig(twcConfig); twcPermissionsDelegateFactory.setTwcMetadataService(twcMetadataService); - Project project = new Project(); + ProjectJson project = new ProjectJson(); TwcMetadata twcMetadata = new TwcMetadata(); twcMetadata.setHost("host"); twcMetadata.setWorkspaceId("workspace"); @@ -81,6 +84,7 @@ public void testTwcProject() { public void testTwcProjectDoesNotMatchTwc() { ApplicationContext applicationContext = mock(ApplicationContext.class); TwcConfig twcConfig = mock(TwcConfig.class); + when(twcConfig.isUseAuthDelegation()).thenReturn(true); TwcMetadataService twcMetadataService = mock(TwcMetadataService.class); when(twcConfig.isUseAuthDelegation()).thenReturn(true); @@ -90,7 +94,7 @@ public void testTwcProjectDoesNotMatchTwc() { twcPermissionsDelegateFactory.setTwcConfig(twcConfig); twcPermissionsDelegateFactory.setTwcMetadataService(twcMetadataService); - Project project = new Project(); + ProjectJson project = new ProjectJson(); TwcMetadata twcMetadata = new TwcMetadata(); twcMetadata.setHost("host"); twcMetadata.setWorkspaceId("workspace"); @@ -110,6 +114,7 @@ public void testTwcProjectDoesNotMatchTwc() { public void testTwcProjectIncompleteMetadata() { ApplicationContext applicationContext = mock(ApplicationContext.class); TwcConfig twcConfig = mock(TwcConfig.class); + when(twcConfig.isUseAuthDelegation()).thenReturn(true); TwcMetadataService twcMetadataService = mock(TwcMetadataService.class); TwcPermissionsDelegateFactory twcPermissionsDelegateFactory = new TwcPermissionsDelegateFactory(); @@ -117,7 +122,7 @@ public void testTwcProjectIncompleteMetadata() { twcPermissionsDelegateFactory.setTwcConfig(twcConfig); twcPermissionsDelegateFactory.setTwcMetadataService(twcMetadataService); - Project project = new Project(); + ProjectJson project = new ProjectJson(); TwcMetadata twcMetadata = new TwcMetadata(); twcMetadata.setHost("host"); twcMetadata.setWorkspaceId("workspace"); @@ -134,6 +139,7 @@ public void testTwcProjectIncompleteMetadata() { public void testTwcProjectNullMetadata() { ApplicationContext applicationContext = mock(ApplicationContext.class); TwcConfig twcConfig = mock(TwcConfig.class); + when(twcConfig.isUseAuthDelegation()).thenReturn(true); TwcMetadataService twcMetadataService = mock(TwcMetadataService.class); TwcPermissionsDelegateFactory twcPermissionsDelegateFactory = new TwcPermissionsDelegateFactory(); @@ -141,7 +147,7 @@ public void testTwcProjectNullMetadata() { twcPermissionsDelegateFactory.setTwcConfig(twcConfig); twcPermissionsDelegateFactory.setTwcMetadataService(twcMetadataService); - Project project = new Project(); + ProjectJson project = new ProjectJson(); when(twcMetadataService.getTwcMetadata(project)).thenReturn(null); @@ -156,7 +162,9 @@ public void testTwcBranch() { ApplicationContext applicationContext = mock(ApplicationContext.class); TwcConfig twcConfig = mock(TwcConfig.class); + when(twcConfig.isUseAuthDelegation()).thenReturn(true); TwcMetadataService twcMetadataService = mock(TwcMetadataService.class); + ProjectPersistence projectPersistence = mock(ProjectPersistence.class); when(twcConfig.isUseAuthDelegation()).thenReturn(true); @@ -164,16 +172,19 @@ public void testTwcBranch() { twcPermissionsDelegateFactory.setApplicationContext(applicationContext); twcPermissionsDelegateFactory.setTwcConfig(twcConfig); twcPermissionsDelegateFactory.setTwcMetadataService(twcMetadataService); + twcPermissionsDelegateFactory.setProjectPersistence(projectPersistence); - Project project = new Project(); - Branch branch = new Branch(); - branch.setProject(project); + ProjectJson project = new ProjectJson(); + RefJson branch = new RefJson(); + String projectId = "projectid"; + branch.setProjectId(projectId); TwcMetadata twcMetadata = new TwcMetadata(); twcMetadata.setHost("host"); twcMetadata.setWorkspaceId("workspace"); twcMetadata.setResourceId("resource"); + when(projectPersistence.findById(projectId)).thenReturn(Optional.of(project)); when(twcMetadataService.getTwcMetadata(project)).thenReturn(twcMetadata); TeamworkCloud teamworkCloud = new TeamworkCloud(); @@ -199,7 +210,9 @@ public void testTwcBranch() { public void testTwcBranchDoesNotMatchTwc() { ApplicationContext applicationContext = mock(ApplicationContext.class); TwcConfig twcConfig = mock(TwcConfig.class); + when(twcConfig.isUseAuthDelegation()).thenReturn(true); TwcMetadataService twcMetadataService = mock(TwcMetadataService.class); + ProjectPersistence projectPersistence = mock(ProjectPersistence.class); when(twcConfig.isUseAuthDelegation()).thenReturn(true); @@ -207,16 +220,19 @@ public void testTwcBranchDoesNotMatchTwc() { twcPermissionsDelegateFactory.setApplicationContext(applicationContext); twcPermissionsDelegateFactory.setTwcConfig(twcConfig); twcPermissionsDelegateFactory.setTwcMetadataService(twcMetadataService); + twcPermissionsDelegateFactory.setProjectPersistence(projectPersistence); - Project project = new Project(); - Branch branch = new Branch(); - branch.setProject(project); + ProjectJson project = new ProjectJson(); + RefJson branch = new RefJson(); + String projectId = "projectid"; + branch.setProjectId(projectId); TwcMetadata twcMetadata = new TwcMetadata(); twcMetadata.setHost("host"); twcMetadata.setWorkspaceId("workspace"); twcMetadata.setResourceId("resource"); + when(projectPersistence.findById(projectId)).thenReturn(Optional.of(project)); when(twcMetadataService.getTwcMetadata(project)).thenReturn(twcMetadata); when(twcConfig.getTeamworkCloud("host")).thenReturn(null); @@ -231,22 +247,27 @@ public void testTwcBranchDoesNotMatchTwc() { public void testTwcBranchIncompleteMetadata() { ApplicationContext applicationContext = mock(ApplicationContext.class); TwcConfig twcConfig = mock(TwcConfig.class); + when(twcConfig.isUseAuthDelegation()).thenReturn(true); TwcMetadataService twcMetadataService = mock(TwcMetadataService.class); + ProjectPersistence projectPersistence = mock(ProjectPersistence.class); TwcPermissionsDelegateFactory twcPermissionsDelegateFactory = new TwcPermissionsDelegateFactory(); twcPermissionsDelegateFactory.setApplicationContext(applicationContext); twcPermissionsDelegateFactory.setTwcConfig(twcConfig); twcPermissionsDelegateFactory.setTwcMetadataService(twcMetadataService); + twcPermissionsDelegateFactory.setProjectPersistence(projectPersistence); - Project project = new Project(); - Branch branch = new Branch(); - branch.setProject(project); + ProjectJson project = new ProjectJson(); + RefJson branch = new RefJson(); + String projectId = "projectid"; + branch.setProjectId(projectId); TwcMetadata twcMetadata = new TwcMetadata(); twcMetadata.setHost("host"); twcMetadata.setWorkspaceId("workspace"); //twcMetadata.setResourceId("resource"); //Resource is missing + when(projectPersistence.findById(projectId)).thenReturn(Optional.of(project)); when(twcMetadataService.getTwcMetadata(project)).thenReturn(twcMetadata); PermissionsDelegate delegate = twcPermissionsDelegateFactory.getPermissionsDelegate(branch); @@ -258,17 +279,22 @@ public void testTwcBranchIncompleteMetadata() { public void testTwcBranchNullMetadata() { ApplicationContext applicationContext = mock(ApplicationContext.class); TwcConfig twcConfig = mock(TwcConfig.class); + when(twcConfig.isUseAuthDelegation()).thenReturn(true); TwcMetadataService twcMetadataService = mock(TwcMetadataService.class); + ProjectPersistence projectPersistence = mock(ProjectPersistence.class); TwcPermissionsDelegateFactory twcPermissionsDelegateFactory = new TwcPermissionsDelegateFactory(); twcPermissionsDelegateFactory.setApplicationContext(applicationContext); twcPermissionsDelegateFactory.setTwcConfig(twcConfig); twcPermissionsDelegateFactory.setTwcMetadataService(twcMetadataService); + twcPermissionsDelegateFactory.setProjectPersistence(projectPersistence); - Project project = new Project(); - Branch branch = new Branch(); - branch.setProject(project); + ProjectJson project = new ProjectJson(); + RefJson branch = new RefJson(); + String projectId = "projectid"; + branch.setProjectId(projectId); + when(projectPersistence.findById(projectId)).thenReturn(Optional.of(project)); when(twcMetadataService.getTwcMetadata(project)).thenReturn(null); PermissionsDelegate delegate = twcPermissionsDelegateFactory.getPermissionsDelegate(branch); diff --git a/twc/src/test/java/org/openmbee/mms/twc/security/TwcAuthenticationFilterTest.java b/twc/src/test/java/org/openmbee/mms/twc/security/TwcAuthenticationFilterTest.java index ed214ad5c..20a727fef 100644 --- a/twc/src/test/java/org/openmbee/mms/twc/security/TwcAuthenticationFilterTest.java +++ b/twc/src/test/java/org/openmbee/mms/twc/security/TwcAuthenticationFilterTest.java @@ -23,7 +23,7 @@ public class TwcAuthenticationFilterTest { TwcConfig twcConfig; @Mock - TwcUserDetailsService userDetailsService; + TwcUserDetailsService userDetailsService; @Mock TwcAuthenticationProvider twcAuthProvider; diff --git a/twc/src/test/java/org/openmbee/mms/twc/utilities/TwcPermissionUtilsTest.java b/twc/src/test/java/org/openmbee/mms/twc/utilities/TwcPermissionUtilsTest.java index ed3c26f57..157db4bf3 100644 --- a/twc/src/test/java/org/openmbee/mms/twc/utilities/TwcPermissionUtilsTest.java +++ b/twc/src/test/java/org/openmbee/mms/twc/utilities/TwcPermissionUtilsTest.java @@ -12,17 +12,10 @@ import org.json.JSONArray; import org.json.JSONObject; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static junit.framework.TestCase.assertNull; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -282,13 +275,13 @@ public void getUsersListTestSuccesful() { .thenAnswer((invocation) -> { Object[] args = invocation.getArguments(); - assertEquals("http://twc.domain.com:8111/osmc/workspaces/workspaceID1/" + assertEquals("http://twc.domain.com:8111/osmc/" + "resources/resourceID1/roles/roleID1/users", args[0]); return responseEntity; }); - actualUsers = twcPermUtils.getUsersList(workspaceId, twc, resourceId, roleId); + actualUsers = twcPermUtils.getUsersList(twc, resourceId, roleId); assertTrue(expectedUsers.equals(actualUsers)); } @@ -344,13 +337,13 @@ public void nullResponseEntityBodyForUsersList() { .thenAnswer((invocation) -> { Object[] args = invocation.getArguments(); - assertEquals("http://twc.domain.com:8111/osmc/workspaces/workspaceID1/" + assertEquals("http://twc.domain.com:8111/osmc/" + "resources/resourceID1/roles/roleID1/users", args[0]); return responseEntity; }); - actualUsers = twcPermUtils.getUsersList(workspaceId, twc, resourceId, roleId); + actualUsers = twcPermUtils.getUsersList(twc, resourceId, roleId); assertNull(actualUsers); } @@ -395,7 +388,7 @@ public void nullResponseEntityForUserLists() { throw new RuntimeException("Test Exception -- should be caught"); }); - actualUsers = twcPermUtils.getUsersList(workspaceId, twc, resourceId, roleId); + actualUsers = twcPermUtils.getUsersList(twc, resourceId, roleId); assertNull(actualUsers); } diff --git a/twc/twc.gradle b/twc/twc.gradle index 9490ed461..8b03a4615 100644 --- a/twc/twc.gradle +++ b/twc/twc.gradle @@ -1,5 +1,4 @@ dependencies { - implementation project(':rdb') implementation project(':crud') @@ -12,3 +11,8 @@ dependencies { testImplementation commonDependencies.'spring-boot-starter-test' } +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } +} \ No newline at end of file diff --git a/view/README.md b/view/README.md new file mode 100644 index 000000000..680342455 --- /dev/null +++ b/view/README.md @@ -0,0 +1 @@ +## VIEW \ No newline at end of file diff --git a/view/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java b/view/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java new file mode 100644 index 000000000..b9a089862 --- /dev/null +++ b/view/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java @@ -0,0 +1,24 @@ +package org.openmbee.mms.view.controllers; + +import java.util.ArrayList; +import java.util.List; +import org.openmbee.mms.core.objects.BaseResponse; +import org.openmbee.mms.json.ElementJson; + +public class DocumentsResponse extends BaseResponse { + + private List documents; + + public DocumentsResponse() { + this.documents = new ArrayList<>(); + } + + public List getDocuments() { + return documents; + } + + public DocumentsResponse setDocuments(List documents) { + this.documents = documents; + return this; + } +} diff --git a/view/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java b/view/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java new file mode 100644 index 000000000..12202f559 --- /dev/null +++ b/view/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java @@ -0,0 +1,24 @@ +package org.openmbee.mms.view.controllers; + +import java.util.ArrayList; +import java.util.List; +import org.openmbee.mms.core.objects.BaseResponse; +import org.openmbee.mms.json.ElementJson; + +public class GroupsResponse extends BaseResponse { + + private List groups; + + public GroupsResponse() { + this.groups = new ArrayList<>(); + } + + public List getGroups() { + return groups; + } + + public GroupsResponse setGroups(List groups) { + this.groups = groups; + return this; + } +} diff --git a/view/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java b/view/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java new file mode 100644 index 000000000..756ab1122 --- /dev/null +++ b/view/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java @@ -0,0 +1,24 @@ +package org.openmbee.mms.view.controllers; + +import java.util.ArrayList; +import java.util.List; +import org.openmbee.mms.core.objects.BaseResponse; +import org.openmbee.mms.json.MountJson; + +public class MountsResponse extends BaseResponse { + + private List projects; + + public MountsResponse() { + this.projects = new ArrayList<>(); + } + + public List getProjects() { + return projects; + } + + public MountsResponse setProjects(List projects) { + this.projects = projects; + return this; + } +} diff --git a/view/src/main/java/org/openmbee/mms/view/controllers/VeController.java b/view/src/main/java/org/openmbee/mms/view/controllers/VeController.java new file mode 100644 index 000000000..480e336cb --- /dev/null +++ b/view/src/main/java/org/openmbee/mms/view/controllers/VeController.java @@ -0,0 +1,124 @@ +package org.openmbee.mms.view.controllers; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.ArrayList; +import java.util.Map; + +import org.openmbee.mms.view.services.ViewService; +import org.openmbee.mms.core.objects.ElementsRequest; +import org.openmbee.mms.core.objects.ElementsResponse; +import org.openmbee.mms.core.services.GenericServiceFactory; +import org.openmbee.mms.crud.controllers.BaseController; +import org.openmbee.mms.json.MountJson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@RequestMapping("/projects/{projectId}/refs/{refId}") +@Tag(name = "Views") +public class VeController extends BaseController { + + private GenericServiceFactory genericServiceFactory; + + @Autowired + public void setGenericServiceFactory( GenericServiceFactory serviceFactory){ + this.genericServiceFactory = serviceFactory; + } + + @GetMapping("/mounts") + @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") + public MountsResponse getMounts( + @PathVariable String projectId, + @PathVariable String refId, + @RequestParam(required = false) String commitId, + @RequestParam(required = false) Map params) { + MountJson json = genericServiceFactory.getServiceForSchema( ViewService.class ,getProjectType(projectId)).getProjectUsages(projectId, refId, params.get("commitId"), new ArrayList<>(), true); + MountsResponse res = new MountsResponse(); + res.getProjects().add(json); + return res; + } + + @GetMapping("/documents") + @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") + public DocumentsResponse getDocuments( + @PathVariable String projectId, + @PathVariable String refId, + @RequestParam(required = false) String commitId, + @RequestParam(required = false) Map params) { + + ElementsResponse docs = genericServiceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getDocuments(projectId, refId, params); + return (new DocumentsResponse()).setDocuments(docs.getElements()); + } + + @GetMapping("/views/{viewId}") + @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") + public ElementsResponse getView( + @PathVariable String projectId, + @PathVariable String refId, + @PathVariable String viewId, + @RequestParam(required = false) String commitId, + @RequestParam(required = false) Map params) { + + ElementsResponse res = genericServiceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getView(projectId, refId, viewId, params); + handleSingleResponse(res); + return res; + } + + @PutMapping("/views") + @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") + public ElementsResponse getViews( + @PathVariable String projectId, + @PathVariable String refId, + @RequestBody ElementsRequest req, + @RequestParam(required = false) String commitId, + @RequestParam(required = false) Map params) { + + return genericServiceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getViews(projectId, refId, req, params); + } + + @PostMapping("/views") + @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_EDIT_CONTENT', false)") + public ElementsResponse createOrUpdateViews( + @PathVariable String projectId, + @PathVariable String refId, + @RequestBody ElementsRequest req, + @RequestParam(required = false) String overwrite, + @RequestParam(required = false) Map params, + @Parameter(hidden = true) Authentication auth) { + + ViewService viewService = genericServiceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)); + ElementsResponse res = viewService.createOrUpdate(projectId, refId, req, params, auth.getName()); + // ToDo : the following is just a workaround till the indexing part is synchronized W + try { + Thread.sleep(1000); // Providing some delay for the indexing to complete as it uses async api call + } catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + // No need to handle this...... + } + viewService.addChildViews(res, params); + return res; + } + + @GetMapping("/groups") + @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") + public GroupsResponse getGroups( + @PathVariable String projectId, + @PathVariable String refId, + @RequestParam(required = false) Map params) { + + ElementsResponse groups = genericServiceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getGroups(projectId, refId, params); + return (new GroupsResponse()).setGroups(groups.getElements()); + } +} diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/PropertyData.java b/view/src/main/java/org/openmbee/mms/view/services/PropertyData.java similarity index 61% rename from cameo/src/main/java/org/openmbee/mms/cameo/services/PropertyData.java rename to view/src/main/java/org/openmbee/mms/view/services/PropertyData.java index e9f0847a4..f654d1de0 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/PropertyData.java +++ b/view/src/main/java/org/openmbee/mms/view/services/PropertyData.java @@ -1,15 +1,9 @@ -package org.openmbee.mms.cameo.services; +package org.openmbee.mms.view.services; -import org.openmbee.mms.data.domains.scoped.Node; import org.openmbee.mms.json.ElementJson; public class PropertyData { - private Node propertyNode; - - private Node assocNode; - - private Node assocPropertyNode; private ElementJson typeJson; @@ -21,13 +15,6 @@ public class PropertyData { private boolean isView; - public Node getPropertyNode() { - return propertyNode; - } - - public void setPropertyNode(Node propertyNode) { - this.propertyNode = propertyNode; - } public ElementJson getTypeJson() { return typeJson; @@ -53,14 +40,6 @@ public void setView(boolean view) { isView = view; } - public Node getAssocNode() { - return assocNode; - } - - public void setAssocNode(Node assocNode) { - this.assocNode = assocNode; - } - public ElementJson getAssocJson() { return assocJson; } @@ -69,13 +48,6 @@ public void setAssocJson(ElementJson assocJson) { this.assocJson = assocJson; } - public Node getAssocPropertyNode() { - return assocPropertyNode; - } - - public void setAssocPropertyNode(Node assocPropertyNode) { - this.assocPropertyNode = assocPropertyNode; - } public ElementJson getAssocPropertyJson() { return assocPropertyJson; diff --git a/view/src/main/java/org/openmbee/mms/view/services/ViewService.java b/view/src/main/java/org/openmbee/mms/view/services/ViewService.java new file mode 100644 index 000000000..a22dfdb32 --- /dev/null +++ b/view/src/main/java/org/openmbee/mms/view/services/ViewService.java @@ -0,0 +1,19 @@ +package org.openmbee.mms.view.services; + +import java.util.Map; +import org.openmbee.mms.core.objects.ElementsRequest; +import org.openmbee.mms.core.objects.ElementsResponse; +import org.openmbee.mms.core.services.HierarchicalNodeService; + +public interface ViewService extends HierarchicalNodeService { + + ElementsResponse getDocuments(String projectId, String refId, Map params); + + ElementsResponse getView(String projectId, String refId, String elementId, Map params); + + ElementsResponse getViews(String projectId, String refId, ElementsRequest req, Map params); + + void addChildViews(ElementsResponse res, Map params); + + ElementsResponse getGroups(String projectId, String refId, Map params); +} diff --git a/view/view.gradle b/view/view.gradle new file mode 100644 index 000000000..21102b288 --- /dev/null +++ b/view/view.gradle @@ -0,0 +1,5 @@ +dependencies { + implementation project(':crud') + + testImplementation commonDependencies.'spring-boot-starter-test' +} diff --git a/webhooks/src/main/java/org/openmbee/mms/webhooks/components/EventListener.java b/webhooks/src/main/java/org/openmbee/mms/webhooks/components/EventListener.java index 91835432f..afd377bba 100644 --- a/webhooks/src/main/java/org/openmbee/mms/webhooks/components/EventListener.java +++ b/webhooks/src/main/java/org/openmbee/mms/webhooks/components/EventListener.java @@ -1,8 +1,8 @@ package org.openmbee.mms.webhooks.components; -import org.openmbee.mms.core.dao.WebhookDAO; -import org.openmbee.mms.data.domains.global.Webhook; import org.openmbee.mms.core.objects.EventObject; +import org.openmbee.mms.webhooks.json.WebhookJson; +import org.openmbee.mms.webhooks.persistence.WebhookPersistence; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -27,18 +27,18 @@ public class EventListener implements ApplicationListener { @Value("${webhook.default.all:#{null}}") private Optional allEvents; - private WebhookDAO eventRepository; + private WebhookPersistence eventRepository; @Autowired - public void setEventRepository(WebhookDAO eventRepository) { + public void setEventRepository(WebhookPersistence eventRepository) { this.eventRepository = eventRepository; } @Override public void onApplicationEvent(EventObject eventObject) { - List webhooks = eventRepository.findAllByProject_ProjectId(eventObject.getProjectId()); + List webhooks = eventRepository.findAllByProjectId(eventObject.getProjectId()); - for (Webhook webhook : webhooks) { + for (WebhookJson webhook : webhooks) { sendWebhook(webhook.getUrl(), eventObject.getSource()); } diff --git a/webhooks/src/main/java/org/openmbee/mms/webhooks/controllers/WebhooksController.java b/webhooks/src/main/java/org/openmbee/mms/webhooks/controllers/WebhooksController.java index f2e5a26c4..fbb09c45d 100644 --- a/webhooks/src/main/java/org/openmbee/mms/webhooks/controllers/WebhooksController.java +++ b/webhooks/src/main/java/org/openmbee/mms/webhooks/controllers/WebhooksController.java @@ -3,19 +3,17 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.annotations.tags.Tag; -import org.openmbee.mms.core.dao.ProjectDAO; -import org.openmbee.mms.core.dao.WebhookDAO; +import org.openmbee.mms.core.dao.ProjectPersistence; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.exceptions.NotFoundException; -import org.openmbee.mms.data.domains.global.Project; -import org.openmbee.mms.data.domains.global.Webhook; +import org.openmbee.mms.json.ProjectJson; import org.openmbee.mms.webhooks.json.WebhookJson; import org.openmbee.mms.webhooks.objects.WebhookRequest; import org.openmbee.mms.webhooks.objects.WebhookResponse; +import org.openmbee.mms.webhooks.persistence.WebhookPersistence; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import java.util.*; @@ -25,18 +23,18 @@ @Tag(name = "Webhooks") public class WebhooksController { - private WebhookDAO webhookRepository; - private ProjectDAO projectRepository; + private ProjectPersistence projectPersistence; + private WebhookPersistence webhookPersistence; private ObjectMapper om; @Autowired - public void setWebhookRepository(WebhookDAO webhookRepository) { - this.webhookRepository = webhookRepository; + public void setProjectPersistence(ProjectPersistence projectPersistence) { + this.projectPersistence = projectPersistence; } @Autowired - public void setProjectRepository(ProjectDAO projectRepository) { - this.projectRepository = projectRepository; + public void setWebhookPersistence(WebhookPersistence webhookPersistence) { + this.webhookPersistence = webhookPersistence; } @Autowired @@ -49,20 +47,12 @@ public void setObjectMapper(ObjectMapper om) { public WebhookResponse getAllWebhooks(@PathVariable String projectId) { WebhookResponse response = new WebhookResponse(); - List webhooks = webhookRepository.findAllByProject_ProjectId(projectId); - - for (Webhook webhook : webhooks) { - WebhookJson webhookJson = new WebhookJson(); - webhookJson.merge(convertToMap(webhook)); - webhookJson.setId(webhook.getId().toString()); - webhookJson.setProjectId(projectId); - response.getWebhooks().add(webhookJson); - } + List webhooks = webhookPersistence.findAllByProjectId(projectId); + response.getWebhooks().addAll(webhooks); return response; } @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - @Transactional @PreAuthorize("@mss.hasProjectPrivilege(authentication, #projectId, 'PROJECT_CREATE_WEBHOOKS', false)") public WebhookResponse createOrUpdateWebhooks(@PathVariable String projectId, @RequestBody WebhookRequest webhooksPost) { @@ -71,28 +61,29 @@ public WebhookResponse createOrUpdateWebhooks(@PathVariable String projectId, @R } WebhookResponse response = new WebhookResponse(); - Optional project = projectRepository.findByProjectId(projectId); + Optional project = projectPersistence.findById(projectId); + if(project.isEmpty()) { + throw new NotFoundException("Project not found"); + } for (WebhookJson json: webhooksPost.getWebhooks()) { - Optional existing = webhookExists(json, projectId); - Webhook hook; - if (!existing.isPresent()) { - hook = new Webhook(); - hook.setProject(project.get()); - } else { - hook = existing.get(); + Optional existing = webhookExists(json, projectId); + if (existing.isPresent()) { + if (!projectId.equals(existing.get().getProjectId())) { + throw new BadRequestException("Cannot move webhooks between projects"); + } + json.merge(existing.get()); } - hook.setUrl(json.getUrl()); - webhookRepository.save(hook); - json.setId(hook.getId().toString()); json.setProjectId(projectId); - response.getWebhooks().add(json); + if(json.getId() == null) { + json.setId(UUID.randomUUID().toString()); + } + response.getWebhooks().add(webhookPersistence.save(json)); } return response; } @DeleteMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - @Transactional @PreAuthorize("@mss.hasProjectPrivilege(authentication, #projectId, 'PROJECT_CREATE_WEBHOOKS', false)") public WebhookResponse deleteWebhooks(@PathVariable String projectId, @RequestBody WebhookRequest webhookRequest) { @@ -102,13 +93,13 @@ public WebhookResponse deleteWebhooks(@PathVariable String projectId, @RequestBo } WebhookResponse response = new WebhookResponse(); - List webhooks = webhookRepository.findAllByProject_ProjectId(projectId); + List webhooks = webhookPersistence.findAllByProjectId(projectId); if (webhooks.isEmpty()) { throw new NotFoundException(response.addMessage("No web hooks found for project")); } - for (Webhook webhook : webhooks) { + for (WebhookJson webhook : webhooks) { if (uris.contains(webhook.getUrl())) { - webhookRepository.delete(webhook); + webhookPersistence.delete(webhook); response.addMessage(String.format("Web hook for project %s to %s deleted", projectId, webhook.getUrl())); } } @@ -119,13 +110,16 @@ public Map convertToMap(Object obj) { return om.convertValue(obj, new TypeReference>() {}); } - private Optional webhookExists(WebhookJson json, String projectId) { + private Optional webhookExists(WebhookJson json, String projectId) { if (json.getId() != null) { - Optional hook = webhookRepository.findById(Long.parseLong(json.getId())); - if (hook.isPresent() && hook.get().getProject().getProjectId().equals(projectId)) { + Optional hook = webhookPersistence.findById(json.getId()); + if(hook.isEmpty()) { + hook = webhookPersistence.findByProjectIdAndUrl(projectId, json.getUrl()); + } + if (hook.isPresent() && hook.get().getProjectId().equals(projectId)) { return hook; //ensure hook by id matches the project being requested } } - return webhookRepository.findByProject_ProjectIdAndUrl(projectId, json.getUrl()); + return webhookPersistence.findByProjectIdAndUrl(projectId, json.getUrl()); } } diff --git a/webhooks/src/main/java/org/openmbee/mms/webhooks/persistence/WebhookPersistence.java b/webhooks/src/main/java/org/openmbee/mms/webhooks/persistence/WebhookPersistence.java new file mode 100644 index 000000000..adaead0eb --- /dev/null +++ b/webhooks/src/main/java/org/openmbee/mms/webhooks/persistence/WebhookPersistence.java @@ -0,0 +1,19 @@ +package org.openmbee.mms.webhooks.persistence; + +import org.openmbee.mms.webhooks.json.WebhookJson; + +import java.util.List; +import java.util.Optional; + +public interface WebhookPersistence { + + WebhookJson save(WebhookJson webhook); + + Optional findById(String id); + + List findAllByProjectId(String projectId); + + Optional findByProjectIdAndUrl(String projectId, String url); + + void delete(WebhookJson webhook); +} diff --git a/webhooks/webhooks.gradle b/webhooks/webhooks.gradle index 378848af7..eefabc146 100644 --- a/webhooks/webhooks.gradle +++ b/webhooks/webhooks.gradle @@ -1,3 +1,9 @@ dependencies { implementation project(':core') +} + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } } \ No newline at end of file From 9e17c7b1d2291aebfe6e46717d175e29b0883da6 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Mon, 20 Mar 2023 09:59:37 -0400 Subject: [PATCH 02/60] Remove redundant commit save --- .../java/org/openmbee/mms/crud/services/DefaultNodeService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java index 5a5cf300d..d492a3a4f 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java @@ -187,7 +187,6 @@ public ElementsCommitResponse delete(String projectId, String refId, ElementsReq changes = nodePersistence.commitChanges(changes); - commitPersistence.save(changes.getCommitJson(), changes.getInstant()); ElementsCommitResponse response = new ElementsCommitResponse(); response.getElements().addAll(changes.getDeletedMap().values()); response.setRejected(new ArrayList<>(changes.getRejected().values())); From 240359a1bb7227da5c68209107facec2591a4538 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Mon, 20 Mar 2023 11:03:20 -0400 Subject: [PATCH 03/60] CodeQL updates --- .../mms/cameo/services/CameoNodeService.java | 1 + .../view/controllers/DocumentsResponse.java | 24 ---- .../mms/view/controllers/GroupsResponse.java | 24 ---- .../mms/view/controllers/MountsResponse.java | 24 ---- .../mms/view/controllers/VeController.java | 115 ------------------ .../PermissionUpdateResponseBuilder.java | 2 +- .../org/openmbee/mms/core/config/Formats.java | 2 - .../core/services/GenericServiceFactory.java | 4 +- .../mms/core/services/NodeChangeInfoImpl.java | 1 + .../openmbee/mms/crud/domain/JsonDomain.java | 2 +- .../mms/crud/domain/NodeChangeDomain.java | 6 +- .../crud/services/DefaultCommitService.java | 3 +- .../crud/services/ElementUtilsFactory.java | 1 + .../mms/crud/services/ServiceFactory.java | 1 + .../dao/FederatedNodePersistence.java | 4 - .../dao/FederatedProjectPersistence.java | 4 +- .../domain/FederatedNodeGetDomain.java | 8 +- .../DefaultBranchPermissionsDelegate.java | 3 +- .../DefaultOrgPermissionsDelegate.java | 3 +- .../DefaultProjectPermissionsDelegate.java | 3 +- .../mms/msosa/services/MsosaNodeService.java | 1 + .../msosa/services/MsosaProjectService.java | 1 + .../mms/oauth/security/OAuthProcessor.java | 2 +- .../TwcRevisionMmsCommitMapService.java | 6 +- 24 files changed, 31 insertions(+), 214 deletions(-) delete mode 100644 cameo/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java delete mode 100644 cameo/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java delete mode 100644 cameo/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java delete mode 100644 cameo/src/main/java/org/openmbee/mms/view/controllers/VeController.java diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java index 53a04e220..fc83864b6 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java @@ -87,6 +87,7 @@ public void extraProcessPostedElement(NodeChangeInfo info, ElementJson element) element.remove(CameoConstants.CHILDVIEWS); } + @Override public MountJson getProjectUsages(String projectId, String refId, String commitId, List> saw, boolean restrictOnPermissions) { saw.add(Pair.of(projectId, refId)); diff --git a/cameo/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java b/cameo/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java deleted file mode 100644 index b9a089862..000000000 --- a/cameo/src/main/java/org/openmbee/mms/view/controllers/DocumentsResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.openmbee.mms.view.controllers; - -import java.util.ArrayList; -import java.util.List; -import org.openmbee.mms.core.objects.BaseResponse; -import org.openmbee.mms.json.ElementJson; - -public class DocumentsResponse extends BaseResponse { - - private List documents; - - public DocumentsResponse() { - this.documents = new ArrayList<>(); - } - - public List getDocuments() { - return documents; - } - - public DocumentsResponse setDocuments(List documents) { - this.documents = documents; - return this; - } -} diff --git a/cameo/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java b/cameo/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java deleted file mode 100644 index 12202f559..000000000 --- a/cameo/src/main/java/org/openmbee/mms/view/controllers/GroupsResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.openmbee.mms.view.controllers; - -import java.util.ArrayList; -import java.util.List; -import org.openmbee.mms.core.objects.BaseResponse; -import org.openmbee.mms.json.ElementJson; - -public class GroupsResponse extends BaseResponse { - - private List groups; - - public GroupsResponse() { - this.groups = new ArrayList<>(); - } - - public List getGroups() { - return groups; - } - - public GroupsResponse setGroups(List groups) { - this.groups = groups; - return this; - } -} diff --git a/cameo/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java b/cameo/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java deleted file mode 100644 index 756ab1122..000000000 --- a/cameo/src/main/java/org/openmbee/mms/view/controllers/MountsResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.openmbee.mms.view.controllers; - -import java.util.ArrayList; -import java.util.List; -import org.openmbee.mms.core.objects.BaseResponse; -import org.openmbee.mms.json.MountJson; - -public class MountsResponse extends BaseResponse { - - private List projects; - - public MountsResponse() { - this.projects = new ArrayList<>(); - } - - public List getProjects() { - return projects; - } - - public MountsResponse setProjects(List projects) { - this.projects = projects; - return this; - } -} diff --git a/cameo/src/main/java/org/openmbee/mms/view/controllers/VeController.java b/cameo/src/main/java/org/openmbee/mms/view/controllers/VeController.java deleted file mode 100644 index 786232c97..000000000 --- a/cameo/src/main/java/org/openmbee/mms/view/controllers/VeController.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.openmbee.mms.view.controllers; - -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.ArrayList; -import java.util.Map; -import org.openmbee.mms.view.services.ViewService; -import org.openmbee.mms.core.objects.ElementsRequest; -import org.openmbee.mms.core.objects.ElementsResponse; -import org.openmbee.mms.core.services.GenericServiceFactory; -import org.openmbee.mms.crud.controllers.BaseController; -import org.openmbee.mms.json.MountJson; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - - -@RestController -@RequestMapping("/projects/{projectId}/refs/{refId}") -@Tag(name = "Views") -public class VeController extends BaseController { - - private GenericServiceFactory serviceFactory; - - @Autowired - public void setGenericServiceFactory( GenericServiceFactory serviceFactory){ - this.serviceFactory = serviceFactory; - } - - @GetMapping("/mounts") - @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") - public MountsResponse getMounts( - @PathVariable String projectId, - @PathVariable String refId, - @RequestParam(required = false) String commitId, - @RequestParam(required = false) Map params) { - MountJson json = serviceFactory.getServiceForSchema( ViewService.class ,getProjectType(projectId)).getProjectUsages(projectId, refId, params.get("commitId"), new ArrayList<>(), true); - MountsResponse res = new MountsResponse(); - res.getProjects().add(json); - return res; - } - - @GetMapping("/documents") - @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") - public DocumentsResponse getDocuments( - @PathVariable String projectId, - @PathVariable String refId, - @RequestParam(required = false) String commitId, - @RequestParam(required = false) Map params) { - - ElementsResponse docs = serviceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getDocuments(projectId, refId, params); - return (new DocumentsResponse()).setDocuments(docs.getElements()); - } - - @GetMapping("/views/{viewId}") - @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") - public ElementsResponse getView( - @PathVariable String projectId, - @PathVariable String refId, - @PathVariable String viewId, - @RequestParam(required = false) String commitId, - @RequestParam(required = false) Map params) { - - ElementsResponse res = serviceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getView(projectId, refId, viewId, params); - handleSingleResponse(res); - return res; - } - - @PutMapping("/views") - @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") - public ElementsResponse getViews( - @PathVariable String projectId, - @PathVariable String refId, - @RequestBody ElementsRequest req, - @RequestParam(required = false) String commitId, - @RequestParam(required = false) Map params) { - - return serviceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getViews(projectId, refId, req, params); - } - - @PostMapping("/views") - @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_EDIT_CONTENT', false)") - public ElementsResponse createOrUpdateViews( - @PathVariable String projectId, - @PathVariable String refId, - @RequestBody ElementsRequest req, - @RequestParam(required = false) String overwrite, - @RequestParam(required = false) Map params, - @Parameter(hidden = true) Authentication auth) { - - ViewService viewService = serviceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)); - ElementsResponse res = viewService.createOrUpdate(projectId, refId, req, params, auth.getName()); - viewService.addChildViews(res, params); - return res; - } - - @GetMapping("/groups") - @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") - public GroupsResponse getGroups( - @PathVariable String projectId, - @PathVariable String refId, - @RequestParam(required = false) Map params) { - - ElementsResponse groups = serviceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)).getGroups(projectId, refId, params); - return (new GroupsResponse()).setGroups(groups.getElements()); - } -} diff --git a/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdateResponseBuilder.java b/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdateResponseBuilder.java index 6abf03a23..f9a143d8a 100644 --- a/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdateResponseBuilder.java +++ b/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdateResponseBuilder.java @@ -6,7 +6,7 @@ public class PermissionUpdateResponseBuilder { - protected class PermissionUpdateWrapper { + protected static class PermissionUpdateWrapper { private PermissionUpdateResponse.PermissionUpdate permissionUpdate; diff --git a/core/src/main/java/org/openmbee/mms/core/config/Formats.java b/core/src/main/java/org/openmbee/mms/core/config/Formats.java index 5c2c3d1c8..9666837b3 100644 --- a/core/src/main/java/org/openmbee/mms/core/config/Formats.java +++ b/core/src/main/java/org/openmbee/mms/core/config/Formats.java @@ -1,13 +1,11 @@ package org.openmbee.mms.core.config; -import java.text.SimpleDateFormat; import java.time.ZoneId; import java.time.format.DateTimeFormatter; public class Formats { public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; - public static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(DATE_FORMAT); public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT).withZone( ZoneId.systemDefault()); diff --git a/core/src/main/java/org/openmbee/mms/core/services/GenericServiceFactory.java b/core/src/main/java/org/openmbee/mms/core/services/GenericServiceFactory.java index d9d7fa097..bc7fdf344 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/GenericServiceFactory.java +++ b/core/src/main/java/org/openmbee/mms/core/services/GenericServiceFactory.java @@ -13,12 +13,12 @@ import java.util.stream.Collectors; @Component -public class GenericServiceFactory implements ApplicationContextAware{ +public class GenericServiceFactory implements ApplicationContextAware { private ApplicationContext context; protected final Logger logger = LoggerFactory.getLogger(getClass()); - @Autowired + @Override public void setApplicationContext(ApplicationContext context) { this.context = context; } diff --git a/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java b/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java index ed0616488..d9f8319d6 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java +++ b/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java @@ -53,6 +53,7 @@ public NodeChangeInfo setDeletedMap(Map deletedMap) { return this; } + @Override public boolean getOverwrite() { return overwrite; } diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java index a0cd2a257..ed1a502fe 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java @@ -18,7 +18,7 @@ public Map convertJsonToMap(List elements) { if (elem == null) { continue; } - if (elem.getId() != null && !elem.getId().equals("")) { + if (elem.getId() != null && !elem.getId().isEmpty()) { result.put(elem.getId(), elem); } } diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java index 3dfc4a612..2e0fa2239 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java @@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory; import java.text.ParseException; +import java.text.SimpleDateFormat; import java.time.Instant; import java.util.*; @@ -119,8 +120,9 @@ protected boolean diffUpdateJson(BaseJson element, Map existing, Object existingModified = existing.get(BaseJson.MODIFIED); if (jsonModified != null && !jsonModified.isEmpty()) { try { - Date jsonModDate = Formats.SIMPLE_DATE_FORMAT.parse(jsonModified); - Date existingModDate = Formats.SIMPLE_DATE_FORMAT.parse(existingModified.toString()); + SimpleDateFormat dateFormat = new SimpleDateFormat(Formats.DATE_FORMAT); + Date jsonModDate = dateFormat.parse(jsonModified); + Date existingModDate = dateFormat.parse(existingModified.toString()); if (jsonModDate.before(existingModDate)) { info.addRejection(element.getId(), new Rejection(element, 409, "Conflict Detected")); return false; diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java index 4de1a422c..7a7b07275 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java @@ -1,6 +1,7 @@ package org.openmbee.mms.crud.services; import java.text.ParseException; +import java.text.SimpleDateFormat; import java.time.Instant; import java.util.*; @@ -40,7 +41,7 @@ public CommitsResponse getRefCommits(String projectId, String refId, Map elements, CommitJson commitJson) { public NodeGetInfo initInfoFromNodes(List existingNodes, CommitJson commitJson) { NodeGetInfo nodeGetInfo = super.initInfo(commitJson, this::createNodeGetInfo); - Set indexIds = new HashSet<>(); - Map existingNodeMap = new HashMap<>(); - for (Node node : existingNodes) { - indexIds.add(node.getDocId()); - existingNodeMap.put(node.getNodeId(), node); - } + Set indexIds = existingNodes.stream().map(Node::getDocId).collect(Collectors.toSet()); // bulk read existing elements in elastic List existingElements = nodeIndex.findAllById(indexIds); addExistingElements(nodeGetInfo, existingElements); // handeled in addExistingElements @@ -178,6 +173,7 @@ protected List getRefCommitIds(Instant time) { return commitIds; } + @Override public void addExistingElements(NodeGetInfo info, List elements) { super.addExistingElements(info, elements); diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java index d3865862b..cef0212ef 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java @@ -86,7 +86,8 @@ public boolean hasPermission(String user, Set groups, String privilege) @Override public boolean hasGroupPermissions(String group, String privilege) { for (BranchGroupPerm perm: branchGroupPermRepo.findAllByBranch(branch)) { - if (perm.getGroup().getName().equals(group) && perm.getRole().equals(privilege)) { + if (perm.getGroup().getName().equals(group) && perm.getRole().getPrivileges().stream() + .anyMatch(v -> v.getName().equals(privilege))) { return true; } } diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java index 0c61ad591..1d85c9eed 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java @@ -71,7 +71,8 @@ public boolean hasPermission(String user, Set groups, String privilege) @Override public boolean hasGroupPermissions(String group, String privilege) { for (OrgGroupPerm perm: orgGroupPermRepo.findAllByOrganization_OrganizationId(organization.getOrganizationId())) { - if (perm.getGroup().getName().equals(group) && perm.getRole().equals(privilege)) { + if (perm.getGroup().getName().equals(group) && perm.getRole().getPrivileges().stream() + .anyMatch(v -> v.getName().equals(privilege))) { return true; } } diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java index f8ea02b4d..a1108e33c 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java @@ -82,7 +82,8 @@ public boolean hasPermission(String user, Set groups, String privilege) @Override public boolean hasGroupPermissions(String group, String privilege) { for (ProjectGroupPerm perm: projectGroupPermRepo.findAllByProject_ProjectId(project.getProjectId())) { - if (perm.getGroup().getName().equals(group) && perm.getRole().equals(privilege)) { + if (perm.getGroup().getName().equals(group) && perm.getRole().getPrivileges().stream(). + anyMatch(v -> v.getName().equals(privilege))) { return true; } } diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaNodeService.java b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaNodeService.java index 0f43c54fe..aeb2828d6 100644 --- a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaNodeService.java +++ b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaNodeService.java @@ -78,6 +78,7 @@ public void extraProcessPostedElement(NodeChangeInfo info, ElementJson element) element.remove(MsosaConstants.CHILDVIEWS); } + @Override public MountJson getProjectUsages(String projectId, String refId, String commitId, List> saw, boolean restrictOnPermissions) { ContextHolder.setContext(projectId, refId); diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaProjectService.java b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaProjectService.java index bd453ea3d..8664e565f 100644 --- a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaProjectService.java +++ b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaProjectService.java @@ -46,6 +46,7 @@ public ProjectJson create(ProjectJson project) { return projectJson; } + @Override public RefJson createMasterRefJson(ProjectJson project){ RefJson branchJson = super.createMasterRefJson(project); branchJson.put("twcId",Constants.MASTER_BRANCH); diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthProcessor.java b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthProcessor.java index aea6513b3..3e6cf6d8b 100644 --- a/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthProcessor.java +++ b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthProcessor.java @@ -85,7 +85,7 @@ public void setUserDetailsService(OAuthUserDetailsService userDetailsService) { OAuth2Authentication validateAuthToken(String accessToken) { - Boolean isAuthenticated = true; + boolean isAuthenticated = true; OAuth2Authentication oauth = null; try { if (accessToken == null) { diff --git a/twc/src/main/java/org/openmbee/mms/twc/services/TwcRevisionMmsCommitMapService.java b/twc/src/main/java/org/openmbee/mms/twc/services/TwcRevisionMmsCommitMapService.java index 56bdf20bd..db7373c2c 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/services/TwcRevisionMmsCommitMapService.java +++ b/twc/src/main/java/org/openmbee/mms/twc/services/TwcRevisionMmsCommitMapService.java @@ -14,6 +14,7 @@ import org.springframework.stereotype.Service; import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.*; @Service("twcRevisionMmsCommitMapService") @@ -100,8 +101,9 @@ public CommitsComparator(Boolean reverseOrder) { @Override public int compare(CommitJson o, CommitJson t1) { try { - Date d1 = Formats.SIMPLE_DATE_FORMAT.parse((String) o.get(CommitJson.CREATED)); - Date d2 = Formats.SIMPLE_DATE_FORMAT.parse((String) t1.get(CommitJson.CREATED)); + SimpleDateFormat dateFormat = new SimpleDateFormat(Formats.DATE_FORMAT); + Date d1 = dateFormat.parse((String) o.get(CommitJson.CREATED)); + Date d2 = dateFormat.parse((String) t1.get(CommitJson.CREATED)); return ascending ? d1.compareTo(d2) : d2.compareTo(d1); } catch (ParseException e) { logger.error("Error parsing commit dates: " + e.getMessage()); From d1cd63d3c6973a80e33666c0eaaac35e362d18e8 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Mon, 20 Mar 2023 11:31:32 -0400 Subject: [PATCH 04/60] More CodeQL changes --- .../java/org/openmbee/mms/core/dao/CommitPersistence.java | 2 -- .../org/openmbee/mms/core/services/NodeChangeInfoImpl.java | 1 + .../java/org/openmbee/mms/crud/domain/NodeGetDomain.java | 6 ------ .../dao/FederatedCommitPersistence.java | 5 ----- .../domain/FederatedNodeChangeDomain.java | 1 + .../federatedpersistence/domain/FederatedNodeGetDomain.java | 1 + 6 files changed, 3 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java index 7ff3ffb25..c31357874 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java +++ b/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java @@ -21,8 +21,6 @@ public interface CommitPersistence { Optional findLatestByProjectAndRef(String projectId, String refId); - Optional findByProjectAndRefAndTimestamp(String projectId, String refId, Instant timestamp); - List findByProjectAndRefAndTimestampAndLimit(String projectId, String refId, Instant timestamp, int limit); List elementHistory(String projectId, String elementId, Set commitDocIds); diff --git a/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java b/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java index d9f8319d6..16ed50f65 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java +++ b/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java @@ -64,6 +64,7 @@ public NodeChangeInfo setOverwrite(boolean overwrite) { return this; } + @Override public boolean getPreserveTimestamps() { return preserveTimestamps; } diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java index d71086fd7..807b47012 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java @@ -34,12 +34,6 @@ public NodeGetInfo initInfo(CommitJson commitJson, NodeGetInfoFactory nodeGetInf return info; } - public NodeGetInfo processGetJson(String projectId, String refId, String commitId, List elements, NodeService service) { - NodeGetInfo info = initInfo(null); - //TODO do we need to do anything here? - return info; - } - protected void rejectNotFound(NodeGetInfo info, String elementId) { info.addRejection(elementId, new Rejection(elementId, 404, "Not Found")); } diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java index 70c21127d..75b01ec77 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java @@ -140,11 +140,6 @@ public Optional findLatestByProjectAndRef(String projectId, String r return commitJson; } - @Override - public Optional findByProjectAndRefAndTimestamp(String projectId, String refId, Instant timestamp) { - return Optional.empty(); - } - @Override public List findByProjectAndRefAndTimestampAndLimit(String projectId, String refId, Instant timestamp, int limit) { ContextHolder.setContext(projectId); diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index 2fe898a27..168c5ee76 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -48,6 +48,7 @@ protected NodeChangeInfo createNodeChangeInfo() { return new FederatedNodeChangeInfoImpl(); } + @Override public NodeChangeInfo initInfo(CommitJson commitJson, boolean overwrite, boolean preserveTimestamps) { commitJson.setId(UUID.randomUUID().toString()); commitJson.setDocId(commitJson.getId()); diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java index b340a0cd4..0ba26f330 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java @@ -192,6 +192,7 @@ public void addExistingElements(NodeGetInfo info, List elements) { } } + @Override public NodeGetInfo createNodeGetInfo() { //ToDo :: check return new FederatedNodeGetInfoImpl(); } From 1cc6a06e45dec4b850298e9c94af960037b65d68 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Wed, 22 Mar 2023 08:27:10 -0400 Subject: [PATCH 05/60] Added timeout to database delete statement --- .../org/openmbee/mms/rdb/config/DatabaseDefinitionService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/config/DatabaseDefinitionService.java b/rdb/src/main/java/org/openmbee/mms/rdb/config/DatabaseDefinitionService.java index 624a634e1..7fb577e75 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/config/DatabaseDefinitionService.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/config/DatabaseDefinitionService.java @@ -91,6 +91,7 @@ public void deleteProjectDatabase(String projectId) throws SQLException { try (Connection connection = crudDataSources.getDataSource(ContextObject.DEFAULT_PROJECT).getConnection(); Statement statement = connection.createStatement()) { if ("org.postgresql.Driver".equals(env.getProperty("spring.datasource.driver-class-name"))) { + statement.setQueryTimeout(60); statement.execute(connection.nativeSQL( "SELECT pid, pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '" + databaseProjectString(projectId) + "';")); From 9ddd6796bff115df65e68aed33ff28cdff9d2bd3 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Mon, 27 Mar 2023 13:23:13 -0400 Subject: [PATCH 06/60] Removing overarching transactional annotations --- .../mms/crud/controllers/projects/ProjectsController.java | 1 - data/src/main/java/org/openmbee/mms/data/dao/ProjectDAO.java | 4 +--- .../federatedpersistence/dao/FederatedProjectPersistence.java | 1 + 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java index cecd977a5..1c8375a08 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java @@ -176,7 +176,6 @@ public ProjectsResponse createOrUpdateProjects( @DeleteMapping(value = "/{projectId}") - @Transactional @PreAuthorize("@mss.hasProjectPrivilege(authentication, #projectId, 'PROJECT_DELETE', false)") public ProjectsResponse deleteProject( @PathVariable String projectId, diff --git a/data/src/main/java/org/openmbee/mms/data/dao/ProjectDAO.java b/data/src/main/java/org/openmbee/mms/data/dao/ProjectDAO.java index 858be1e3c..e3f791727 100644 --- a/data/src/main/java/org/openmbee/mms/data/dao/ProjectDAO.java +++ b/data/src/main/java/org/openmbee/mms/data/dao/ProjectDAO.java @@ -12,12 +12,10 @@ public interface ProjectDAO { Optional findByProjectName(String name); - public List findAllByOrgId(String id); + List findAllByOrgId(String id); - @Transactional(Transactional.TxType.REQUIRES_NEW) Project save(Project p); - @Transactional(Transactional.TxType.REQUIRES_NEW) void delete(String projectId); List findAll(); diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java index b6b57f029..d38ddcbd8 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java @@ -96,6 +96,7 @@ public Collection findAllByOrgId(String orgId) { public void hardDelete(String projectId) { String message = ""; try { + ContextHolder.clearContext(); projectDAO.delete(projectId); } catch (Exception ex){ message = message.concat("Project DAO, cannot delete. "); From bc41d2825b8657d7c56a81f787b874b4242212a9 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Tue, 28 Mar 2023 10:02:24 -0400 Subject: [PATCH 07/60] Removed transactional annotations from DefaultPermissionService --- .../services/DefaultPermissionService.java | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java b/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java index 999101146..e743b505c 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java +++ b/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java @@ -56,7 +56,6 @@ public void setOrgPersistence(OrgPersistence orgPersistence) { } @Override - @Transactional public void initOrgPerms(String orgId, String creator) { OrgJson organization = getOrganization(orgId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); @@ -64,7 +63,6 @@ public void initOrgPerms(String orgId, String creator) { } @Override - @Transactional public void initProjectPerms(String projectId, boolean inherit, String creator) { ProjectJson project = getProject(projectId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); @@ -75,7 +73,6 @@ public void initProjectPerms(String projectId, boolean inherit, String creator) } @Override - @Transactional public void initBranchPerms(String projectId, String branchId, boolean inherit, String creator) { RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.CREATE); if(branch == null) { @@ -89,7 +86,6 @@ public void initBranchPerms(String projectId, String branchId, boolean inherit, } @Override - @Transactional public PermissionUpdatesResponse updateOrgUserPerms(PermissionUpdateRequest req, String orgId) { OrgJson organization = getOrganization(orgId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); @@ -106,7 +102,6 @@ public PermissionUpdatesResponse updateOrgUserPerms(PermissionUpdateRequest req, } @Override - @Transactional public PermissionUpdatesResponse updateOrgGroupPerms(PermissionUpdateRequest req, String orgId) { OrgJson organization = getOrganization(orgId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); @@ -122,7 +117,6 @@ public PermissionUpdatesResponse updateOrgGroupPerms(PermissionUpdateRequest req } @Override - @Transactional public PermissionUpdatesResponse updateProjectUserPerms(PermissionUpdateRequest req, String projectId) { ProjectJson project = getProject(projectId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); @@ -138,7 +132,6 @@ public PermissionUpdatesResponse updateProjectUserPerms(PermissionUpdateRequest } @Override - @Transactional public PermissionUpdatesResponse updateProjectGroupPerms(PermissionUpdateRequest req, String projectId) { ProjectJson project = getProject(projectId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); @@ -154,7 +147,6 @@ public PermissionUpdatesResponse updateProjectGroupPerms(PermissionUpdateRequest } @Override - @Transactional public PermissionUpdateResponse updateBranchUserPerms(PermissionUpdateRequest req, String projectId, String branchId) { RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); if(branch == null) { @@ -165,7 +157,6 @@ public PermissionUpdateResponse updateBranchUserPerms(PermissionUpdateRequest re } @Override - @Transactional public PermissionUpdateResponse updateBranchGroupPerms(PermissionUpdateRequest req, String projectId, String branchId) { RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(branch); @@ -173,7 +164,6 @@ public PermissionUpdateResponse updateBranchGroupPerms(PermissionUpdateRequest r } @Override - @Transactional public PermissionUpdatesResponse setProjectInherit(boolean isInherit, String projectId) { PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); responseBuilder.setInherit(true); @@ -186,7 +176,6 @@ public PermissionUpdatesResponse setProjectInherit(boolean isInherit, String pro } @Override - @Transactional public PermissionUpdatesResponse setBranchInherit(boolean isInherit, String projectId, String branchId) { PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); responseBuilder.setInherit(isInherit); @@ -199,7 +188,6 @@ public PermissionUpdatesResponse setBranchInherit(boolean isInherit, String proj } @Override - @Transactional public boolean setOrgPublic(boolean isPublic, String orgId) { OrgJson organization = getOrganization(orgId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); @@ -208,7 +196,6 @@ public boolean setOrgPublic(boolean isPublic, String orgId) { } @Override - @Transactional public boolean setProjectPublic(boolean isPublic, String projectId) { ProjectJson project = getProject(projectId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); @@ -217,7 +204,6 @@ public boolean setProjectPublic(boolean isPublic, String projectId) { } @Override - @Transactional public boolean hasOrgPrivilege(String privilege, String user, Set groups, String orgId) { if (groups.contains(AuthorizationConstants.MMSADMIN)) return true; @@ -227,7 +213,6 @@ public boolean hasOrgPrivilege(String privilege, String user, Set groups } @Override - @Transactional public boolean hasProjectPrivilege(String privilege, String user, Set groups, String projectId) { if (groups.contains(AuthorizationConstants.MMSADMIN)) return true; @@ -237,7 +222,6 @@ public boolean hasProjectPrivilege(String privilege, String user, Set gr } @Override - @Transactional public boolean hasBranchPrivilege(String privilege, String user, Set groups, String projectId, String branchId) { if (groups.contains(AuthorizationConstants.MMSADMIN)) return true; @@ -247,31 +231,26 @@ public boolean hasBranchPrivilege(String privilege, String user, Set gro } @Override - @Transactional public boolean isProjectInherit(String projectId) { return projectPersistence.inheritsPermissions(projectId); } @Override - @Transactional public boolean isBranchInherit(String projectId, String branchId) { return branchPersistence.inheritsPermissions(projectId, branchId); } @Override - @Transactional public boolean isOrgPublic(String orgId) { return orgPersistence.hasPublicPermissions(orgId); } @Override - @Transactional public boolean isProjectPublic(String projectId) { return projectPersistence.hasPublicPermissions(projectId); } @Override - @Transactional public PermissionResponse getOrgGroupRoles(String orgId) { OrgJson organization = getOrganization(orgId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); @@ -279,7 +258,6 @@ public PermissionResponse getOrgGroupRoles(String orgId) { } @Override - @Transactional public PermissionResponse getOrgUserRoles(String orgId) { OrgJson organization = getOrganization(orgId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(organization); @@ -287,7 +265,6 @@ public PermissionResponse getOrgUserRoles(String orgId) { } @Override - @Transactional public PermissionResponse getProjectGroupRoles(String projectId) { ProjectJson project = getProject(projectId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); @@ -295,7 +272,6 @@ public PermissionResponse getProjectGroupRoles(String projectId) { } @Override - @Transactional public PermissionResponse getProjectUserRoles(String projectId) { ProjectJson project = getProject(projectId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); @@ -303,7 +279,6 @@ public PermissionResponse getProjectUserRoles(String projectId) { } @Override - @Transactional public PermissionResponse getBranchGroupRoles(String projectId, String branchId) { RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.IGNORE); @@ -316,7 +291,6 @@ public PermissionResponse getBranchGroupRoles(String projectId, String branchId) } @Override - @Transactional public PermissionResponse getBranchUserRoles(String projectId, String branchId) { RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.IGNORE); From ff3d4fdeeb820d86fa97b8891f96090c3a940312 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Tue, 28 Mar 2023 10:16:38 -0400 Subject: [PATCH 08/60] Removed @Transactional annotation from modules that are not tasked with persistence implementation --- .../mms/crud/controllers/branches/BranchesController.java | 3 --- .../openmbee/mms/crud/controllers/orgs/OrgsController.java | 5 ----- .../mms/crud/controllers/projects/ProjectsController.java | 4 ---- .../mms/groups/controllers/LocalGroupsController.java | 6 ------ .../mms/localuser/security/UserDetailsServiceImpl.java | 3 --- .../mms/oauth/security/OAuthUserDetailsService.java | 2 -- .../org/openmbee/mms/permissions/PermissionsController.java | 4 ---- .../mms/twc/maintenance/TWCMaintenanceController.java | 4 ---- .../openmbee/mms/twc/security/TwcUserDetailsService.java | 2 -- 9 files changed, 33 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java index 096904e52..cc2eb6ef7 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java @@ -17,7 +17,6 @@ import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; @@ -74,7 +73,6 @@ public RefsResponse getRef( } @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - @Transactional @PreAuthorize("@mss.hasProjectPrivilege(authentication, #projectId, 'PROJECT_CREATE_BRANCH', false)") public RefsResponse createRefs( @PathVariable String projectId, @@ -125,7 +123,6 @@ public RefsResponse createRefs( } @DeleteMapping("/{refId}") - @Transactional @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_DELETE', false)") public RefsResponse deleteRef( @PathVariable String projectId, diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java index a003b640c..bf24ec99a 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java @@ -23,7 +23,6 @@ import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -45,7 +44,6 @@ public OrgsController(OrgPersistence organizationRepository) { } @GetMapping - @Transactional public OrganizationsResponse getAllOrgs( Authentication auth) { OrganizationsResponse response = new OrganizationsResponse(); @@ -59,7 +57,6 @@ public OrganizationsResponse getAllOrgs( Authentication auth) { } @GetMapping(value = "/{orgId}") - @Transactional @PreAuthorize("@mss.hasOrgPrivilege(authentication, #orgId, 'ORG_READ', true)") public OrganizationsResponse getOrg( @PathVariable String orgId) { @@ -74,7 +71,6 @@ public OrganizationsResponse getOrg( } @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - @Transactional @PreAuthorize("isAuthenticated()") public OrganizationsResponse createOrUpdateOrgs( @RequestBody OrganizationsRequest orgPost, @@ -124,7 +120,6 @@ public OrganizationsResponse createOrUpdateOrgs( } @DeleteMapping(value = "/{orgId}") - @Transactional @PreAuthorize("@mss.hasOrgPrivilege(authentication, #orgId, 'ORG_DELETE', false)") public OrganizationsResponse deleteOrg( @PathVariable String orgId) { diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java index 1c8375a08..c9ce4f7a4 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java @@ -18,7 +18,6 @@ import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import java.time.Instant; @@ -46,7 +45,6 @@ public void setProjectSchemas(ProjectSchemas projectSchemas) { } @GetMapping - @Transactional(readOnly = true) public ProjectsResponse getAllProjects(Authentication auth) { ProjectsResponse response = new ProjectsResponse(); @@ -66,7 +64,6 @@ public ProjectsResponse getAllProjects(Authentication auth) { } @GetMapping(value = "/{projectId}") - @Transactional(readOnly = true) @PreAuthorize("@mss.hasProjectPrivilege(authentication, #projectId, 'PROJECT_READ', true)") public ProjectsResponse getProject( @PathVariable String projectId) { @@ -85,7 +82,6 @@ public ProjectsResponse getProject( } @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - @Transactional @PreAuthorize("isAuthenticated()") public ProjectsResponse createOrUpdateProjects( @RequestBody ProjectsRequest projectsPost, diff --git a/groups/src/main/java/org/openmbee/mms/groups/controllers/LocalGroupsController.java b/groups/src/main/java/org/openmbee/mms/groups/controllers/LocalGroupsController.java index 08a25c67e..5dc3c6732 100644 --- a/groups/src/main/java/org/openmbee/mms/groups/controllers/LocalGroupsController.java +++ b/groups/src/main/java/org/openmbee/mms/groups/controllers/LocalGroupsController.java @@ -9,7 +9,6 @@ import org.openmbee.mms.core.config.AuthorizationConstants; import org.openmbee.mms.core.dao.GroupPersistence; import org.openmbee.mms.core.dao.UserGroupsPersistence; -import org.openmbee.mms.core.dao.UserPersistence; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.exceptions.ConflictException; import org.openmbee.mms.core.exceptions.NotFoundException; @@ -20,14 +19,12 @@ import org.openmbee.mms.json.UserJson; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/groups") @Tag(name = "Groups") -@Transactional public class LocalGroupsController { private GroupPersistence groupPersistence; @@ -78,7 +75,6 @@ public GroupsResponse getAllGroups() { @GetMapping("/{group}") @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) - @Transactional public GroupResponse getGroup(@PathVariable String group) { if(groupValidationService.isRestrictedGroup(group)) { throw new BadRequestException(GroupConstants.RESTRICTED_GROUP); @@ -90,7 +86,6 @@ public GroupResponse getGroup(@PathVariable String group) { @DeleteMapping("/{group}") @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) @ResponseBody - @Transactional public void deleteLocalGroup(@PathVariable String group) { GroupJson groupJson = groupPersistence.findByName(group).orElseThrow(() -> new NotFoundException(GroupConstants.GROUP_NOT_FOUND)); if (groupValidationService.canDeleteGroup(groupJson)) { @@ -102,7 +97,6 @@ public void deleteLocalGroup(@PathVariable String group) { @PostMapping("/{group}/users") @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) - @Transactional public GroupUpdateResponse updateGroupUsers(@PathVariable String group, @RequestBody GroupUpdateRequest groupUpdateRequest) { diff --git a/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsServiceImpl.java b/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsServiceImpl.java index e47bbd02c..893692b9a 100644 --- a/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsServiceImpl.java +++ b/localuser/src/main/java/org/openmbee/mms/localuser/security/UserDetailsServiceImpl.java @@ -13,7 +13,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; @Service public class UserDetailsServiceImpl implements UserDetailsService { @@ -58,7 +57,6 @@ public Collection getUsers() { return userPersistence.findAll(); } - @Transactional public UserJson register(UserCreateRequest req) { UserJson user = new UserJson(); user.setUsername(req.getUsername()); @@ -71,7 +69,6 @@ public UserJson register(UserCreateRequest req) { return userPersistence.save(user); } - @Transactional public void changeUserPassword(String username, String password, boolean asAdmin) { Optional userOptional = userPersistence.findByUsername(username); if(userOptional.isEmpty()) { diff --git a/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetailsService.java b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetailsService.java index f6f9667a4..5f54b4b16 100644 --- a/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetailsService.java +++ b/oauth/src/main/java/org/openmbee/mms/oauth/security/OAuthUserDetailsService.java @@ -8,7 +8,6 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.Optional; @@ -41,7 +40,6 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx return new OAuthUserDetails(user, userGroupsPersistence.findGroupsAssignedToUser(username)); } - @Transactional public UserJson addUser(String username) { UserJson user = new UserJson(); user.setUsername(username); diff --git a/permissions/src/main/java/org/openmbee/mms/permissions/PermissionsController.java b/permissions/src/main/java/org/openmbee/mms/permissions/PermissionsController.java index 12035d0ad..9790620ca 100644 --- a/permissions/src/main/java/org/openmbee/mms/permissions/PermissionsController.java +++ b/permissions/src/main/java/org/openmbee/mms/permissions/PermissionsController.java @@ -8,7 +8,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -27,7 +26,6 @@ public PermissionsController(PermissionService permissionService) { } @PostMapping(value = "/orgs/{orgId}/permissions", consumes = MediaType.APPLICATION_JSON_VALUE) - @Transactional @PreAuthorize("@mss.hasOrgPrivilege(authentication, #orgId, 'ORG_UPDATE_PERMISSIONS', false)") public PermissionUpdatesResponse updateOrgPermissions( @PathVariable String orgId, @@ -48,7 +46,6 @@ public PermissionUpdatesResponse updateOrgPermissions( } @PostMapping(value = "/projects/{projectId}/permissions", consumes = MediaType.APPLICATION_JSON_VALUE) - @Transactional @PreAuthorize("@mss.hasProjectPrivilege(authentication, #projectId, 'PROJECT_UPDATE_PERMISSIONS', false)") public PermissionUpdatesResponse updateProjectPermissions( @PathVariable String projectId, @@ -72,7 +69,6 @@ public PermissionUpdatesResponse updateProjectPermissions( } @PostMapping(value = "/projects/{projectId}/refs/{refId}/permissions", consumes = MediaType.APPLICATION_JSON_VALUE) - @Transactional @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_UPDATE_PERMISSIONS', false)") public PermissionUpdatesResponse updateBranchPermissions( @PathVariable String projectId, diff --git a/twc/src/main/java/org/openmbee/mms/twc/maintenance/TWCMaintenanceController.java b/twc/src/main/java/org/openmbee/mms/twc/maintenance/TWCMaintenanceController.java index 3866c9aa0..a2052465a 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/maintenance/TWCMaintenanceController.java +++ b/twc/src/main/java/org/openmbee/mms/twc/maintenance/TWCMaintenanceController.java @@ -10,7 +10,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import java.util.Optional; @@ -33,7 +32,6 @@ public void setTwcMetadataService(TwcMetadataService twcMetadataService){ this.twcMetadataService = twcMetadataService; } - @Transactional @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) @GetMapping(value = "/project/twcmetadata/{id}") @ResponseBody @@ -43,7 +41,6 @@ public TwcMetadata getProjectMetadata(@PathVariable String id){ } - @Transactional @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) @PostMapping(value = "/project/twcmetadata/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody @@ -58,7 +55,6 @@ public void updateProjectMetadata(@PathVariable String id, @RequestBody TwcMetad } } - @Transactional @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) @DeleteMapping(value = "/project/twcmetadata/{id}") @ResponseBody diff --git a/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetailsService.java b/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetailsService.java index 1eed02611..f01284441 100644 --- a/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetailsService.java +++ b/twc/src/main/java/org/openmbee/mms/twc/security/TwcUserDetailsService.java @@ -8,7 +8,6 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.Optional; @@ -41,7 +40,6 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx return new TwcUserDetails(user, userGroupsPersistence.findGroupsAssignedToUser(username)); } - @Transactional public UserJson addUser(String username) { UserJson user = new UserJson(); user.setUsername(username); From b6a0edea86c4d358d6d0287d1a122bcd34608191 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Wed, 29 Mar 2023 14:50:18 -0400 Subject: [PATCH 09/60] Fixes for failing crud tests --- .../mms/core/services/NodeChangeInfoImpl.java | 1 + .../mms/core/services/NodeGetInfo.java | 3 + .../mms/core/services/NodeGetInfoImpl.java | 13 ++++ .../elements/ElementsController.java | 70 +++++++++---------- .../crud/controllers/orgs/OrgsController.java | 3 +- .../projects/ProjectsController.java | 5 +- .../mms/crud/domain/NodeGetDomain.java | 4 +- .../mms/crud/services/DefaultNodeService.java | 23 ++++-- .../dao/FederatedProjectPersistence.java | 2 + .../domain/FederatedNodeGetDomain.java | 7 ++ 10 files changed, 87 insertions(+), 44 deletions(-) diff --git a/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java b/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java index 16ed50f65..2883701fc 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java +++ b/core/src/main/java/org/openmbee/mms/core/services/NodeChangeInfoImpl.java @@ -74,4 +74,5 @@ public NodeChangeInfo setPreserveTimestamps(boolean preserveTimestamps) { this.preserveTimestamps = preserveTimestamps; return this; } + } diff --git a/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfo.java b/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfo.java index 96ea56a92..cc4978f00 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfo.java +++ b/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfo.java @@ -30,4 +30,7 @@ public interface NodeGetInfo { void addRejection(String id, Rejection rejection); + void setRefId(String refId); + String getRefId(); + } diff --git a/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfoImpl.java b/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfoImpl.java index 1f0e57bef..8e7284793 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfoImpl.java +++ b/core/src/main/java/org/openmbee/mms/core/services/NodeGetInfoImpl.java @@ -20,6 +20,8 @@ public class NodeGetInfoImpl implements NodeGetInfo { private CommitJson commitJson; + private String refId; + @Override public Map getReqElementMap() { return reqElementMap; @@ -84,4 +86,15 @@ public NodeGetInfo setCommitJson(CommitJson commitJson) { this.commitJson = commitJson; return this; } + + + @Override + public void setRefId(String refId) { + this.refId = refId; + } + + @Override + public String getRefId() { + return this.refId; + } } diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/elements/ElementsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/elements/ElementsController.java index 07eca2d81..2a141bfd5 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/elements/ElementsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/elements/ElementsController.java @@ -76,11 +76,11 @@ public void setCommitService(@Qualifier("defaultCommitService")CommitService com @Content(mediaType = "application/x-ndjson") }) public ResponseEntity getAllElements( - @PathVariable String projectId, - @PathVariable String refId, - @RequestParam(required = false) String commitId, - @RequestParam(required = false) Map params, - @Parameter(hidden = true) @RequestHeader(value = "Accept", defaultValue = "application/json") String accept) { + @PathVariable String projectId, + @PathVariable String refId, + @RequestParam(required = false) String commitId, + @RequestParam(required = false) Map params, + @Parameter(hidden = true) @RequestHeader(value = "Accept", defaultValue = "application/json") String accept) { NodeService nodeService = getNodeService(projectId); if (commitId != null && !commitId.isEmpty()) { @@ -95,11 +95,11 @@ public ResponseEntity getAllElements( @GetMapping(value = "/{elementId}", produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") public ElementsResponse getElement( - @PathVariable String projectId, - @PathVariable String refId, - @PathVariable String elementId, - @RequestParam(required = false) String commitId, - @RequestParam(required = false) Map params) { + @PathVariable String projectId, + @PathVariable String refId, + @PathVariable String elementId, + @RequestParam(required = false) String commitId, + @RequestParam(required = false) Map params) { NodeService nodeService = getNodeService(projectId); ElementsResponse res = nodeService.read(projectId, refId, elementId, params); @@ -110,12 +110,12 @@ public ElementsResponse getElement( @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_EDIT_CONTENT', false)") public ElementsCommitResponse createOrUpdateElements( - @PathVariable String projectId, - @PathVariable String refId, - @RequestBody ElementsRequest req, - @RequestParam(required = false) String overwrite, - @RequestParam(required = false) Map params, - Authentication auth) { + @PathVariable String projectId, + @PathVariable String refId, + @RequestBody ElementsRequest req, + @RequestParam(required = false) String overwrite, + @RequestParam(required = false) Map params, + Authentication auth) { embeddedHookService.hook(new ElementUpdateHook(ElementUpdateHook.Action.ADD_UPDATE, projectId, refId, req.getElements(), params, auth)); @@ -132,12 +132,12 @@ public ElementsCommitResponse createOrUpdateElements( @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_EDIT_CONTENT', false)") */ public ResponseEntity createOrUpdateElementsStream( - @PathVariable String projectId, - @PathVariable String refId, - @RequestParam(required = false) Map params, - @Parameter(hidden = true) @RequestHeader(value = "Accept", defaultValue = "application/json") String accept, - Authentication auth, - HttpEntity requestEntity) { + @PathVariable String projectId, + @PathVariable String refId, + @RequestParam(required = false) Map params, + @Parameter(hidden = true) @RequestHeader(value = "Accept", defaultValue = "application/json") String accept, + Authentication auth, + HttpEntity requestEntity) { String commitId = UUID.randomUUID().toString(); // Generate a commitId from the start params.put("commitId", commitId); @@ -180,11 +180,11 @@ public ResponseEntity createOrUpdateElementsStream( @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_READ', true)") public ElementsResponse getElements( - @PathVariable String projectId, - @PathVariable String refId, - @RequestBody ElementsRequest req, - @RequestParam(required = false) String commitId, - @RequestParam(required = false) Map params) { + @PathVariable String projectId, + @PathVariable String refId, + @RequestBody ElementsRequest req, + @RequestParam(required = false) String commitId, + @RequestParam(required = false) Map params) { ElementsResponse response = new ElementsResponse(); if (!req.getElements().isEmpty()) { @@ -197,10 +197,10 @@ public ElementsResponse getElements( @DeleteMapping(value = "/{elementId}") @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_EDIT_CONTENT', false)") public ElementsCommitResponse deleteElement( - @PathVariable String projectId, - @PathVariable String refId, - @PathVariable String elementId, - Authentication auth) { + @PathVariable String projectId, + @PathVariable String refId, + @PathVariable String elementId, + Authentication auth) { ElementsCommitResponse res = getNodeService(projectId).delete(projectId, refId, elementId, auth.getName()); handleSingleResponse(res); @@ -210,10 +210,10 @@ public ElementsCommitResponse deleteElement( @DeleteMapping(consumes = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("@mss.hasBranchPrivilege(authentication, #projectId, #refId, 'BRANCH_EDIT_CONTENT', false)") public ElementsResponse deleteElements( - @PathVariable String projectId, - @PathVariable String refId, - @RequestBody ElementsRequest req, - Authentication auth) { + @PathVariable String projectId, + @PathVariable String refId, + @RequestBody ElementsRequest req, + Authentication auth) { return getNodeService(projectId).delete(projectId, refId, req, auth.getName()); } diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java index bf24ec99a..05b408a6c 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/orgs/OrgsController.java @@ -121,8 +121,7 @@ public OrganizationsResponse createOrUpdateOrgs( @DeleteMapping(value = "/{orgId}") @PreAuthorize("@mss.hasOrgPrivilege(authentication, #orgId, 'ORG_DELETE', false)") - public OrganizationsResponse deleteOrg( - @PathVariable String orgId) { + public OrganizationsResponse deleteOrg(@PathVariable String orgId) { OrganizationsResponse response = new OrganizationsResponse(); Optional orgOption = organizationRepository.findById(orgId); diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java index c9ce4f7a4..095276dd0 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java @@ -45,10 +45,11 @@ public void setProjectSchemas(ProjectSchemas projectSchemas) { } @GetMapping - public ProjectsResponse getAllProjects(Authentication auth) { + public ProjectsResponse getAllProjects(Authentication auth, @RequestParam(required = false) String orgId) { ProjectsResponse response = new ProjectsResponse(); - List allProjects = projectPersistence.findAll(); + Collection allProjects = + orgId == null ? projectPersistence.findAll() : projectPersistence.findAllByOrgId(orgId); for (ProjectJson projectJson : allProjects) { try { if (mss.hasProjectPrivilege(auth, projectJson.getProjectId(), Privileges.PROJECT_READ.name(), true) diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java index 807b47012..8e338732d 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeGetDomain.java @@ -47,6 +47,8 @@ public NodeGetInfo createNodeGetInfo() { } public void addExistingElements(NodeGetInfo info, List elements) { - elements.forEach(e -> info.getExistingElementMap().put(e.getId(), e)); + elements.forEach(e -> { + info.getExistingElementMap().put(e.getId(), e); + }); } } diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java index d492a3a4f..3a2b803a6 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java @@ -2,6 +2,7 @@ import org.openmbee.mms.core.dao.*; import org.openmbee.mms.core.exceptions.BadRequestException; +import org.openmbee.mms.core.exceptions.ConflictException; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.core.objects.ElementsCommitResponse; import org.openmbee.mms.core.objects.ElementsRequest; @@ -75,7 +76,8 @@ public void readAsStream(String projectId, String refId, Map par String separator = "\n"; if (!"application/x-ndjson".equals(accept)) { - stream.write("{\"elements\":[".getBytes(StandardCharsets.UTF_8)); + String intro = "{\"commitId\":\"" + commitId + "\",\"elements\":["; + stream.write(intro.getBytes(StandardCharsets.UTF_8)); separator = ","; } @@ -106,9 +108,9 @@ public ElementsResponse read(String projectId, String refId, String id, logger.debug("ElementId given: {}", id); NodeGetInfo getInfo = nodePersistence.findById(projectId, refId, commitId, id); - if(!getInfo.getRejected().isEmpty()){ + if (!getInfo.getRejected().isEmpty()) { response.addRejection(getInfo.getRejected().get(id)); - }else{ + } else { response.getElements().add(getInfo.getActiveElementMap().get(id)); } @@ -119,6 +121,7 @@ public ElementsResponse read(String projectId, String refId, String id, List nodes = nodePersistence.findAll(projectId, refId, commitId); response.getElements().addAll(nodes); } + response.getElements().forEach(v -> v.setRefId(refId)); return response; } @@ -142,6 +145,14 @@ public ElementsCommitResponse createOrUpdate(String projectId, String refId, Ele boolean overwriteJson = Boolean.parseBoolean(params.get(CrudConstants.OVERWRITE)); boolean preserveTimestamps = Boolean.parseBoolean(params.get(CrudConstants.PRESERVETIMESTAMPS)); String commitId = params.getOrDefault(CrudConstants.COMMITID, null); + String lastCommitId = req.getLastCommitId(); + + if (lastCommitId != null && !lastCommitId.isEmpty()) { + Optional latestCommit = commitPersistence.findLatestByProjectAndRef(projectId, refId); + if (latestCommit.isEmpty() || !lastCommitId.equals(latestCommit.get().getId())) { + throw new ConflictException("Given commitId " + lastCommitId + " is not the latest"); + } + } NodeChangeInfo changes = nodePersistence.prepareChange(createCommit(user, refId, projectId, commitId, req), overwriteJson, preserveTimestamps); @@ -150,6 +161,9 @@ public ElementsCommitResponse createOrUpdate(String projectId, String refId, Ele for(ElementJson element : changes.getUpdatedMap().values()) { extraProcessPostedElement(changes, element); } + if(req.getDeletes() != null) { + changes = nodePersistence.prepareDeletes(changes, req.getDeletes()); + } changes = nodePersistence.commitChanges(changes); CommitJson commitJson = changes.getCommitJson(); @@ -158,8 +172,9 @@ public ElementsCommitResponse createOrUpdate(String projectId, String refId, Ele ElementsCommitResponse response = new ElementsCommitResponse(); response.getElements().addAll(changes.getUpdatedMap().values()); + response.setDeleted(new ArrayList<>(changes.getDeletedMap().values())); response.setRejected(new ArrayList<>(changes.getRejected().values())); - if(!changes.getUpdatedMap().isEmpty()) { + if(!changes.getUpdatedMap().isEmpty() || !changes.getDeletedMap().isEmpty()) { response.setCommitId(changes.getCommitJson().getId()); } return response; diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java index d38ddcbd8..116c2fa43 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java @@ -17,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import javax.transaction.Transactional; import java.util.*; import java.util.stream.Collectors; @@ -78,6 +79,7 @@ public List findAll() { } @Override + @Transactional public Collection findAllByOrgId(String orgId) { Optional org = orgRepository.findByOrganizationId(orgId); if(org.isEmpty()) { diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java index 0ba26f330..c88c9ba49 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java @@ -1,5 +1,6 @@ package org.openmbee.mms.federatedpersistence.domain; +import org.openmbee.mms.core.config.ContextHolder; import org.openmbee.mms.core.config.Formats; import org.openmbee.mms.core.dao.CommitPersistence; import org.openmbee.mms.data.dao.NodeDAO; @@ -118,6 +119,12 @@ protected NodeGetInfo processCommit(NodeGetInfo info, String commitId) { continue; } ElementJson indexElement = info.getExistingElementMap().get(nodeId); + if(info.getCommitJson() != null && info.getCommitJson().getRefId() != null) { + indexElement.setRefId(info.getCommitJson().getRefId()); + } else { + indexElement.setRefId(ContextHolder.getContext().getBranchId()); + } + Instant modified = Instant.from(Formats.FORMATTER.parse(indexElement.getModified())); Instant created = Instant.from(Formats.FORMATTER.parse(indexElement.getCreated())); From 98083174d7bf7c86aa623e6df13f4aec4733d71c Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Wed, 29 Mar 2023 14:58:15 -0400 Subject: [PATCH 10/60] Increased request delay for crud tests --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9ec19d859..a3da66368 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,7 +31,7 @@ jobs: command: | docker create -v /etc/newman --name mms_test_configs alpine:3.4 /bin/true docker cp example/. mms_test_configs:/etc/newman - docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run crud.postman_collection.json -e test-env.json --delay-request 500 + docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run crud.postman_collection.json -e test-env.json --delay-request 1000 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run cameo.postman_collection.json -e test-env.json --delay-request 1000 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run jupyter.postman_collection.json -e test-env.json --delay-request 500 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run localauth.postman_collection.json -e test-env.json --delay-request 500 From a9595f66a73467ba4303acc5ff6a726e3b0d3735 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Thu, 30 Mar 2023 16:56:12 -0400 Subject: [PATCH 11/60] Working on fixing permissions test failures --- .../core/builders/PermissionUpdatesResponseBuilder.java | 3 +++ .../org/openmbee/mms/data/domains/global/Privilege.java | 7 ++----- .../permissions/DefaultBranchPermissionsDelegate.java | 7 +++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdatesResponseBuilder.java b/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdatesResponseBuilder.java index e6640d13a..d2e7a42d3 100644 --- a/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdatesResponseBuilder.java +++ b/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdatesResponseBuilder.java @@ -62,6 +62,9 @@ private Boolean or(Boolean a, Boolean b) { if(a == null) { return b; } + if(b == null) { + return a; + } return a || b; } } diff --git a/data/src/main/java/org/openmbee/mms/data/domains/global/Privilege.java b/data/src/main/java/org/openmbee/mms/data/domains/global/Privilege.java index aed832ece..c2bbd1b64 100644 --- a/data/src/main/java/org/openmbee/mms/data/domains/global/Privilege.java +++ b/data/src/main/java/org/openmbee/mms/data/domains/global/Privilege.java @@ -1,10 +1,7 @@ package org.openmbee.mms.data.domains.global; import java.util.Set; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.ManyToMany; -import javax.persistence.Table; +import javax.persistence.*; @Entity @Table(name = "privileges") @@ -13,7 +10,7 @@ public class Privilege extends Base { @Column(unique = true) private String name; - @ManyToMany(mappedBy = "privileges") + @ManyToMany(mappedBy = "privileges", fetch = FetchType.EAGER) private Set roles; public Privilege() { diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java index cef0212ef..a6d6e408b 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java @@ -25,6 +25,7 @@ import org.springframework.data.util.Pair; import org.springframework.http.HttpStatus; +import javax.transaction.Transactional; import java.util.*; public class DefaultBranchPermissionsDelegate extends AbstractDefaultPermissionsDelegate { @@ -66,6 +67,7 @@ public void setProjectUserPermRepo(ProjectUserPermRepository projectUserPermRepo } @Override + @Transactional public boolean hasPermission(String user, Set groups, String privilege) { Optional priv = getPrivRepo().findByName(privilege); @@ -84,6 +86,7 @@ public boolean hasPermission(String user, Set groups, String privilege) } @Override + @Transactional public boolean hasGroupPermissions(String group, String privilege) { for (BranchGroupPerm perm: branchGroupPermRepo.findAllByBranch(branch)) { if (perm.getGroup().getName().equals(group) && perm.getRole().getPrivileges().stream() @@ -145,6 +148,7 @@ public void setPublic(boolean isPublic) { } @Override + @Transactional public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest req) { FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); @@ -211,6 +215,7 @@ public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest re } @Override + @Transactional public PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest req) { FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); @@ -273,6 +278,7 @@ public PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest r } @Override + @Transactional public PermissionResponse getUserRoles() { PermissionResponse res = PermissionResponse.getDefaultResponse(); for (BranchUserPerm perm: branchUserPermRepo.findAllByBranch(branch)) { @@ -286,6 +292,7 @@ public PermissionResponse getUserRoles() { } @Override + @Transactional public PermissionResponse getGroupRoles() { PermissionResponse res = PermissionResponse.getDefaultResponse(); for (BranchGroupPerm perm: branchGroupPermRepo.findAllByBranch(branch)) { From 23848a0064e4f774e294d38d6ac850642dae9c3a Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Fri, 31 Mar 2023 08:44:06 -0400 Subject: [PATCH 12/60] increased delay in permissions test --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a3da66368..3bea3dc3a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,7 +35,7 @@ jobs: docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run cameo.postman_collection.json -e test-env.json --delay-request 1000 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run jupyter.postman_collection.json -e test-env.json --delay-request 500 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run localauth.postman_collection.json -e test-env.json --delay-request 500 - docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run permissions.postman_collection.json -e test-env.json --delay-request 500 + docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run permissions.postman_collection.json -e test-env.json --delay-request 1000 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run search.postman_collection.json -e test-env.json --delay-request 1000 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run artifacts.postman_collection.json -e test-env.json --delay-request 500 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run elastic.postman_collection.json -e test-env.json --delay-request 500 From b07f73bffa5f7c8f508dace951e2cf60339c2db3 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Fri, 31 Mar 2023 14:57:46 -0400 Subject: [PATCH 13/60] Fixing problem with bean creation for permissions delegates that was preventing them from getting transaction proxies created --- .../DefaultBranchPermissionsDelegate.java | 4 ++++ .../DefaultFederatedPermissionsDelegateFactory.java | 12 +++--------- .../permissions/DefaultOrgPermissionsDelegate.java | 8 ++++++++ .../DefaultProjectPermissionsDelegate.java | 8 ++++++++ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java index a6d6e408b..aaeb0bdb1 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultBranchPermissionsDelegate.java @@ -22,12 +22,16 @@ import org.openmbee.mms.rdb.repositories.ProjectGroupPermRepository; import org.openmbee.mms.rdb.repositories.ProjectUserPermRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; import org.springframework.data.util.Pair; import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; import javax.transaction.Transactional; import java.util.*; +@Component +@Scope(value = "prototype") public class DefaultBranchPermissionsDelegate extends AbstractDefaultPermissionsDelegate { private BranchRepository branchRepo; diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultFederatedPermissionsDelegateFactory.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultFederatedPermissionsDelegateFactory.java index 234f21148..8a9cf14d6 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultFederatedPermissionsDelegateFactory.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultFederatedPermissionsDelegateFactory.java @@ -53,7 +53,7 @@ public PermissionsDelegate getPermissionsDelegate(ProjectJson project) { throw new NotFoundException("project not found"); } - return autowire(new DefaultProjectPermissionsDelegate(projectOptional.get())); + return applicationContext.getBean(DefaultProjectPermissionsDelegate.class, projectOptional.get()); } @Override @@ -62,7 +62,7 @@ public PermissionsDelegate getPermissionsDelegate(OrgJson organization) { if(orgOptional.isEmpty()) { throw new NotFoundException("org not found"); } - return autowire(new DefaultOrgPermissionsDelegate(orgOptional.get())); + return applicationContext.getBean(DefaultOrgPermissionsDelegate.class, orgOptional.get()); } @Override @@ -72,12 +72,6 @@ public PermissionsDelegate getPermissionsDelegate(RefJson branch) { if(branchOptional.isEmpty()) { throw new NotFoundException("branch not found"); } - return autowire(new DefaultBranchPermissionsDelegate(branchOptional.get())); + return applicationContext.getBean(DefaultBranchPermissionsDelegate.class, branchOptional.get()); } - - private PermissionsDelegate autowire(PermissionsDelegate permissionsDelegate) { - applicationContext.getAutowireCapableBeanFactory().autowireBean(permissionsDelegate); - return permissionsDelegate; - } - } diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java index 1d85c9eed..370217058 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultOrgPermissionsDelegate.java @@ -18,11 +18,16 @@ import org.openmbee.mms.rdb.repositories.OrgUserPermRepository; import org.openmbee.mms.rdb.repositories.OrganizationRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; import org.springframework.data.util.Pair; import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import javax.transaction.Transactional; import java.util.*; +@Component +@Scope(value = "prototype") public class DefaultOrgPermissionsDelegate extends AbstractDefaultPermissionsDelegate { private OrganizationRepository orgRepo; @@ -51,6 +56,7 @@ public void setOrgGroupPermRepo(OrgGroupPermRepository orgGroupPermRepo) { } @Override + @Transactional public boolean hasPermission(String user, Set groups, String privilege) { Optional priv = getPrivRepo().findByName(privilege); @@ -124,6 +130,7 @@ public void setPublic(boolean isPublic) { } @Override + @Transactional public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest req) { FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); @@ -190,6 +197,7 @@ public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest re } @Override + @Transactional public PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest req) { FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); switch(req.getAction()) { diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java index a1108e33c..e5beb45e9 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/DefaultProjectPermissionsDelegate.java @@ -15,11 +15,16 @@ import org.openmbee.mms.rdb.repositories.ProjectRepository; import org.openmbee.mms.rdb.repositories.ProjectUserPermRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; import org.springframework.data.util.Pair; import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import javax.transaction.Transactional; import java.util.*; +@Component +@Scope(value = "prototype") public class DefaultProjectPermissionsDelegate extends AbstractDefaultPermissionsDelegate { private ProjectUserPermRepository projectUserPermRepo; @@ -60,6 +65,7 @@ public void setOrgUserPermRepo(OrgUserPermRepository orgUserPermRepo) { } @Override + @Transactional public boolean hasPermission(String user, Set groups, String privilege) { Optional priv = getPrivRepo().findByName(privilege); @@ -141,6 +147,7 @@ public void setPublic(boolean isPublic) { } @Override + @Transactional public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest req) { FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); @@ -207,6 +214,7 @@ public PermissionUpdateResponse updateUserPermissions(PermissionUpdateRequest re } @Override + @Transactional public PermissionUpdateResponse updateGroupPermissions(PermissionUpdateRequest req) { FederatedPermissionsUpdateResponseBuilder responseBuilder = new FederatedPermissionsUpdateResponseBuilder(); From 307a8b9e7e58c3ee4afc1f98876ddc78936d3774 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Tue, 4 Apr 2023 13:05:49 -0400 Subject: [PATCH 14/60] Fixed artifacts behavior --- .../mms/artifacts/crud/ArtifactsContext.java | 13 ++++ .../ArtifactsPersistenceNodeUpdateFilter.java | 24 +++++++ .../service/DefaultArtifactService.java | 29 +++++--- .../crud/domain/DefaultNodeUpdateFilter.java | 67 +++++++++++++++++++ .../mms/crud/domain/NodeChangeDomain.java | 40 +++-------- .../mms/crud/domain/NodeUpdateFilter.java | 8 +++ .../domain/FederatedNodeChangeDomain.java | 29 ++++---- 7 files changed, 153 insertions(+), 57 deletions(-) create mode 100644 artifacts/src/main/java/org/openmbee/mms/artifacts/crud/ArtifactsContext.java create mode 100644 artifacts/src/main/java/org/openmbee/mms/artifacts/crud/ArtifactsPersistenceNodeUpdateFilter.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java create mode 100644 crud/src/main/java/org/openmbee/mms/crud/domain/NodeUpdateFilter.java diff --git a/artifacts/src/main/java/org/openmbee/mms/artifacts/crud/ArtifactsContext.java b/artifacts/src/main/java/org/openmbee/mms/artifacts/crud/ArtifactsContext.java new file mode 100644 index 000000000..1e24d8697 --- /dev/null +++ b/artifacts/src/main/java/org/openmbee/mms/artifacts/crud/ArtifactsContext.java @@ -0,0 +1,13 @@ +package org.openmbee.mms.artifacts.crud; + +public class ArtifactsContext { + private static ThreadLocal artifactContext = new ThreadLocal<>(); + + public static void setArtifactContext(Boolean isArtifactContext) { + artifactContext.set(isArtifactContext); + } + + public static boolean isArtifactContext() { + return Boolean.TRUE.equals(artifactContext.get()); + } +} diff --git a/artifacts/src/main/java/org/openmbee/mms/artifacts/crud/ArtifactsPersistenceNodeUpdateFilter.java b/artifacts/src/main/java/org/openmbee/mms/artifacts/crud/ArtifactsPersistenceNodeUpdateFilter.java new file mode 100644 index 000000000..1c53ab9ca --- /dev/null +++ b/artifacts/src/main/java/org/openmbee/mms/artifacts/crud/ArtifactsPersistenceNodeUpdateFilter.java @@ -0,0 +1,24 @@ +package org.openmbee.mms.artifacts.crud; + +import org.openmbee.mms.artifacts.json.ArtifactJson; +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.crud.domain.NodeUpdateFilter; +import org.openmbee.mms.json.ElementJson; +import org.springframework.stereotype.Component; + +@Component +public class ArtifactsPersistenceNodeUpdateFilter implements NodeUpdateFilter { + @Override + public boolean filterUpdate(NodeChangeInfo nodeChangeInfo, ElementJson updated, ElementJson existing) { + if(ArtifactsContext.isArtifactContext()) { + return true; + } + //Ensure artifacts aren't cleared or added by the regular element update process + if(existing.containsKey(ArtifactJson.ARTIFACTS)) { + updated.put(ArtifactJson.ARTIFACTS, existing.get(ArtifactJson.ARTIFACTS)); + } else { + updated.remove(ArtifactJson.ARTIFACTS); + } + return true; + } +} diff --git a/artifacts/src/main/java/org/openmbee/mms/artifacts/service/DefaultArtifactService.java b/artifacts/src/main/java/org/openmbee/mms/artifacts/service/DefaultArtifactService.java index 8cffaaf18..98df304b1 100644 --- a/artifacts/src/main/java/org/openmbee/mms/artifacts/service/DefaultArtifactService.java +++ b/artifacts/src/main/java/org/openmbee/mms/artifacts/service/DefaultArtifactService.java @@ -1,5 +1,6 @@ package org.openmbee.mms.artifacts.service; +import org.openmbee.mms.artifacts.crud.ArtifactsContext; import org.openmbee.mms.artifacts.storage.ArtifactStorage; import org.openmbee.mms.artifacts.ArtifactConstants; import org.openmbee.mms.artifacts.objects.ArtifactResponse; @@ -54,7 +55,7 @@ public ArtifactResponse get(String projectId, String refId, String id, Map artifacts = ArtifactJson.getArtifacts(elementJson); ArtifactJson artifact = new ArtifactJson(); if (artifacts != null) { - artifact = getExistingArtifact(artifacts, params); + artifact = getExistingArtifact(artifacts, params, elementJson); } byte[] data = artifactStorage.get(artifact.getLocation(), elementJson, artifact.getMimeType()); ArtifactResponse response = new ArtifactResponse(); @@ -93,7 +94,12 @@ public ElementsResponse createOrUpdate(String projectId, String refId, String id elementJson = attachOrUpdateArtifact(elementJson, artifactLocation, fileExtension, mimeType, "internal", checksum); ElementsRequest elementsRequest = new ElementsRequest(); elementsRequest.setElements(Arrays.asList(elementJson)); - return nodeService.createOrUpdate(projectId, refId, elementsRequest, params, user); + try { + ArtifactsContext.setArtifactContext(true); + return nodeService.createOrUpdate(projectId, refId, elementsRequest, params, user); + } finally { + ArtifactsContext.setArtifactContext(false); + } } @Override @@ -105,12 +111,17 @@ public ElementsResponse disassociate(String projectId, String refId, String id, if (artifacts == null) { throw new NotFoundException("Artifacts not found"); } - ArtifactJson artifact = getExistingArtifact(artifacts, params); + ArtifactJson artifact = getExistingArtifact(artifacts, params, elementJson); artifacts.remove(artifact); ArtifactJson.setArtifacts(elementJson, artifacts); ElementsRequest elementsRequest = new ElementsRequest(); elementsRequest.setElements(Arrays.asList(elementJson)); - return nodeService.createOrUpdate(projectId, refId, elementsRequest, params, user); + try { + ArtifactsContext.setArtifactContext(true); + return nodeService.createOrUpdate(projectId, refId, elementsRequest, params, user); + } finally { + ArtifactsContext.setArtifactContext(false); + } } private ElementJson getElement(NodeService nodeService, String projectId, String refId, String id, Map params) { @@ -132,7 +143,7 @@ private ElementJson attachOrUpdateArtifact(ElementJson elementJson, String artif try { //nested try/nullcheck OK? if (artifacts != null) { - artifact = getExistingArtifact(artifacts, mimeType, null); + artifact = getExistingArtifact(artifacts, mimeType, null, elementJson); } else { throw new NotFoundException("Null artifact exception"); } @@ -154,11 +165,11 @@ private ElementJson attachOrUpdateArtifact(ElementJson elementJson, String artif return elementJson; } - private ArtifactJson getExistingArtifact(List artifacts, Map params) { - return getExistingArtifact(artifacts, params.get(ArtifactConstants.MIMETYPE_PARAM), params.get(ArtifactConstants.EXTENSION_PARAM)); + private ArtifactJson getExistingArtifact(List artifacts, Map params, ElementJson element) { + return getExistingArtifact(artifacts, params.get(ArtifactConstants.MIMETYPE_PARAM), params.get(ArtifactConstants.EXTENSION_PARAM), element); } - private ArtifactJson getExistingArtifact(List artifacts, String mimeType, String extension) { + private ArtifactJson getExistingArtifact(List artifacts, String mimeType, String extension, ElementJson element) { if (mimeType == null && extension == null) { throw new BadRequestException("Missing mimetype or extension"); } @@ -169,7 +180,7 @@ private ArtifactJson getExistingArtifact(List artifacts, String mi if (existing.isPresent()) { return existing.get(); } - throw new NotFoundException("Artifact not found"); + throw new NotFoundException(element); } private String getFileExtension(MultipartFile file) { diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java b/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java new file mode 100644 index 000000000..f283e05ce --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java @@ -0,0 +1,67 @@ +package org.openmbee.mms.crud.domain; + +import org.openmbee.mms.core.config.Constants; +import org.openmbee.mms.core.config.Formats; +import org.openmbee.mms.core.objects.Rejection; +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.json.BaseJson; +import org.openmbee.mms.json.ElementJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; + +@Component +public class DefaultNodeUpdateFilter implements NodeUpdateFilter { + private static final Logger logger = LoggerFactory.getLogger(DefaultNodeUpdateFilter.class); + + @Override + public boolean filterUpdate(NodeChangeInfo info, ElementJson updated, ElementJson existing) { + if (!info.getOverwrite()) { + if (Constants.TRUE.equals(existing.getIsDeleted()) || isUpdated(updated, existing, info)) { + diffUpdateJson(updated, existing, info); + } else { + return false; + } + } else { + updated.setCreator(existing.getCreator()); + updated.setCreated(existing.getCreated()); + } + return true; + } + + protected boolean isUpdated(BaseJson element, Map existing, NodeChangeInfo info) { + + if (element.isPartialOf(existing)) { + info.addRejection(element.getId(), new Rejection(element, 304, "Is Equivalent")); + return false; + } + return true; + } + + protected boolean diffUpdateJson(BaseJson element, Map existing, NodeChangeInfo info) { + + String jsonModified = element.getModified(); + Object existingModified = existing.get(BaseJson.MODIFIED); + if (jsonModified != null && !jsonModified.isEmpty()) { + try { + SimpleDateFormat dateFormat = new SimpleDateFormat(Formats.DATE_FORMAT); + Date jsonModDate = dateFormat.parse(jsonModified); + Date existingModDate = dateFormat.parse(existingModified.toString()); + if (jsonModDate.before(existingModDate)) { + info.addRejection(element.getId(), new Rejection(element, 409, "Conflict Detected")); + return false; + } + } catch (ParseException e) { + logger.info("date parse exception: {} {}", jsonModified, existingModified); + } + } + element.merge(existing); + return true; + } + +} diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java index 2e0fa2239..964276b48 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java @@ -10,6 +10,7 @@ import org.openmbee.mms.json.ElementJson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.w3c.dom.Node; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -22,9 +23,12 @@ public abstract class NodeChangeDomain extends JsonDomain { private NodeGetDomain nodeGetDomain; private CommitDomain commitDomain; - public NodeChangeDomain(NodeGetDomain nodeGetDomain, CommitDomain commitDomain) { + private List nodeUpdateFilters; + + public NodeChangeDomain(NodeGetDomain nodeGetDomain, CommitDomain commitDomain, List nodeUpdateFilters) { this.nodeGetDomain = nodeGetDomain; this.commitDomain = commitDomain; + this.nodeUpdateFilters = nodeUpdateFilters; } public NodeChangeInfo initInfo(CommitJson commitJson, boolean overwrite, boolean preserveTimestamps) { @@ -54,7 +58,10 @@ public void processElementAdded(NodeChangeInfo info, ElementJson element) { element.setCreated(commitJson.getCreated()); } - public void processElementUpdated(NodeChangeInfo info, ElementJson element) { + public void processElementUpdated(NodeChangeInfo info, ElementJson element, ElementJson existing) { + if(nodeUpdateFilters.stream().anyMatch(f -> !f.filterUpdate(info, element, existing))) { + return; + } processElementAddedOrUpdated(info, element); } @@ -105,35 +112,6 @@ protected void rejectNotFound(NodeGetInfo info, String elementId) { info.addRejection(elementId, new Rejection(elementId, 404, "Not Found")); } - protected boolean isUpdated(BaseJson element, Map existing, NodeChangeInfo info) { - - if (element.isPartialOf(existing)) { - info.addRejection(element.getId(), new Rejection(element, 304, "Is Equivalent")); - return false; - } - return true; - } - - protected boolean diffUpdateJson(BaseJson element, Map existing, NodeChangeInfo info) { - - String jsonModified = element.getModified(); - Object existingModified = existing.get(BaseJson.MODIFIED); - if (jsonModified != null && !jsonModified.isEmpty()) { - try { - SimpleDateFormat dateFormat = new SimpleDateFormat(Formats.DATE_FORMAT); - Date jsonModDate = dateFormat.parse(jsonModified); - Date existingModDate = dateFormat.parse(existingModified.toString()); - if (jsonModDate.before(existingModDate)) { - info.addRejection(element.getId(), new Rejection(element, 409, "Conflict Detected")); - return false; - } - } catch (ParseException e) { - logger.info("date parse exception: {} {}", jsonModified, existingModified); - } - } - element.merge(existing); - return true; - } // create new elastic id for all element json, update modified time, modifier (use dummy for now), set _projectId, _refId, _inRefIds public abstract NodeChangeInfo processPostJson(NodeChangeInfo nodeChangeInfo, Collection elements); diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeUpdateFilter.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeUpdateFilter.java new file mode 100644 index 000000000..0a7095c35 --- /dev/null +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeUpdateFilter.java @@ -0,0 +1,8 @@ +package org.openmbee.mms.crud.domain; + +import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.json.ElementJson; + +public interface NodeUpdateFilter { + boolean filterUpdate(NodeChangeInfo nodeChangeInfo, ElementJson updated, ElementJson existing); +} diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index 168c5ee76..be39d90ee 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -1,5 +1,7 @@ package org.openmbee.mms.federatedpersistence.domain; +import org.openmbee.mms.core.config.Constants; +import org.openmbee.mms.crud.domain.NodeUpdateFilter; import org.openmbee.mms.data.dao.NodeDAO; import org.openmbee.mms.data.dao.NodeIndexDAO; import org.openmbee.mms.core.exceptions.InternalErrorException; @@ -35,8 +37,9 @@ public class FederatedNodeChangeDomain extends NodeChangeDomain { @Autowired public FederatedNodeChangeDomain(NodeGetDomain nodeGetDomain, CommitDomain commitDomain, - FederatedNodeGetDomain getDomain, NodeDAO nodeRepository, NodeIndexDAO nodeIndex, FederatedElementDomain elementDomain) { - super(nodeGetDomain, commitDomain); + FederatedNodeGetDomain getDomain, NodeDAO nodeRepository, NodeIndexDAO nodeIndex, FederatedElementDomain elementDomain, + List nodeUpdateFilters) { + super(nodeGetDomain, commitDomain, nodeUpdateFilters); this.getDomain = getDomain; this.nodeRepository = nodeRepository; this.nodeIndex = nodeIndex; @@ -90,7 +93,7 @@ public void processElementAdded(NodeChangeInfo info, ElementJson element) { } @Override - public void processElementUpdated(NodeChangeInfo info, ElementJson element) { + public void processElementUpdated(NodeChangeInfo info, ElementJson element, ElementJson existing) { if(!(info instanceof FederatedNodeChangeInfo)) { throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); } @@ -99,11 +102,14 @@ public void processElementUpdated(NodeChangeInfo info, ElementJson element) { if(n != null) { previousDocId = n.getDocId(); ((FederatedNodeChangeInfo) info).getOldDocIds().add(previousDocId); + if(n.isDeleted()) { + existing.setIsDeleted(Constants.TRUE); + } } else { return; } - super.processElementUpdated(info, element); + super.processElementUpdated(info, element, existing); ElementVersion newObj= new ElementVersion() .setPreviousDocId(previousDocId) .setDocId(element.getDocId()) @@ -212,7 +218,6 @@ public FederatedNodeChangeInfo processPostJson(NodeChangeInfo info, Collection Date: Tue, 4 Apr 2023 13:38:30 -0400 Subject: [PATCH 15/60] extending artifacts test delay to 1000 ms --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3bea3dc3a..910f9ac4c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -37,7 +37,7 @@ jobs: docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run localauth.postman_collection.json -e test-env.json --delay-request 500 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run permissions.postman_collection.json -e test-env.json --delay-request 1000 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run search.postman_collection.json -e test-env.json --delay-request 1000 - docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run artifacts.postman_collection.json -e test-env.json --delay-request 500 + docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run artifacts.postman_collection.json -e test-env.json --delay-request 1000 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run elastic.postman_collection.json -e test-env.json --delay-request 500 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run groups.postman_collection.json -e test-env.json --delay-request 500 From a95b385c20df199d1b2e8259c36c8f30178deb11 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Wed, 5 Apr 2023 10:08:14 -0400 Subject: [PATCH 16/60] Fixing elastic test failures --- .../federatedpersistence/dao/FederatedNodePersistence.java | 6 +++--- .../java/org/openmbee/mms/rdb/repositories/BaseDAOImpl.java | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java index b1a439ea0..b7d227120 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java @@ -219,6 +219,7 @@ public FederatedNodeChangeInfo commitChanges(NodeChangeInfo info) { } //TODO: Test rollback on IndexDAO failure //TODO: move transaction stuff out into transaction service + ContextHolder.setContext(info.getCommitJson().getProjectId(), info.getCommitJson().getRefId()); TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = nodeDAO.getTransactionManager().getTransaction(def); @@ -229,13 +230,12 @@ public FederatedNodeChangeInfo commitChanges(NodeChangeInfo info) { Instant now = federatedInfo.getInstant(); if (!nodes.isEmpty()) { try { - nodeDAO.saveAll(new ArrayList<>(nodes.values())); if (json != null && !json.isEmpty()) { nodeIndexDAO.indexAll(json.values()); } - nodeIndexDAO.removeFromRef(federatedInfo.getOldDocIds()); + nodeDAO.saveAll(new ArrayList<>(nodes.values())); commitPersistence.save(cmjs,now); - + nodeIndexDAO.removeFromRef(federatedInfo.getOldDocIds()); nodeDAO.getTransactionManager().commit(status); } catch (Exception e) { logger.error("commitChanges error: ", e); diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BaseDAOImpl.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BaseDAOImpl.java index cea6c82b3..eb866cc55 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BaseDAOImpl.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/BaseDAOImpl.java @@ -24,8 +24,7 @@ public void setCrudDataSources(CrudDataSources crudDataSources) { } @Autowired - public void setTransactionManager( - PlatformTransactionManager transactionManager) { + public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } From 066a212459e4e686be492dae1ff23f2380984395 Mon Sep 17 00:00:00 2001 From: Ward Schweitzer Date: Wed, 5 Apr 2023 10:29:38 -0400 Subject: [PATCH 17/60] Fixing groups test failures --- .../dao/FederatedUserGroupsPersistence.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserGroupsPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserGroupsPersistence.java index 3d13c001b..4cb9de80d 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserGroupsPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedUserGroupsPersistence.java @@ -11,6 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import javax.transaction.Transactional; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -38,6 +39,7 @@ public void setJsonUtils(FederatedJsonUtils jsonUtils) { } @Override + @Transactional public boolean addUserToGroup(String groupName, String username) { Optional userOptional = userRepository.findByUsernameIgnoreCase(username); if(userOptional.isEmpty()) { @@ -58,6 +60,7 @@ public boolean addUserToGroup(String groupName, String username) { } @Override + @Transactional public boolean removeUserFromGroup(String groupName, String username) { Optional userOptional = userRepository.findByUsernameIgnoreCase(username); if(userOptional.isEmpty()) { @@ -78,6 +81,7 @@ public boolean removeUserFromGroup(String groupName, String username) { } @Override + @Transactional public Collection findUsersInGroup(String groupName) { Optional groupOptional = groupRepository.findByName(groupName); if(groupOptional.isEmpty()){ @@ -87,6 +91,7 @@ public Collection findUsersInGroup(String groupName) { } @Override + @Transactional public Collection findGroupsAssignedToUser(String username) { Optional userOptional = userRepository.findByUsernameIgnoreCase(username); if(userOptional.isEmpty()) { From fe50baf13ea30a7606a50272514b1f2a238f160f Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 13:53:43 -0800 Subject: [PATCH 18/60] fix NodePersistence.findById interface argument names --- .../main/java/org/openmbee/mms/core/dao/NodePersistence.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/openmbee/mms/core/dao/NodePersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/NodePersistence.java index be20c50d8..e06c7414c 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/NodePersistence.java +++ b/core/src/main/java/org/openmbee/mms/core/dao/NodePersistence.java @@ -21,7 +21,7 @@ public interface NodePersistence { NodeChangeInfo commitChanges(NodeChangeInfo nodeChangeInfo); - NodeGetInfo findById(String elementId, String refId, String commitId, String id); + NodeGetInfo findById(String projectId, String refId, String commitId, String elementId); List findAllByNodeType(String projectId, String refId, String commitId, int nodeType); From ceb011f94432b0d2884dbfb34f518e2c432f2466 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 13:56:10 -0800 Subject: [PATCH 19/60] fix FederatedNodeChangeDomain superclass constructor call --- .../domain/FederatedNodeChangeDomain.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index be39d90ee..5ceeef7be 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -36,10 +36,10 @@ public class FederatedNodeChangeDomain extends NodeChangeDomain { protected NodeIndexDAO nodeIndex; @Autowired - public FederatedNodeChangeDomain(NodeGetDomain nodeGetDomain, CommitDomain commitDomain, + public FederatedNodeChangeDomain(CommitDomain commitDomain, FederatedNodeGetDomain getDomain, NodeDAO nodeRepository, NodeIndexDAO nodeIndex, FederatedElementDomain elementDomain, List nodeUpdateFilters) { - super(nodeGetDomain, commitDomain, nodeUpdateFilters); + super(getDomain, commitDomain, nodeUpdateFilters); this.getDomain = getDomain; this.nodeRepository = nodeRepository; this.nodeIndex = nodeIndex; From d042a36588de75c22246ec88f4d74ebdc841bcac Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 14:21:17 -0800 Subject: [PATCH 20/60] change CommitPersistence.elementHistory interface so DefaultCommitService doesn't need to pass in commitIds (this is so it's not getting json just to get ids as getting entire commit json is expensive for federated persistence) --- .../openmbee/mms/core/dao/CommitPersistence.java | 2 +- .../mms/crud/services/DefaultCommitService.java | 7 +------ .../dao/FederatedCommitPersistence.java | 14 ++++++++++++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java b/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java index c31357874..ee10a8c8d 100644 --- a/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java +++ b/core/src/main/java/org/openmbee/mms/core/dao/CommitPersistence.java @@ -23,7 +23,7 @@ public interface CommitPersistence { List findByProjectAndRefAndTimestampAndLimit(String projectId, String refId, Instant timestamp, int limit); - List elementHistory(String projectId, String elementId, Set commitDocIds); + List elementHistory(String projectId, String refId, String elementId); Optional deleteById(String projectId, String commitId); diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java index 7a7b07275..b13654fa0 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java @@ -85,12 +85,7 @@ public CommitsResponse getElementCommits(String projectId, String refId, String if (!ref.isPresent()) { throw new NotFoundException("Branch not found"); } - List refCommits = commitPersistence.findByProjectAndRefAndTimestampAndLimit(projectId, refId, null, 0); - Set commitIds = new LinkedHashSet<>(); - for (CommitJson commit: refCommits) { - commitIds.add(commit.getId()); - } - res.getCommits().addAll(commitPersistence.elementHistory(projectId, elementId, commitIds)); + res.getCommits().addAll(commitPersistence.elementHistory(projectId, refId, elementId)); return res; } diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java index 75b01ec77..ddd7b1c59 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java @@ -157,9 +157,19 @@ public List findByProjectAndRefAndTimestampAndLimit(String projectId } @Override - public List elementHistory(String projectId, String elementId, Set commitDocIds) { + public List elementHistory(String projectId, String refId, String elementId) { ContextHolder.setContext(projectId); - List commits = commitIndexDAO.elementHistory(elementId, commitDocIds); + Optional branchOptional = branchDAO.findByBranchId(refId); + if(!branchOptional.isPresent()) { + return new ArrayList<>(); + } + Branch b = branchOptional.get(); + List commitList = commitDAO.findByRefAndTimestampAndLimit(b, null, 0); + Set commitIds = new HashSet<>(); + for (Commit commit: commitList) { + commitIds.add(commit.getCommitId()); + } + List commits = commitIndexDAO.elementHistory(elementId, commitIds); commits.sort(Comparator.comparing(CommitJson::getCreated).reversed()); return commits; } From 1190f3165a46a062fdcf6e69cd788e88aac75be8 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 14:44:21 -0800 Subject: [PATCH 21/60] remove redundant groups read from CameoViewService --- .../org/openmbee/mms/cameo/services/CameoViewService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java index ff85d0a3a..aab332687 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java @@ -86,8 +86,8 @@ public ElementsResponse getGroups(String projectId, String refId, Map groups = getNodePersistence().findAllByNodeType(projectId, refId, commitId, CameoNodeType.GROUP.getValue()); - ElementsResponse res = this.read(projectId, refId, buildRequestFromJsons(groups), params); - for (ElementJson e: res.getElements()) { + ElementsResponse res = new ElementsResponse().setElements(groups); + for (ElementJson e: groups) { Optional parent = getFirstRelationshipOfType(projectId, refId, commitId, e, List.of(CameoNodeType.GROUP.getValue()), CameoConstants.OWNERID); parent.ifPresent(elementJson -> e.put(CameoConstants.PARENTID, elementJson.getId())); From 8fab8167d43dc2ea8230eb41ad25925a4abeb456 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 14:47:29 -0800 Subject: [PATCH 22/60] remove posted _childViews handling from CameoViewService, this was handling ve structural changes and has been taken out of ve (5+) --- .../org/openmbee/mms/cameo/services/CameoViewService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java index aab332687..ce6329f88 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java @@ -94,7 +94,7 @@ public ElementsResponse getGroups(String projectId, String refId, Map getFirstRelationshipOfType(String projectId, String refId, String commitId, ElementJson e, List types, String relkey) { From 5db7179f8857121a5b52bc314319c74f6cdb88fc Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 15:13:20 -0800 Subject: [PATCH 23/60] continue if no permission to change project org on project update --- .../crud/controllers/projects/ProjectsController.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java index 095276dd0..739ba55e1 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java @@ -142,22 +142,23 @@ public ProjectsResponse createOrUpdateProjects( continue; } if (json.getOrgId() != null && !json.getOrgId().isEmpty()) { - projectPersistence.findById(json.getProjectId()).ifPresent(projectJson -> { + Optional projectJsonOption = projectPersistence.findById(json.getProjectId()); + if (projectJsonOption.isPresent()) { + ProjectJson projectJson = projectJsonOption.get(); String existingOrg = projectJson.getOrgId(); if (!json.getOrgId().equals(existingOrg)) { if (!mss.hasProjectPrivilege(auth, json.getProjectId(), Privileges.PROJECT_DELETE.name(), false) || !mss.hasOrgPrivilege(auth, json.getOrgId(), Privileges.ORG_CREATE_PROJECT.name(), false)) { response.addRejection( new Rejection(json, 403, "No permission to move project org")); + continue; } if (projectPersistence.inheritsPermissions(projectJson.getProjectId())) { permissionService.setProjectInherit(false, json.getProjectId()); permissionService.setProjectInherit(true, json.getProjectId()); } } - - } - ); + } } response.getProjects().add(ps.update(json)); } From 1d7513f5c22a46e3a95e8c9a68256041ff0d843d Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 16:27:07 -0800 Subject: [PATCH 24/60] reduce amount of commits gotten to test for new project in DefaultCommitService and CameoCommitService --- .../org/openmbee/mms/cameo/services/CameoCommitService.java | 2 +- .../org/openmbee/mms/crud/services/DefaultCommitService.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoCommitService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoCommitService.java index 35c326ecc..a5999f50e 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoCommitService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoCommitService.java @@ -11,7 +11,7 @@ public class CameoCommitService extends DefaultCommitService implements CommitService { @Override public boolean isProjectNew(String projectId) { - List commits = commitPersistence.findAllByProjectId(projectId); + List commits = commitPersistence.findByProjectAndRefAndTimestampAndLimit(projectId, "master", null, 2); return commits == null || commits.size() <= 1; // a cameo project gets 1 auto commit, so its still "new" } } diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java index b13654fa0..ec3224910 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultCommitService.java @@ -107,7 +107,8 @@ public CommitsResponse getCommits(String projectId, CommitsRequest req) { @Override public boolean isProjectNew(String projectId) { - List commits = commitPersistence.findAllByProjectId(projectId); + // if project is not new, there must be at least 1 commit to master + List commits = commitPersistence.findByProjectAndRefAndTimestampAndLimit(projectId, "master", null, 1); return commits == null || commits.isEmpty(); } } From 4db7311f4c6b887b48e4c8efbd1327ddb4c35a9d Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 16:45:25 -0800 Subject: [PATCH 25/60] add commitId to node get responses in DefaultNodeService and CameoNodeService, --- .../mms/cameo/services/CameoNodeService.java | 8 +++-- .../mms/crud/services/DefaultNodeService.java | 36 ++++++++++--------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java index fc83864b6..291702f53 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java @@ -10,6 +10,7 @@ import org.openmbee.mms.core.services.HierarchicalNodeService; import org.openmbee.mms.core.services.NodeChangeInfo; import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.crud.CrudConstants; import org.openmbee.mms.crud.services.DefaultNodeService; import org.openmbee.mms.json.CommitJson; import org.openmbee.mms.json.ElementJson; @@ -42,17 +43,17 @@ public void setMss(MethodSecurityService mss) { public ElementsResponse read(String projectId, String refId, ElementsRequest req, Map params) { - String commitId = params.getOrDefault(CameoConstants.COMMITID, null); + String commitId = params.getOrDefault(CrudConstants.COMMITID, null); if (commitId == null) { Optional commitJson = commitPersistence.findLatestByProjectAndRef(projectId, refId); if (!commitJson.isPresent()) { throw new InternalErrorException("Could not find latest commit for project and ref"); } - commitId = commitJson.get().getId(); + commitId = commitJson.get().getId(); } NodeGetInfo info = getNodePersistence().findAll(projectId, refId, commitId, req.getElements()); - + if (!info.getRejected().isEmpty()) { //continue looking in visible mounted projects for elements if not all found NodeGetInfo curInfo = info; @@ -78,6 +79,7 @@ public ElementsResponse read(String projectId, String refId, ElementsRequest req ElementsResponse response = new ElementsResponse(); response.getElements().addAll(info.getActiveElementMap().values()); response.setRejected(new ArrayList<>(info.getRejected().values())); + response.setCommitId(commitId); return response; } diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java index 3a2b803a6..c5c665679 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java @@ -94,6 +94,11 @@ public void readAsStream(String projectId, String refId, Map par public ElementsResponse read(String projectId, String refId, String id, Map params) { + if (id != null && !id.isEmpty()) { + logger.debug("ElementId given: {}", id); + ElementsRequest req = buildRequest(id); + return read(projectId, refId, req, params); + } String commitId = params.getOrDefault(CrudConstants.COMMITID, null); if (commitId == null) { Optional commitJson = commitPersistence.findLatestByProjectAndRef(projectId, refId); @@ -102,26 +107,14 @@ public ElementsResponse read(String projectId, String refId, String id, } commitId = commitJson.get().getId(); } - + // If no id is provided, return all ElementsResponse response = new ElementsResponse(); - if (id != null && !id.isEmpty()) { - logger.debug("ElementId given: {}", id); - - NodeGetInfo getInfo = nodePersistence.findById(projectId, refId, commitId, id); - if (!getInfo.getRejected().isEmpty()) { - response.addRejection(getInfo.getRejected().get(id)); - } else { - response.getElements().add(getInfo.getActiveElementMap().get(id)); - } - - } else { - // If no id is provided, return all - logger.debug("No ElementId given"); + logger.debug("No ElementId given"); - List nodes = nodePersistence.findAll(projectId, refId, commitId); - response.getElements().addAll(nodes); - } + List nodes = nodePersistence.findAll(projectId, refId, commitId); + response.getElements().addAll(nodes); response.getElements().forEach(v -> v.setRefId(refId)); + response.setCommitId(commitId); return response; } @@ -130,11 +123,20 @@ public ElementsResponse read(String projectId, String refId, ElementsRequest req Map params) { String commitId = params.getOrDefault(CrudConstants.COMMITID, null); + if (commitId == null) { + Optional commitJson = commitPersistence.findLatestByProjectAndRef(projectId, refId); + if (!commitJson.isPresent()) { + throw new InternalErrorException("Could not find latest commit for project and ref"); + } + commitId = commitJson.get().getId(); + } + NodeGetInfo info = nodePersistence.findAll(projectId, refId, commitId, req.getElements()); ElementsResponse response = new ElementsResponse(); response.getElements().addAll(info.getActiveElementMap().values()); response.setRejected(new ArrayList<>(info.getRejected().values())); + response.setCommitId(commitId); return response; } From d706aa44e37084231030ace95169f297075b46a7 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 16:57:15 -0800 Subject: [PATCH 26/60] removed duplicated convertJsonToMap function in DefaultNodeService, moved filter to JsonDomain --- .../mms/cameo/services/CameoViewService.java | 5 ++-- .../openmbee/mms/crud/domain/JsonDomain.java | 20 ++++++++++----- .../mms/crud/services/DefaultNodeService.java | 25 ------------------- .../jupyter/services/JupyterNodeService.java | 3 ++- .../mms/msosa/services/MsosaViewService.java | 5 ++-- 5 files changed, 22 insertions(+), 36 deletions(-) diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java index ce6329f88..98152f886 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java @@ -19,6 +19,7 @@ import org.openmbee.mms.core.objects.ElementsResponse; import org.openmbee.mms.core.services.NodeChangeInfo; import org.openmbee.mms.core.services.NodeGetInfo; +import org.openmbee.mms.crud.domain.JsonDomain; import org.openmbee.mms.json.ElementJson; import org.openmbee.mms.view.services.PropertyData; import org.openmbee.mms.view.services.ViewService; @@ -37,7 +38,7 @@ public ElementsResponse getDocuments(String projectId, String refId, Map parent = getFirstRelationshipOfType(projectId, refId, commitId, e, List.of(CameoNodeType.GROUP.getValue()), CameoConstants.OWNERID); parent.ifPresent(elementJson -> e.put(CameoConstants.SITECHARACTERIZATIONID, elementJson.getId())); - + } return res; } @@ -64,7 +65,7 @@ public void addChildViews(ElementsResponse res, Map params) { } ElementsResponse ownedAttributes = this.read(element.getProjectId(), element.getRefId(), buildRequest(ownedAttributeIds), params); - List filtered = filter(ownedAttributeIds, ownedAttributes.getElements()); + List filtered = JsonDomain.filter(ownedAttributeIds, ownedAttributes.getElements()); List childViews = new ArrayList<>(); for (ElementJson attr : filtered) { String childId = (String) attr.get(CameoConstants.TYPEID); diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java index ed1a502fe..e635058a2 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java @@ -1,19 +1,27 @@ package org.openmbee.mms.crud.domain; import org.openmbee.mms.json.ElementJson; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; + +import java.util.*; public class JsonDomain { - public Map convertJsonToMap(List elements) { + public static List filter(List ids, List orig) { + Map map = convertJsonToMap(orig); + List ret = new ArrayList<>(); + for (String id: ids) { + if (map.containsKey(id)) { + ret.add(map.get(id)); + } + } + return ret; + } + public static Map convertJsonToMap(List elements) { if (elements == null || elements.isEmpty()) { return Collections.emptyMap(); } Map result = new HashMap<>(); - + for (ElementJson elem : elements) { if (elem == null) { continue; diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java index c5c665679..9baaa1e25 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java @@ -247,29 +247,4 @@ private CommitJson createCommit(String creator, String refId, String projectId, } return cmjs; } - - protected List filter(List ids, List orig) { - Map map = convertJsonToMap(orig); - List ret = new ArrayList<>(); - for (String id: ids) { - if (map.containsKey(id)) { - ret.add(map.get(id)); - } - } - return ret; - } - - protected Map convertJsonToMap(List elements) { - Map result = new HashMap<>(); - for (ElementJson elem : elements) { - if (elem == null) { - continue; - } - if (elem.getId() != null && !elem.getId().isBlank()) { - result.put(elem.getId(), elem); - } - } - return result; - } - } diff --git a/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterNodeService.java b/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterNodeService.java index 96e57b6f2..cfcc1ed8f 100644 --- a/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterNodeService.java +++ b/jupyter/src/main/java/org/openmbee/mms/jupyter/services/JupyterNodeService.java @@ -9,6 +9,7 @@ import org.openmbee.mms.core.objects.ElementsResponse; import org.openmbee.mms.core.objects.Rejection; import org.openmbee.mms.core.services.NodeChangeInfo; +import org.openmbee.mms.crud.domain.JsonDomain; import org.openmbee.mms.json.ElementJson; import org.openmbee.mms.crud.services.DefaultNodeService; import org.openmbee.mms.core.services.NodeService; @@ -59,7 +60,7 @@ public ElementsResponse readNotebooks(String projectId, String refId, ElementsRe ElementsRequest req2 = new ElementsRequest(); req2.setElements(req2s); ElementsResponse cells = this.read(projectId, refId, req2, params); - Map cellmap = convertJsonToMap(cells.getElements()); + Map cellmap = JsonDomain.convertJsonToMap(cells.getElements()); e.put(JupyterConstants.CELLS, order((List)e.get(JupyterConstants.CELLS), cellmap)); } res.getElements().removeAll(nonNotebooks); diff --git a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaViewService.java b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaViewService.java index 55f7c0f83..cf1e7a4da 100644 --- a/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaViewService.java +++ b/msosa/src/main/java/org/openmbee/mms/msosa/services/MsosaViewService.java @@ -11,6 +11,7 @@ import java.util.Set; import java.util.UUID; +import org.openmbee.mms.crud.domain.JsonDomain; import org.openmbee.mms.msosa.MsosaConstants; import org.openmbee.mms.msosa.MsosaNodeType; import org.openmbee.mms.core.config.ContextHolder; @@ -60,7 +61,7 @@ public void addChildViews(ElementsResponse res, Map params) { List ownedAttributeIds = (List) element.get(MsosaConstants.OWNEDATTRIBUTEIDS); ElementsResponse ownedAttributes = this.read(element.getProjectId(), element.getRefId(), buildRequest(ownedAttributeIds), params); - List filtered = filter(ownedAttributeIds, ownedAttributes.getElements()); + List filtered = JsonDomain.filter(ownedAttributeIds, ownedAttributes.getElements()); List childViews = new ArrayList<>(); for (ElementJson attr : filtered) { String childId = (String) attr.get(MsosaConstants.TYPEID); @@ -153,7 +154,7 @@ public void extraProcessPostedElement(NodeChangeInfo info, ElementJson element) //existing property and type, reuse PropertyData data = oldPropertiesTypeMapping.get(typeId); newProperties.add(data); - newAttributeIds.add(data.getPropertyJson().getId()); + newAttributeIds.add(data.getPropertyJson().getId()); continue; } //create new properties and association From 322eb78b6d76f79b1a7c7bf95f188fff6ac50122 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 17:21:01 -0800 Subject: [PATCH 27/60] prevent adding change info to commit if element isn't actually being updated --- .../openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java | 2 +- .../org/openmbee/mms/crud/domain/NodeChangeDomain.java | 7 ++++--- .../domain/FederatedNodeChangeDomain.java | 8 +++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java b/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java index f283e05ce..5cefbfd53 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java @@ -23,7 +23,7 @@ public class DefaultNodeUpdateFilter implements NodeUpdateFilter { public boolean filterUpdate(NodeChangeInfo info, ElementJson updated, ElementJson existing) { if (!info.getOverwrite()) { if (Constants.TRUE.equals(existing.getIsDeleted()) || isUpdated(updated, existing, info)) { - diffUpdateJson(updated, existing, info); + return diffUpdateJson(updated, existing, info); } else { return false; } diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java index 964276b48..c7218dd3b 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java @@ -58,11 +58,12 @@ public void processElementAdded(NodeChangeInfo info, ElementJson element) { element.setCreated(commitJson.getCreated()); } - public void processElementUpdated(NodeChangeInfo info, ElementJson element, ElementJson existing) { - if(nodeUpdateFilters.stream().anyMatch(f -> !f.filterUpdate(info, element, existing))) { - return; + public boolean processElementUpdated(NodeChangeInfo info, ElementJson element, ElementJson existing) { + if (nodeUpdateFilters.stream().anyMatch(f -> !f.filterUpdate(info, element, existing))) { + return false; } processElementAddedOrUpdated(info, element); + return true; } protected void processElementAddedOrUpdated(NodeChangeInfo info, ElementJson element) { diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index 5ceeef7be..7c55061e7 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -93,7 +93,7 @@ public void processElementAdded(NodeChangeInfo info, ElementJson element) { } @Override - public void processElementUpdated(NodeChangeInfo info, ElementJson element, ElementJson existing) { + public boolean processElementUpdated(NodeChangeInfo info, ElementJson element, ElementJson existing) { if(!(info instanceof FederatedNodeChangeInfo)) { throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); } @@ -106,10 +106,12 @@ public void processElementUpdated(NodeChangeInfo info, ElementJson element, Elem existing.setIsDeleted(Constants.TRUE); } } else { - return; + return false; } - super.processElementUpdated(info, element, existing); + if (!super.processElementUpdated(info, element, existing)) { + return false; + } ElementVersion newObj= new ElementVersion() .setPreviousDocId(previousDocId) .setDocId(element.getDocId()) From 3ae6efb2053227a06766310d6e9111904832aa6b Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 17:30:29 -0800 Subject: [PATCH 28/60] fix setting initial commit of added element, prefix deleted key with _ --- .../federatedpersistence/domain/FederatedNodeChangeDomain.java | 3 ++- json/src/main/java/org/openmbee/mms/json/BaseJson.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index 7c55061e7..871468af4 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -80,7 +80,7 @@ public void processElementAdded(NodeChangeInfo info, ElementJson element) { ((FederatedNodeChangeInfo) info).getExistingNodeMap().put(element.getId(), node); super.processElementAdded(info, element); - node.setInitialCommit(element.getDocId()); + node.setInitialCommit(element.getCommitId()); CommitJson commitJson = info.getCommitJson(); ElementVersion newObj = new ElementVersion() @@ -118,6 +118,7 @@ public boolean processElementUpdated(NodeChangeInfo info, ElementJson element, E .setId(element.getId()) .setType("Element"); info.getCommitJson().getUpdated().add(newObj); + return true; } @Override diff --git a/json/src/main/java/org/openmbee/mms/json/BaseJson.java b/json/src/main/java/org/openmbee/mms/json/BaseJson.java index 0d628e022..b99b8b8e7 100644 --- a/json/src/main/java/org/openmbee/mms/json/BaseJson.java +++ b/json/src/main/java/org/openmbee/mms/json/BaseJson.java @@ -22,7 +22,7 @@ public class BaseJson extends HashMap { public static final String CREATED = "_created"; public static final String COMMITID = "_commitId"; public static final String TYPE = "type"; - public static final String IS_DELETED = "deleted"; + public static final String IS_DELETED = "_deleted"; public String getId() { return (String) this.get(ID); From 7e83e9c4d16a1e5a3b1a63a95d65d3e173a6ab8a Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 17:33:25 -0800 Subject: [PATCH 29/60] fix processElementDeleted --- .../domain/FederatedNodeChangeDomain.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index 871468af4..e3bece888 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -151,10 +151,7 @@ public void processElementDeleted(NodeChangeInfo info, ElementJson element) { throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); } Node n = ((FederatedNodeChangeInfo) info).getExistingNodeMap().get(element.getId()); - if(n != null) { - n.setNodeId(element.getId()); - n.setInitialCommit(element.getDocId()); - } else { + if (n == null) { return; } From 0340a0d0da45548ea32dcd8c6b0a4cea1caa67ff Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 22:12:30 -0800 Subject: [PATCH 30/60] use commitDAO interface in FederatedNodeChangeDomain instead of commitPersistence for better performance, change convertJsonToMap to accept collection --- .../openmbee/mms/crud/domain/JsonDomain.java | 2 +- .../domain/FederatedNodeGetDomain.java | 56 ++++++++++--------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java index e635058a2..d5d9f51b4 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/JsonDomain.java @@ -16,7 +16,7 @@ public static List filter(List ids, List orig) } return ret; } - public static Map convertJsonToMap(List elements) { + public static Map convertJsonToMap(Collection elements) { if (elements == null || elements.isEmpty()) { return Collections.emptyMap(); } diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java index c88c9ba49..83967eb92 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java @@ -3,12 +3,16 @@ import org.openmbee.mms.core.config.ContextHolder; import org.openmbee.mms.core.config.Formats; import org.openmbee.mms.core.dao.CommitPersistence; +import org.openmbee.mms.data.dao.BranchDAO; +import org.openmbee.mms.data.dao.CommitDAO; import org.openmbee.mms.data.dao.NodeDAO; import org.openmbee.mms.data.dao.NodeIndexDAO; import org.openmbee.mms.core.exceptions.BadRequestException; import org.openmbee.mms.core.exceptions.InternalErrorException; import org.openmbee.mms.core.services.NodeGetInfo; import org.openmbee.mms.crud.domain.NodeGetDomain; +import org.openmbee.mms.data.domains.scoped.Branch; +import org.openmbee.mms.data.domains.scoped.Commit; import org.openmbee.mms.data.domains.scoped.Node; import org.openmbee.mms.federatedpersistence.dao.FederatedNodeGetInfo; import org.openmbee.mms.federatedpersistence.dao.FederatedNodeGetInfoImpl; @@ -26,15 +30,18 @@ @Component public class FederatedNodeGetDomain extends NodeGetDomain { - private CommitPersistence commitPersistence; private NodeDAO nodeDAO; private NodeIndexDAO nodeIndex; + private CommitDAO commitDAO; + private BranchDAO branchDAO; @Autowired - public FederatedNodeGetDomain(CommitPersistence commitPersistence, NodeDAO nodeDAO, NodeIndexDAO nodeIndex) { - this.commitPersistence = commitPersistence; + public FederatedNodeGetDomain(NodeDAO nodeDAO, NodeIndexDAO nodeIndex, + CommitDAO commitDAO, BranchDAO branchDAO) { this.nodeDAO = nodeDAO; this.nodeIndex = nodeIndex; + this.commitDAO = commitDAO; + this.branchDAO = branchDAO; } public NodeGetInfo initInfo(List elements, CommitJson commitJson) { @@ -44,12 +51,22 @@ public NodeGetInfo initInfo(List elements, CommitJson commitJson) { NodeGetInfo nodeGetInfo = initInfoFromNodes(existingNodes, commitJson); nodeGetInfo.getReqElementMap().putAll(convertJsonToMap(elements)); return nodeGetInfo; - + } public NodeGetInfo initInfoFromNodes(List existingNodes, CommitJson commitJson) { NodeGetInfo nodeGetInfo = super.initInfo(commitJson, this::createNodeGetInfo); Set indexIds = existingNodes.stream().map(Node::getDocId).collect(Collectors.toSet()); + if (nodeGetInfo instanceof FederatedNodeGetInfo) { + FederatedNodeGetInfo federatedInfo = (FederatedNodeGetInfo)nodeGetInfo; + federatedInfo.setExistingNodeMap(new HashMap<>()); + federatedInfo.setReqIndexIds(new HashSet<>()); + for (Node node : existingNodes) { + federatedInfo.getReqIndexIds().add(node.getDocId()); + federatedInfo.getExistingNodeMap().put(node.getNodeId(), node); + federatedInfo.getReqElementMap().put(node.getNodeId(), new ElementJson().setId(node.getNodeId())); + } + } // bulk read existing elements in elastic List existingElements = nodeIndex.findAllById(indexIds); addExistingElements(nodeGetInfo, existingElements); // handeled in addExistingElements @@ -108,11 +125,11 @@ protected NodeGetInfo processCommit(NodeGetInfo info, String commitId) { throw new InternalErrorException("Invalid use of FederatedNodeGetDomain"); } FederatedNodeGetInfo federatedInfo = (FederatedNodeGetInfo) info; - Optional commit = commitPersistence.findById(getContext().getProjectId(), commitId); + Optional commit = commitDAO.findByCommitId(commitId); if (!commit.isPresent() ) { throw new BadRequestException("commitId is invalid"); } - Instant time = Instant.from(Formats.FORMATTER.parse(commit.get().getCreated())); //time of commit + Instant time = commit.get().getTimestamp(); //time of commit List refCommitIds = null; //get it later if needed for (String nodeId : info.getReqElementMap().keySet()) { if (!existingNodeContainsNodeId(federatedInfo, nodeId)) { // nodeId not found @@ -173,34 +190,23 @@ protected void addActiveElement(NodeGetInfo info, String nodeId, ElementJson ind protected List getRefCommitIds(Instant time) { List commitIds = new ArrayList<>(); - List refCommits = commitPersistence.findByProjectAndRefAndTimestampAndLimit(getContext().getProjectId(), getContext().getBranchId(), time, 0); - for (CommitJson c : refCommits) { - commitIds.add(c.getId()); - } + Optional ref = branchDAO.findByBranchId(getContext().getBranchId()); + ref.ifPresent(current -> { + List refCommits = commitDAO.findByRefAndTimestampAndLimit(current, time, 0); + for (Commit c : refCommits) { + commitIds.add(c.getCommitId()); + } + }); return commitIds; } @Override public void addExistingElements(NodeGetInfo info, List elements) { super.addExistingElements(info, elements); - - if(info instanceof FederatedNodeGetInfo) { - FederatedNodeGetInfo federatedInfo = (FederatedNodeGetInfo)info; - Set elementIds = elements.stream().map(ElementJson::getId).collect(Collectors.toSet()); - List existingNodes = nodeDAO.findAllByNodeIds(elementIds); - //ToDo : could also be done in get functions in respective classes to prevent extra null checks and NPE - federatedInfo.setExistingNodeMap(new HashMap<>()); - federatedInfo.setReqIndexIds(new HashSet<>()); - for (Node node : existingNodes) { - federatedInfo.getReqIndexIds().add(node.getDocId()); - federatedInfo.getExistingNodeMap().put(node.getNodeId(), node); - federatedInfo.getReqElementMap().put(node.getNodeId(), new ElementJson().setId(node.getNodeId())); - } - } } @Override - public NodeGetInfo createNodeGetInfo() { //ToDo :: check + public NodeGetInfo createNodeGetInfo() { //ToDo :: check return new FederatedNodeGetInfoImpl(); } } From f453d79560b1e76e5a16365db586b526da82c821 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Tue, 13 Feb 2024 22:14:24 -0800 Subject: [PATCH 31/60] clean up unused things in FederatedNodeChangeDomain --- .../mms/crud/domain/NodeChangeDomain.java | 3 --- .../domain/FederatedNodeChangeDomain.java | 15 +++++---------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java index c7218dd3b..016573da2 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java @@ -118,9 +118,6 @@ protected void rejectNotFound(NodeGetInfo info, String elementId) { public abstract NodeChangeInfo processPostJson(NodeChangeInfo nodeChangeInfo, Collection elements); - public void addExistingElements(NodeChangeInfo nodeChangeInfo, List existingElements) { - nodeGetDomain.addExistingElements(nodeChangeInfo, existingElements); - } public abstract void primeNodeChangeInfo(NodeChangeInfo nodeChangeInfo, Collection transactedElements); } diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index e3bece888..00908ad1c 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -241,10 +241,6 @@ public FederatedNodeChangeInfo processPostJson(NodeChangeInfo info, Collection existingElements) { - getDomain.addExistingElements(nodeChangeInfo, existingElements); - } //ToDo :: Check @Override @@ -258,23 +254,22 @@ public void primeNodeChangeInfo(NodeChangeInfo nodeChangeInfo, Collection existingElements = nodeIndex.findAllById(indexIds); Map existingElementMap = convertJsonToMap(existingElements); + nodeChangeInfo.setExistingElementMap(existingElementMap); + nodeChangeInfo.setReqElementMap(reqElementMap); + nodeChangeInfo.setRejected(new HashMap<>()); + nodeChangeInfo.setActiveElementMap(new HashMap<>()); if (nodeChangeInfo instanceof FederatedNodeChangeInfo) { - ((FederatedNodeChangeInfo) nodeChangeInfo).setExistingElementMap(existingElementMap); ((FederatedNodeChangeInfo) nodeChangeInfo).setExistingNodeMap(existingNodeMap); - ((FederatedNodeChangeInfo) nodeChangeInfo).setReqElementMap(reqElementMap); ((FederatedNodeChangeInfo) nodeChangeInfo).setReqIndexIds(indexIds); - ((FederatedNodeChangeInfo) nodeChangeInfo).setRejected(new HashMap<>()); - ((FederatedNodeChangeInfo) nodeChangeInfo).setActiveElementMap(new HashMap<>()); } } From cd4791e985dfcdde1d2bea2c3e7aaa6562026b42 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Wed, 14 Feb 2024 16:00:43 -0800 Subject: [PATCH 32/60] uncomment child views handling so tests run for now --- .../org/openmbee/mms/cameo/services/CameoViewService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java index 98152f886..c0749c4a6 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoViewService.java @@ -95,7 +95,7 @@ public ElementsResponse getGroups(String projectId, String refId, Map getFirstRelationshipOfType(String projectId, String refId, String commitId, ElementJson e, List types, String relkey) { From aaa1324f4fe499ae2f9d1550170a220fd14f4161 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Thu, 15 Feb 2024 00:43:01 -0800 Subject: [PATCH 33/60] if searching by commit id doesn't work, try getting by id directly this fixes jupyter tests because in jupyter, it's doing a read in the same call as an update - the read now always gets the latest commit - which elastic may not have had time to index and results in an error --- .../mms/crud/services/DefaultNodeService.java | 5 +++-- .../mms/elastic/CommitElasticDAOImpl.java | 18 +++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java index 9baaa1e25..1234febec 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java @@ -242,8 +242,9 @@ private CommitJson createCommit(String creator, String refId, String projectId, cmjs.setSource(req.getSource()); cmjs.setRefId(refId); cmjs.setProjectId(projectId); - if(commitId != null) { - cmjs.setCommitId(commitId); + if (commitId != null) { + cmjs.setId(commitId); + cmjs.setDocId(commitId); } return cmjs; } diff --git a/elastic/src/main/java/org/openmbee/mms/elastic/CommitElasticDAOImpl.java b/elastic/src/main/java/org/openmbee/mms/elastic/CommitElasticDAOImpl.java index 0b3f73268..3438335ef 100644 --- a/elastic/src/main/java/org/openmbee/mms/elastic/CommitElasticDAOImpl.java +++ b/elastic/src/main/java/org/openmbee/mms/elastic/CommitElasticDAOImpl.java @@ -173,14 +173,18 @@ private List getDocs(String commitId) { QueryBuilder commitQuery = QueryBuilders.boolQuery() .filter(QueryBuilders.termQuery(CommitJson.ID, commitId)); SearchHits hits = getCommitResults(commitQuery); - if (hits.getTotalHits().value == 0) { - return new ArrayList<>(); - } List rawCommits = new ArrayList<>(); - for (SearchHit hit : hits.getHits()) { - CommitJson ob = new CommitJson(); - ob.putAll(hit.getSourceAsMap()); - rawCommits.add(ob); // gets "_source" + if (hits.getTotalHits().value > 0) { + for (SearchHit hit : hits.getHits()) { + CommitJson ob = new CommitJson(); + ob.putAll(hit.getSourceAsMap()); + rawCommits.add(ob); // gets "_source" + } + } + if (rawCommits.isEmpty()) { + //try getting directly using id + Optional c = super.findById(this.getIndex(), commitId); + c.ifPresent(commit -> rawCommits.add(commit)); } return rawCommits; } catch (IOException ioe) { From 9a9bd03dd0b7e617adeedfd1196402da99fdbfdd Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Thu, 15 Feb 2024 00:52:34 -0800 Subject: [PATCH 34/60] reduce test delay back to 500 --- .circleci/config.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a55ff8509..8e0c28ac7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,25 +20,25 @@ jobs: - setup_remote_docker - - run: + - run: name: "Create and start all services from the docker-compose configuration" command: | cp example/src/main/resources/application.properties.example ./example/src/main/resources/application.properties docker-compose up --build -d docker run --network container:mms curlimages/curl --retry 8 --retry-delay 10 --retry-max-time 90 --retry-connrefused http://mms:8080/healthcheck - - run: + - run: name: "Run and test Postman Collection" command: | docker create -v /etc/newman --name mms_test_configs alpine:3.4 /bin/true docker cp example/. mms_test_configs:/etc/newman - docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run crud.postman_collection.json -e test-env.json --delay-request 1000 - docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run cameo.postman_collection.json -e test-env.json --delay-request 1000 + docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run crud.postman_collection.json -e test-env.json --delay-request 500 + docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run cameo.postman_collection.json -e test-env.json --delay-request 500 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run jupyter.postman_collection.json -e test-env.json --delay-request 500 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run localauth.postman_collection.json -e test-env.json --delay-request 500 - docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run permissions.postman_collection.json -e test-env.json --delay-request 1000 + docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run permissions.postman_collection.json -e test-env.json --delay-request 500 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run search.postman_collection.json -e test-env.json --delay-request 1000 - docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run artifacts.postman_collection.json -e test-env.json --delay-request 1000 + docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run artifacts.postman_collection.json -e test-env.json --delay-request 500 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run elastic.postman_collection.json -e test-env.json --delay-request 500 docker run --volumes-from mms_test_configs --network container:mms -t postman/newman run groups.postman_collection.json -e test-env.json --delay-request 500 From cd7248fe726da53789ce6090c21897bba118a9db Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Wed, 21 Feb 2024 12:33:22 -0800 Subject: [PATCH 35/60] support updating existing ref's json properties, will resurrect if previously deleted, will not allow changing parentRef or parentCommit --- .../branches/BranchesController.java | 28 +++++++++++-------- .../dao/FederatedBranchPersistence.java | 9 ++++-- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java index cc2eb6ef7..e6a446469 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/branches/BranchesController.java @@ -74,7 +74,7 @@ public RefsResponse getRef( @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("@mss.hasProjectPrivilege(authentication, #projectId, 'PROJECT_CREATE_BRANCH', false)") - public RefsResponse createRefs( + public RefsResponse createRefs( //this can also handle update @PathVariable String projectId, @RequestBody RefsRequest projectsPost, Authentication auth) { @@ -92,24 +92,30 @@ public RefsResponse createRefs( response.addRejection(new Rejection(branch, 400, "Branch id is invalid.")); continue; } - + Optional existingOptional = branchPersistence.findById(projectId, branch.getId()); + if (existingOptional.isPresent()) { + //Branch exists, should merge the json, but cannot change parent ref or commit + //if branch exists it will get resurrected if it was deleted + RefJson existing = existingOptional.get(); + if (branch.getParentRefId() != null && !branch.getParentRefId().equals(existing.getParentRefId()) || + branch.getParentCommitId() != null && !branch.getParentCommitId().equals(existing.getParentCommitId())) { + response.addRejection(new Rejection(branch, 400, "Cannot change existing branch's origin")); + continue; + } + branch.merge(existingOptional.get()); + RefJson res = branchService.updateBranch(projectId, branch); + response.getRefs().add(res); + continue; + } RefJson res; branch.setCreator(auth.getName()); if (branch.getParentCommitId() == null || branch.getParentCommitId().isEmpty()) { - Optional existingOptional = branchPersistence.findById(projectId, branch.getId()); - if (existingOptional.isPresent()) { - //Branch exists, should merge the json - branch.merge(existingOptional.get()); - res = branchService.updateBranch(projectId, branch); - } else { - res = branchService.createBranch(projectId, branch); - } + res = branchService.createBranch(projectId, branch); } else { //TODO implement branching from historical commit response.addRejection(new Rejection(branch, 400, "Branching from historical commits is not implemented.")); continue; } - permissionService.initBranchPerms(projectId, branch.getId(), true, auth.getName()); response.getRefs().add(res); } catch (MMSException e) { diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedBranchPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedBranchPersistence.java index b346c5da4..c2c694708 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedBranchPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedBranchPersistence.java @@ -44,7 +44,7 @@ public FederatedBranchPersistence(BranchDAO branchDAO, BranchGDAO branchGDAO, Br @Override public RefJson save(RefJson refJson) { //Master branch special case - boolean isMasterBranch = refJson.getName().equals(Constants.MASTER_BRANCH); + boolean isMasterBranch = refJson.getId().equals(Constants.MASTER_BRANCH); //Fill in docId if (refJson.getDocId() == null || refJson.getDocId().isEmpty()) { @@ -109,7 +109,12 @@ public RefJson save(RefJson refJson) { @Override public RefJson update(RefJson refJson) { - return save(refJson); + ContextHolder.setContext(refJson.getProjectId()); + Optional existing = branchDAO.findByBranchId(refJson.getId()); + existing.get().setDeleted(refJson.isDeleted()); + branchDAO.save(existing.get()); + branchIndexDAO.update(refJson); + return refJson; } @Override From 0f5e331f589ccb770628feddf0509de575d7749c Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Wed, 21 Feb 2024 14:06:26 -0800 Subject: [PATCH 36/60] fix updating org name --- .../federatedpersistence/dao/FederatedOrgPersistence.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedOrgPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedOrgPersistence.java index 0bc7b70bc..c293e6804 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedOrgPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedOrgPersistence.java @@ -33,10 +33,7 @@ public void setJsonUtils(FederatedJsonUtils jsonUtils) { @Override public OrgJson save(OrgJson orgJson) { Optional organizationOptional = orgDAO.findByOrganizationId(orgJson.getId()); - if (organizationOptional.isPresent()) { - throw new ForbiddenException("org " + orgJson.getId() + " already exists"); - } - Organization organization = new Organization(); + Organization organization = organizationOptional.orElse(new Organization()); organization.setOrganizationId(orgJson.getId()); organization.setOrganizationName(orgJson.getName()); return getOrgJson(orgDAO.save(organization)); From 19741515400c75c2fb7584133a375faf92a58f30 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Wed, 21 Feb 2024 14:31:59 -0800 Subject: [PATCH 37/60] prevent project update from updating schema --- .../mms/crud/controllers/projects/ProjectsController.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java index 739ba55e1..5076805ab 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java @@ -103,8 +103,12 @@ public ProjectsResponse createOrUpdateProjects( } Optional existingOptional = projectPersistence.findById(json.getProjectId()); - if(existingOptional.isPresent()) { - //Project exists, should merge the json + if (existingOptional.isPresent()) { + //Project exists, should merge the json, but not if schema is different + if (json.getProjectType() != null && !json.getProjectType().equals(existingOptional.get().getProjectType())) { + response.addRejection(new Rejection(json, 400, "Cannot change existing project schema")); + continue; + } json.merge(existingOptional.get()); } else { //New Project From 6a49df8e6f40a5483e1c9ffdbcd9d455c078599b Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Wed, 21 Feb 2024 14:34:18 -0800 Subject: [PATCH 38/60] fix setting commit id from commitJson id --- .../federatedpersistence/dao/FederatedCommitPersistence.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java index ddd7b1c59..76788e335 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java @@ -41,7 +41,7 @@ public CommitJson save(CommitJson commitJson, Instant now) { ContextHolder.setContext(commitJson.getProjectId()); Commit commit = new Commit(); commit.setComment(commitJson.getComment()); - commit.setCommitId(commitJson.getDocId()); + commit.setCommitId(commitJson.getId()); commit.setCreator(commitJson.getCreator()); commit.setBranchId(commitJson.getRefId()); commit.setCommitType(CommitType.COMMIT); From 9345bb5c2bdda1a08a21168d8adda145adafd773 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Wed, 21 Feb 2024 14:40:01 -0800 Subject: [PATCH 39/60] sort commits by descending --- .../federatedpersistence/dao/FederatedCommitPersistence.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java index 76788e335..29adccb08 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java @@ -106,7 +106,7 @@ public List findAllById(String projectId, Set commitIds) { } }); List commits = commitIndexDAO.findAllById(foundCommitIds); - commits.sort(Comparator.comparing(BaseJson::getCreated)); + commits.sort(Comparator.comparing(CommitJson::getCreated).reversed()); return commits; } From 7064a3c187ce68c2d6d05477d4a770455f68ea4d Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 24 Feb 2024 01:07:48 -0800 Subject: [PATCH 40/60] check if commitId is latest for gets and pass in null if it is for performance --- .../dao/FederatedNodePersistence.java | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java index b7d227120..8418b75dc 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java @@ -112,40 +112,34 @@ public NodeGetInfo findById(String projectId, String refId, String commitId, Str @Override public List findAllByNodeType(String projectId, String refId, String commitId, int nodeType) { ContextHolder.setContext(projectId, refId); - Optional branch = branchDAO.findByBranchId(refId); - validateBranch(branch); + String commitToPass = checkCommit(refId, commitId); List nodes; - Optional latestCommit = commitDAO.findLatestByRef(branch.get()); - if (commitId != null && !commitId.isEmpty() && !commitId.equals(latestCommit.map(Commit::getCommitId).orElse(null))) { - validateCommit(commitDAO.findByCommitId(commitId)); + if (commitToPass != null) { nodes = nodeDAO.findAllByNodeType(nodeType); } else { nodes = nodeDAO.findAllByDeletedAndNodeType(false, nodeType); } - return new ArrayList<>(getNodeGetDomain().processGetJsonFromNodes(nodes, commitId).getActiveElementMap().values()); + return new ArrayList<>(getNodeGetDomain().processGetJsonFromNodes(nodes, commitToPass).getActiveElementMap().values()); } - + @Override public NodeGetInfo findAll(String projectId, String refId, String commitId, List elements) { ContextHolder.setContext(projectId, refId); - return getNodeGetDomain().processGetJson(elements, commitId); + return getNodeGetDomain().processGetJson(elements, checkCommit(refId, commitId)); } @Override public List findAll(String projectId, String refId, String commitId) { ContextHolder.setContext(projectId, refId); - Optional branch = branchDAO.findByBranchId(refId); - validateBranch(branch); List nodes; - Optional latestCommit = commitDAO.findLatestByRef(branch.get()); - if (commitId != null && !commitId.isEmpty() && !commitId.equals(latestCommit.map(Commit::getCommitId).orElse(null))) { - validateCommit(commitDAO.findByCommitId(commitId)); + String commitToPass = checkCommit(refId, commitId); + if (commitToPass != null) { nodes = nodeDAO.findAll(); } else { nodes = nodeDAO.findAllByDeleted(false); } - return new ArrayList<>(getNodeGetDomain().processGetJsonFromNodes(nodes, commitId).getActiveElementMap().values()); + return new ArrayList<>(getNodeGetDomain().processGetJsonFromNodes(nodes, commitToPass).getActiveElementMap().values()); } @@ -153,16 +147,12 @@ public List findAll(String projectId, String refId, String commitId public void streamAllAtCommit(String projectId, String refId, String commitId, OutputStream stream, String separator) { ContextHolder.setContext(projectId, refId); List nodes; - Optional branch = branchDAO.findByBranchId(refId); - validateBranch(branch); - Optional latestCommit = commitDAO.findLatestByRef(branch.get()); - if (commitId != null && !commitId.isEmpty() && !commitId.equals(latestCommit.map(Commit::getCommitId).orElse(null))) { - validateCommit(commitDAO.findByCommitId(commitId)); + final String commitToPass = checkCommit(refId, commitId); + if (commitToPass != null) { nodes = nodeDAO.findAll(); } else { nodes = nodeDAO.findAllByDeleted(false); } - AtomicInteger counter = new AtomicInteger(); batches(nodes, streamLimit).forEach(ns -> { try { @@ -171,7 +161,7 @@ public void streamAllAtCommit(String projectId, String refId, String commitId, O } else { stream.write(separator.getBytes(StandardCharsets.UTF_8)); } - Collection result = getNodeGetDomain().processGetJsonFromNodes(ns, commitId) + Collection result = getNodeGetDomain().processGetJsonFromNodes(ns, commitToPass) .getActiveElementMap().values(); stream.write(result.stream().map(this::toJson).collect(Collectors.joining(separator)) .getBytes(StandardCharsets.UTF_8)); @@ -257,4 +247,17 @@ private void validateCommit(Optional commit) { throw new BadRequestException("commit id is invalid"); } } + + // if commitId is latest, return null + private String checkCommit(String refId, String commitId) { + Optional branch = branchDAO.findByBranchId(refId); + validateBranch(branch); + if (commitId != null && !commitId.isEmpty() && !commitId.equals( + commitDAO.findLatestByRef(branch.get()).map(Commit::getCommitId).orElse(null))) { + validateCommit(commitDAO.findByCommitId(commitId)); + return commitId; + } else { + return null; + } + } } From a30da31fe83338a7531ed7cf4f207c003ed61539 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 24 Feb 2024 01:35:59 -0800 Subject: [PATCH 41/60] remove any _deleted flag in element to be updated --- .../org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java b/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java index 5cefbfd53..7174db827 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/DefaultNodeUpdateFilter.java @@ -61,6 +61,7 @@ protected boolean diffUpdateJson(BaseJson element, Map existi } } element.merge(existing); + element.remove(ElementJson.IS_DELETED); return true; } From 7831189a80e2265e6e831bd56ca00e774f42387b Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 24 Feb 2024 01:56:44 -0800 Subject: [PATCH 42/60] set returned elements refId to match requested refId --- .../java/org/openmbee/mms/crud/services/DefaultNodeService.java | 1 + .../mms/federatedpersistence/dao/FederatedNodePersistence.java | 1 + 2 files changed, 2 insertions(+) diff --git a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java index 1234febec..43e075364 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java +++ b/crud/src/main/java/org/openmbee/mms/crud/services/DefaultNodeService.java @@ -135,6 +135,7 @@ public ElementsResponse read(String projectId, String refId, ElementsRequest req ElementsResponse response = new ElementsResponse(); response.getElements().addAll(info.getActiveElementMap().values()); + response.getElements().forEach(v -> v.setRefId(refId)); response.setRejected(new ArrayList<>(info.getRejected().values())); response.setCommitId(commitId); return response; diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java index 8418b75dc..eef4d9f10 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedNodePersistence.java @@ -163,6 +163,7 @@ public void streamAllAtCommit(String projectId, String refId, String commitId, O } Collection result = getNodeGetDomain().processGetJsonFromNodes(ns, commitToPass) .getActiveElementMap().values(); + result.forEach(v -> v.setRefId(refId)); stream.write(result.stream().map(this::toJson).collect(Collectors.joining(separator)) .getBytes(StandardCharsets.UTF_8)); } catch (IOException ioe) { From 4a9180797023f096bb97e3319316e756f10316af Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 24 Feb 2024 02:14:53 -0800 Subject: [PATCH 43/60] set refIds for cameo element gets --- .../org/openmbee/mms/cameo/services/CameoNodeService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java index 291702f53..4669d1ff8 100644 --- a/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java +++ b/cameo/src/main/java/org/openmbee/mms/cameo/services/CameoNodeService.java @@ -53,7 +53,7 @@ public ElementsResponse read(String projectId, String refId, ElementsRequest req } NodeGetInfo info = getNodePersistence().findAll(projectId, refId, commitId, req.getElements()); - + info.getActiveElementMap().values().forEach((e) -> e.setRefId(refId)); if (!info.getRejected().isEmpty()) { //continue looking in visible mounted projects for elements if not all found NodeGetInfo curInfo = info; @@ -62,9 +62,11 @@ public ElementsResponse read(String projectId, String refId, ElementsRequest req int i = 1; //0 is entry project, already gotten while (!curInfo.getRejected().isEmpty() && i < usages.size()) { + final int j = i; ElementsRequest reqNext = buildRequest(curInfo.getRejected().keySet()); //TODO use the right commitId in child if commitId is present in params :: same commit Id is not working for child curInfo = getNodePersistence().findAll(usages.get(i).getFirst(), usages.get(i).getSecond(), "", reqNext.getElements()); + curInfo.getActiveElementMap().values().forEach((e) -> e.setRefId(usages.get(j).getSecond())); info.getActiveElementMap().putAll(curInfo.getActiveElementMap()); curInfo.getActiveElementMap().forEach((id, json) -> info.getRejected().remove(id)); curInfo.getRejected().forEach((id, rejection) -> { From 064512b3c301b203b3b9be2529e77583a7096b26 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 24 Feb 2024 09:50:58 -0800 Subject: [PATCH 44/60] allow update even if previous element not found --- .../domain/FederatedNodeChangeDomain.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index 00908ad1c..ae893c26c 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -1,7 +1,9 @@ package org.openmbee.mms.federatedpersistence.domain; import org.openmbee.mms.core.config.Constants; +import org.openmbee.mms.core.config.Formats; import org.openmbee.mms.crud.domain.NodeUpdateFilter; +import org.openmbee.mms.data.dao.CommitDAO; import org.openmbee.mms.data.dao.NodeDAO; import org.openmbee.mms.data.dao.NodeIndexDAO; import org.openmbee.mms.core.exceptions.InternalErrorException; @@ -9,7 +11,7 @@ import org.openmbee.mms.core.services.NodeChangeInfo; import org.openmbee.mms.crud.domain.CommitDomain; import org.openmbee.mms.crud.domain.NodeChangeDomain; -import org.openmbee.mms.crud.domain.NodeGetDomain; +import org.openmbee.mms.data.domains.scoped.Commit; import org.openmbee.mms.data.domains.scoped.Node; import org.openmbee.mms.federatedpersistence.dao.FederatedNodeChangeInfo; import org.openmbee.mms.federatedpersistence.dao.FederatedNodeChangeInfoImpl; @@ -34,16 +36,18 @@ public class FederatedNodeChangeDomain extends NodeChangeDomain { protected FederatedElementDomain elementDomain; protected NodeDAO nodeRepository; protected NodeIndexDAO nodeIndex; + protected CommitDAO commitDAO; @Autowired public FederatedNodeChangeDomain(CommitDomain commitDomain, FederatedNodeGetDomain getDomain, NodeDAO nodeRepository, NodeIndexDAO nodeIndex, FederatedElementDomain elementDomain, - List nodeUpdateFilters) { + List nodeUpdateFilters, CommitDAO commitDAO) { super(getDomain, commitDomain, nodeUpdateFilters); this.getDomain = getDomain; this.nodeRepository = nodeRepository; this.nodeIndex = nodeIndex; this.elementDomain = elementDomain; + this.commitDAO = commitDAO; } @Override @@ -227,8 +231,12 @@ public FederatedNodeChangeInfo processPostJson(NodeChangeInfo info, Collection init = commitDAO.findByCommitId(n.getInitialCommit()); + if (init.isPresent()) { + indexElement.setCreator(init.get().getCreator()); + indexElement.setCreated(Formats.FORMATTER.format(init.get().getTimestamp())); + } } // create new doc id for all element json, update modified time, modifier (use dummy for now), set _projectId, _refId, _inRefIds From c76802c4bcdb3d719110b7ed86f02cee15d926c0 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 24 Feb 2024 09:59:13 -0800 Subject: [PATCH 45/60] handle if element json not found during gets --- .../domain/FederatedNodeGetDomain.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java index 83967eb92..146675e3f 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeGetDomain.java @@ -2,7 +2,6 @@ import org.openmbee.mms.core.config.ContextHolder; import org.openmbee.mms.core.config.Formats; -import org.openmbee.mms.core.dao.CommitPersistence; import org.openmbee.mms.data.dao.BranchDAO; import org.openmbee.mms.data.dao.CommitDAO; import org.openmbee.mms.data.dao.NodeDAO; @@ -18,6 +17,8 @@ import org.openmbee.mms.federatedpersistence.dao.FederatedNodeGetInfoImpl; import org.openmbee.mms.json.CommitJson; import org.openmbee.mms.json.ElementJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -30,6 +31,8 @@ @Component public class FederatedNodeGetDomain extends NodeGetDomain { + protected static final Logger logger = LoggerFactory.getLogger(FederatedNodeGetDomain.class); + private NodeDAO nodeDAO; private NodeIndexDAO nodeIndex; private CommitDAO commitDAO; @@ -110,7 +113,12 @@ protected NodeGetInfo processLatest(NodeGetInfo info) { continue; } ElementJson indexElement = info.getExistingElementMap().get(nodeId); - + if (indexElement == null) { + logger.warn("node db and index mismatch on element get: nodeId: " + nodeId + + ", docId not found: " + federatedInfo.getExistingNodeMap().get(nodeId).getDocId()); + rejectNotFound(info, nodeId); + continue; + } if (federatedInfo.getExistingNodeMap().get(nodeId).isDeleted()) { rejectDeleted(info, nodeId, indexElement); continue; @@ -136,7 +144,25 @@ protected NodeGetInfo processCommit(NodeGetInfo info, String commitId) { continue; } ElementJson indexElement = info.getExistingElementMap().get(nodeId); - if(info.getCommitJson() != null && info.getCommitJson().getRefId() != null) { + if (indexElement == null) { + // latest element not found, mock an object to continue + Node n = federatedInfo.getExistingNodeMap().get(nodeId); + logger.warn("node db and index mismatch on element commit get: nodeId: " + nodeId + + ", docId not found: " + n.getDocId()); + Optional last = commitDAO.findByCommitId(n.getLastCommit()); + Optional first = commitDAO.findByCommitId(n.getInitialCommit()); + if (!last.isPresent() || !first.isPresent()) { + rejectNotFound(info, nodeId); + continue; + } + indexElement = new ElementJson().setId(nodeId).setDocId(n.getDocId()); + indexElement.setModified(Formats.FORMATTER.format(last.get().getTimestamp())); + indexElement.setModifier(last.get().getCreator()); + indexElement.setCommitId(last.get().getCommitId()); + indexElement.setCreator(first.get().getCreator()); + indexElement.setCreated(Formats.FORMATTER.format(first.get().getTimestamp())); + } + if (info.getCommitJson() != null && info.getCommitJson().getRefId() != null) { indexElement.setRefId(info.getCommitJson().getRefId()); } else { indexElement.setRefId(ContextHolder.getContext().getBranchId()); From 13f1ad704c245bde92df22a5e3d2abf3c96dbaa4 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 24 Feb 2024 10:50:30 -0800 Subject: [PATCH 46/60] remove addChildViews delay --- .../org/openmbee/mms/view/controllers/VeController.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/view/src/main/java/org/openmbee/mms/view/controllers/VeController.java b/view/src/main/java/org/openmbee/mms/view/controllers/VeController.java index 480e336cb..6e54ae507 100644 --- a/view/src/main/java/org/openmbee/mms/view/controllers/VeController.java +++ b/view/src/main/java/org/openmbee/mms/view/controllers/VeController.java @@ -99,14 +99,6 @@ public ElementsResponse createOrUpdateViews( ViewService viewService = genericServiceFactory.getServiceForSchema( ViewService.class , getProjectType(projectId)); ElementsResponse res = viewService.createOrUpdate(projectId, refId, req, params, auth.getName()); - // ToDo : the following is just a workaround till the indexing part is synchronized W - try { - Thread.sleep(1000); // Providing some delay for the indexing to complete as it uses async api call - } catch(InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (Exception e) { - // No need to handle this...... - } viewService.addChildViews(res, params); return res; } From a52046c89b2c4ef0a88e5df99386508c78a39006 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 24 Feb 2024 11:24:27 -0800 Subject: [PATCH 47/60] throw error if project can't hard delete --- .../dao/FederatedProjectPersistence.java | 20 ++++++++----------- .../mms/rdb/repositories/ProjectDAOImpl.java | 6 ++++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java index 116c2fa43..68d545a13 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedProjectPersistence.java @@ -82,10 +82,10 @@ public List findAll() { @Transactional public Collection findAllByOrgId(String orgId) { Optional org = orgRepository.findByOrganizationId(orgId); - if(org.isEmpty()) { + if (org.isEmpty()) { throw new NotFoundException("org not found"); } - if(org.get().getProjects() == null) { + if (org.get().getProjects() == null) { return List.of(); } return org.get().getProjects().stream().map(project -> { @@ -100,21 +100,17 @@ public void hardDelete(String projectId) { try { ContextHolder.clearContext(); projectDAO.delete(projectId); - } catch (Exception ex){ - message = message.concat("Project DAO, cannot delete. "); + } catch (Exception e) { + message.concat(e.getMessage()); } try { ContextHolder.setContext(projectId); projectIndexDAO.delete(projectId); - if(!message.isEmpty()) { - message = message.concat("Project index DAO deleted "); - } - } catch (Exception ex){ - message = message.concat("Project index DAO, cannot delete. "); + } catch (Exception e) { + message.concat(e.getMessage()); } - - if(!message.isEmpty()) { - throw new NotFoundException(message); + if (!message.isEmpty()) { + throw new InternalErrorException(message); } } diff --git a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectDAOImpl.java b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectDAOImpl.java index 6aec4672e..0d7405a0f 100644 --- a/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectDAOImpl.java +++ b/rdb/src/main/java/org/openmbee/mms/rdb/repositories/ProjectDAOImpl.java @@ -65,13 +65,15 @@ public void delete(String projectId) { try { projectRepository.findByProjectId(projectId).ifPresent(v -> projectRepository.delete(v)); } catch (Exception ex) { - logger.error("Could not delete project from project Repository"); + logger.error("Could not delete project from project Repository", ex); + throw new InternalErrorException(ex); } try { projectOperations.deleteProjectDatabase(projectId); } catch (SQLException ex) { - logger.error("DELETE PROJECT DATABASE EXCEPTION\nPotential connection issue, query statement mishap, or unexpected RDB behavior."); + logger.error("DELETE PROJECT DATABASE EXCEPTION\nPotential connection issue, query statement mishap, or unexpected RDB behavior.", ex); + throw new InternalErrorException(ex); } } From c6aecc544f4172f6e161a984f1e180c1bc4d6494 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 24 Feb 2024 13:14:10 -0800 Subject: [PATCH 48/60] update project permissions after changing orgs if needed --- .../mms/crud/controllers/projects/ProjectsController.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java index 5076805ab..7e9a2ab92 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java +++ b/crud/src/main/java/org/openmbee/mms/crud/controllers/projects/ProjectsController.java @@ -145,6 +145,7 @@ public ProjectsResponse createOrUpdateProjects( response.addRejection(new Rejection(json, 403, "No permission to change project")); continue; } + boolean updatePermissions = false; if (json.getOrgId() != null && !json.getOrgId().isEmpty()) { Optional projectJsonOption = projectPersistence.findById(json.getProjectId()); if (projectJsonOption.isPresent()) { @@ -158,13 +159,16 @@ public ProjectsResponse createOrUpdateProjects( continue; } if (projectPersistence.inheritsPermissions(projectJson.getProjectId())) { - permissionService.setProjectInherit(false, json.getProjectId()); - permissionService.setProjectInherit(true, json.getProjectId()); + updatePermissions = true; } } } } response.getProjects().add(ps.update(json)); + if (updatePermissions) { + permissionService.setProjectInherit(false, json.getProjectId()); + permissionService.setProjectInherit(true, json.getProjectId()); + } } } catch (MMSException ex) { response.addRejection(new Rejection(json, ex.getCode().value(), ex.getMessageObject().toString())); From 1af38d9c838538828f34ed3115456116f1f3869d Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 24 Feb 2024 14:00:25 -0800 Subject: [PATCH 49/60] continue with element delete even if json not found --- .../domain/FederatedNodeChangeDomain.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index ae893c26c..5bbad8e5a 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -187,9 +187,14 @@ public FederatedNodeChangeInfo processDeleteJson(NodeChangeInfo info, Collection ElementJson indexElement = info.getExistingElementMap().get(nodeId); if (node.isDeleted()) { - info.addRejection(nodeId, new Rejection(indexElement, 410, "Already deleted")); + info.addRejection(nodeId, new Rejection(indexElement, 410, "Already deleted")); continue; } + if (indexElement == null) { + logger.warn("node db and index mismatch on element delete: nodeId: " + nodeId + + ", docId not found: " + federatedInfo.getExistingNodeMap().get(nodeId).getDocId()); + indexElement = new ElementJson().setId(nodeId).setDocId(node.getDocId()); + } ElementJson request = info.getReqElementMap().get(nodeId); request.putAll(indexElement); From e8fc60717cc14259505d9eddc8cfc9ee348e5f65 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sun, 10 Mar 2024 23:12:53 -0700 Subject: [PATCH 50/60] add more tests for branching behavior --- ...keBranchFromCommit.postman_collection.json | 403 +++++++++++++++++- 1 file changed, 388 insertions(+), 15 deletions(-) diff --git a/example/makeBranchFromCommit.postman_collection.json b/example/makeBranchFromCommit.postman_collection.json index 817a46cf2..6d863f1a7 100644 --- a/example/makeBranchFromCommit.postman_collection.json +++ b/example/makeBranchFromCommit.postman_collection.json @@ -285,7 +285,7 @@ "response": [] }, { - "name": "delete a in refa", + "name": "delete a in master", "event": [ { "listen": "test", @@ -329,7 +329,7 @@ "response": [] }, { - "name": "update b in refa", + "name": "update b in master", "event": [ { "listen": "test", @@ -381,7 +381,7 @@ "response": [] }, { - "name": "delete c in refa", + "name": "delete c in master", "event": [ { "listen": "test", @@ -433,7 +433,7 @@ "response": [] }, { - "name": "add e to refa", + "name": "add e to master", "event": [ { "listen": "test", @@ -480,7 +480,7 @@ "response": [] }, { - "name": "recurrect c in refa", + "name": "recurrect c in master", "event": [ { "listen": "test", @@ -527,7 +527,7 @@ "response": [] }, { - "name": "create branch from \"add c to master\"", + "name": "create branch from \"add d to master\"", "event": [ { "listen": "test", @@ -535,7 +535,7 @@ "exec": [ "pm.test(\"branch created with right parentRef and commit id\", function () {", " var jsonData = pm.response.json();", - " pm.expect(jsonData.refs[0].id).to.eql('addCCommitId_branch');", + " pm.expect(jsonData.refs[0].id).to.eql('addDCommitId_branch');", " pm.expect(jsonData.refs[0].parentRefId).to.eql('master');", "});" ], @@ -554,7 +554,7 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"refs\": [\n\t\t{\n\t\t\t\"id\": \"addCCommitId_branch\",\n\t\t\t\"name\": \"addCCommitId_branch\",\n\t\t\t\"type\": \"Branch\",\n\t\t\t\"parentCommitId\": \"{{addCCommitId}}\"\n\t\t}\n\t]\n}" + "raw": "{\n\t\"refs\": [\n\t\t{\n\t\t\t\"id\": \"addDCommitId_branch\",\n\t\t\t\"name\": \"addDCommitId_branch\",\n\t\t\t\"type\": \"Branch\",\n\t\t\t\"parentCommitId\": \"{{addDCommitId}}\"\n\t\t}\n\t]\n}" }, "url": { "raw": "{{host}}/projects/branch_from_commit/refs", @@ -571,19 +571,191 @@ "response": [] }, { - "name": "get elements from branch from \"add c to master\"", + "name": "create branch from \"delete c\"", "event": [ { "listen": "test", "script": { "exec": [ - "pm.test(\"response has a,b,c\", function () {", + "pm.test(\"branch created with right parentRef and commit id\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.refs[0].id).to.eql('deleteCCommitId_branch');", + " pm.expect(jsonData.refs[0].parentRefId).to.eql('master');", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"refs\": [\n\t\t{\n\t\t\t\"id\": \"deleteCCommitId_branch\",\n\t\t\t\"name\": \"deleteCCommitId_branch\",\n\t\t\t\"type\": \"Branch\",\n\t\t\t\"parentCommitId\": \"{{deleteCCommitId}}\"\n\t\t}\n\t]\n}" + }, + "url": { + "raw": "{{host}}/projects/branch_from_commit/refs", + "host": [ + "{{host}}" + ], + "path": [ + "projects", + "branch_from_commit", + "refs" + ] + } + }, + "response": [] + }, + { + "name": "create branch from \"resurrect c\"", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"branch created with right parentRef and commit id\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.refs[0].id).to.eql('resurrectCCommitId_branch');", + " pm.expect(jsonData.refs[0].parentRefId).to.eql('master');", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"refs\": [\n\t\t{\n\t\t\t\"id\": \"resurrectCCommitId_branch\",\n\t\t\t\"name\": \"resurrectCCommitId_branch\",\n\t\t\t\"type\": \"Branch\",\n\t\t\t\"parentCommitId\": \"{{resurrectCCommitId}}\"\n\t\t}\n\t]\n}" + }, + "url": { + "raw": "{{host}}/projects/branch_from_commit/refs", + "host": [ + "{{host}}" + ], + "path": [ + "projects", + "branch_from_commit", + "refs" + ] + } + }, + "response": [] + }, + { + "name": "try to change parentCommitId on existing branch", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"400 response\", function () {", + " pm.response.to.have.status(400);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"refs\": [\n\t\t{\n\t\t\t\"id\": \"resurrectCCommitId_branch\",\n\t\t\t\"name\": \"resurrectCCommitId_branch\",\n\t\t\t\"type\": \"Branch\",\n\t\t\t\"parentCommitId\": \"{{deleteCCommitId}}\"\n\t\t}\n\t]\n}" + }, + "url": { + "raw": "{{host}}/projects/branch_from_commit/refs", + "host": [ + "{{host}}" + ], + "path": [ + "projects", + "branch_from_commit", + "refs" + ] + } + }, + "response": [] + }, + { + "name": "add more metadata to existing branch", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"200 response\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"refs\": [\n\t\t{\n\t\t\t\"id\": \"resurrectCCommitId_branch\",\n\t\t\t\"name\": \"resurrectCCommitId_branch\",\n\t\t\t\"type\": \"Branch\",\n\t\t\t\"newAttribute\": \"new data\"\n\t\t}\n\t]\n}" + }, + "url": { + "raw": "{{host}}/projects/branch_from_commit/refs", + "host": [ + "{{host}}" + ], + "path": [ + "projects", + "branch_from_commit", + "refs" + ] + } + }, + "response": [] + }, + { + "name": "get elements from branch from \"add d to master\"", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"response has a,b,c,d\", function () {", " var jsonData = pm.response.json();", "", - " pm.expect(jsonData.elements.length).to.eql(3);", + " pm.expect(jsonData.elements.length).to.eql(4);", " var result = jsonData.elements.map(e => ({id: e.id}));", - " pm.expect(result).to.deep.have.members([{id: 'a'}, {id: 'b'}, {id: 'c'}]);", - " pm.expect(jsonData.commitId).to.eql(pm.environment.get('addCCommitId'));", + " pm.expect(result).to.deep.have.members([{id: 'a'}, {id: 'b'}, {id: 'c'}, {id: 'd'}]);", + " pm.expect(jsonData.commitId).to.eql(pm.environment.get('addDCommitId'));", " ", "})", "", @@ -603,7 +775,7 @@ } ], "url": { - "raw": "{{host}}/projects/branch_from_commit/refs/addCCommitId_branch/elements", + "raw": "{{host}}/projects/branch_from_commit/refs/addDCommitId_branch/elements", "host": [ "{{host}}" ], @@ -611,12 +783,213 @@ "projects", "branch_from_commit", "refs", - "addCCommitId_branch", + "addDCommitId_branch", "elements" ] } }, "response": [] + }, + { + "name": "get elements from branch from \"delete c\"", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"response has b,d\", function () {", + " var jsonData = pm.response.json();", + "", + " pm.expect(jsonData.elements.length).to.eql(2);", + " var result = jsonData.elements.map(e => ({id: e.id, name: e.name}));", + " pm.expect(result).to.deep.have.members([{id: 'b', name: 'b updated'}, {id: 'd', name: 'd'}]);", + " pm.expect(jsonData.commitId).to.eql(pm.environment.get('deleteCCommitId'));", + " ", + "})", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/projects/branch_from_commit/refs/deleteCCommitId_branch/elements", + "host": [ + "{{host}}" + ], + "path": [ + "projects", + "branch_from_commit", + "refs", + "deleteCCommitId_branch", + "elements" + ] + } + }, + "response": [] + }, + { + "name": "get elements from branch from \"resurrect c\"", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"response has b,c,d,e\", function () {", + " var jsonData = pm.response.json();", + "", + " pm.expect(jsonData.elements.length).to.eql(4);", + " var result = jsonData.elements.map(e => ({id: e.id}));", + " pm.expect(result).to.deep.have.members([{id: 'b'}, {id: 'c'}, {id: 'd'}, {id: 'e'}]);", + " pm.expect(jsonData.commitId).to.eql(pm.environment.get('resurrectCCommitId'));", + " ", + "})", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/projects/branch_from_commit/refs/resurrectCCommitId_branch/elements", + "host": [ + "{{host}}" + ], + "path": [ + "projects", + "branch_from_commit", + "refs", + "resurrectCCommitId_branch", + "elements" + ] + } + }, + "response": [] + }, + { + "name": "get commits from \"add d to master\"", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"response has right commit history\", function () {", + " var jsonData = pm.response.json();", + "", + " pm.expect(jsonData.commits.length).to.eql(3);", + " var result = jsonData.commits.map(e => ({id: e.id}));", + " pm.expect(result).to.deep.include.ordered.members([", + " {id: pm.environment.get('addDCommitId')}, ", + " {id: pm.environment.get('addCCommitId')}, ", + " {id: pm.environment.get('addABCommitId')}", + " ]);", + " ", + "})", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/projects/branch_from_commit/refs/addDCommitId_branch/commits", + "host": [ + "{{host}}" + ], + "path": [ + "projects", + "branch_from_commit", + "refs", + "addDCommitId_branch", + "commits" + ] + } + }, + "response": [] + }, + { + "name": "get commits from \"delete c\"", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"response has right commit history\", function () {", + " var jsonData = pm.response.json();", + "", + " pm.expect(jsonData.commits.length).to.eql(6);", + " var result = jsonData.commits.map(e => ({id: e.id}));", + " pm.expect(result).to.deep.include.ordered.members([", + " {id: pm.environment.get('deleteCCommitId')},", + " {id: pm.environment.get('updateBCommitId')},", + " {id: pm.environment.get('deleteACommitId')},", + " {id: pm.environment.get('addDCommitId')}, ", + " {id: pm.environment.get('addCCommitId')}, ", + " {id: pm.environment.get('addABCommitId')},", + " ]);", + "", + "})", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/projects/branch_from_commit/refs/deleteCCommitId_branch/commits", + "host": [ + "{{host}}" + ], + "path": [ + "projects", + "branch_from_commit", + "refs", + "deleteCCommitId_branch", + "commits" + ] + } + }, + "response": [] } ], "auth": { From 13318b954c42721a1ad2d775da067643e3084727 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Mon, 18 Mar 2024 20:06:21 -0700 Subject: [PATCH 51/60] use returned instance from spring data repository save --- .../permissions/AbstractDefaultPermissionsDelegate.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/AbstractDefaultPermissionsDelegate.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/AbstractDefaultPermissionsDelegate.java index 6f36ecfd8..f3086980a 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/AbstractDefaultPermissionsDelegate.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/AbstractDefaultPermissionsDelegate.java @@ -64,7 +64,8 @@ protected Pair getGroupAndRole(PermissionUpdateRequest.Permission p } if (!group.isPresent()) { group = Optional.of(new Group(p.getName())); - getGroupRepo().save(group.get()); + Group g = getGroupRepo().save(group.get()); + group = Optional.of(g); } return Pair.of(group.get(), role.get()); } From e51fa08429757ef2458cce98f1607ab360822882 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Mon, 25 Mar 2024 10:53:32 -0700 Subject: [PATCH 52/60] fix branch not found behavior when init branch perms --- .../openmbee/mms/core/services/DefaultPermissionService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java b/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java index e743b505c..06a12ab7d 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java +++ b/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java @@ -74,7 +74,7 @@ public void initProjectPerms(String projectId, boolean inherit, String creator) @Override public void initBranchPerms(String projectId, String branchId, boolean inherit, String creator) { - RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.CREATE); + RefJson branch = getBranch(projectId, branchId, BRANCH_NOTFOUND_BEHAVIOR.THROW); if(branch == null) { logger.error("Error initiating branch permissions " + projectId + " / " + branchId); throw new InternalErrorException("Could not initiate branch permissions"); @@ -348,11 +348,13 @@ private RefJson getBranch(String projectId, String branchId, BRANCH_NOTFOUND_BEH switch (mode) { case THROW: throw new NotFoundException("Branch " + projectId + " " + branchId + " not found"); + /* branch should never be created here case CREATE: RefJson b = new RefJson(); b.setProjectId(projectId); b.setRefId(branchId); return branchPersistence.save(b); + */ default: //do nothing break; From a55b5ca0b3b3851b6cc453c8ac164933e29363d7 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 30 Mar 2024 16:50:05 -0700 Subject: [PATCH 53/60] remove unused things in FederatedPermissionUpdatesResponseBuilder fix argument in DefaultPermissionService.setProjectInherit --- .../PermissionUpdatesResponseBuilder.java | 11 +++---- .../services/DefaultPermissionService.java | 2 +- ...ratedPermissionUpdatesResponseBuilder.java | 31 ------------------- 3 files changed, 5 insertions(+), 39 deletions(-) diff --git a/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdatesResponseBuilder.java b/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdatesResponseBuilder.java index d2e7a42d3..4ed15275a 100644 --- a/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdatesResponseBuilder.java +++ b/core/src/main/java/org/openmbee/mms/core/builders/PermissionUpdatesResponseBuilder.java @@ -5,8 +5,8 @@ public class PermissionUpdatesResponseBuilder { - private Boolean inherit; - private Boolean isPublic; + protected Boolean inherit; + protected Boolean isPublic; private PermissionUpdateResponseBuilder usersBuilder = new PermissionUpdateResponseBuilder(); private PermissionUpdateResponseBuilder groupsBuilder = new PermissionUpdateResponseBuilder(); @@ -56,13 +56,10 @@ public PermissionUpdateResponseBuilder getGroups() { } private Boolean or(Boolean a, Boolean b) { - if(a == b) { - return a; - } - if(a == null) { + if (a == null) { return b; } - if(b == null) { + if (b == null) { return a; } return a || b; diff --git a/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java b/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java index 06a12ab7d..c53ad3dab 100644 --- a/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java +++ b/core/src/main/java/org/openmbee/mms/core/services/DefaultPermissionService.java @@ -166,7 +166,7 @@ public PermissionUpdateResponse updateBranchGroupPerms(PermissionUpdateRequest r @Override public PermissionUpdatesResponse setProjectInherit(boolean isInherit, String projectId) { PermissionUpdatesResponseBuilder responseBuilder = new PermissionUpdatesResponseBuilder(); - responseBuilder.setInherit(true); + responseBuilder.setInherit(isInherit); ProjectJson project = getProject(projectId); PermissionsDelegate permissionsDelegate = permissionsDelegateUtil.getPermissionsDelegate(project); if (permissionsDelegate.setInherit(isInherit)) { diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionUpdatesResponseBuilder.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionUpdatesResponseBuilder.java index 03cd7271b..9922c4e77 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionUpdatesResponseBuilder.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/permissions/FederatedPermissionUpdatesResponseBuilder.java @@ -6,31 +6,9 @@ public class FederatedPermissionUpdatesResponseBuilder extends PermissionUpdatesResponseBuilder { - private Boolean inherit; - private Boolean isPublic; private FederatedPermissionsUpdateResponseBuilder usersBuilder = new FederatedPermissionsUpdateResponseBuilder(); private FederatedPermissionsUpdateResponseBuilder groupsBuilder = new FederatedPermissionsUpdateResponseBuilder(); - @Override - public FederatedPermissionUpdatesResponseBuilder setInherit(Boolean inherit) { - this.inherit = inherit; - return this; - } - - @Override - public FederatedPermissionUpdatesResponseBuilder setPublic(Boolean aPublic) { - isPublic = aPublic; - return this; - } - - @Override - public FederatedPermissionUpdatesResponseBuilder insert(PermissionUpdatesResponse permissionUpdatesResponse) { - this.inherit = or(this.inherit, permissionUpdatesResponse.getInherit()); - this.isPublic = or(this.isPublic, permissionUpdatesResponse.getPublic()); - this.insertUsers(permissionUpdatesResponse.getUsers()); - this.insertGroups(permissionUpdatesResponse.getGroups()); - return this; - } @Override public FederatedPermissionUpdatesResponseBuilder insertUsers(PermissionUpdateResponse permissionUpdateResponse) { @@ -64,13 +42,4 @@ public FederatedPermissionsUpdateResponseBuilder getGroups() { return groupsBuilder; } - private Boolean or(Boolean a, Boolean b) { - if (a == null) { - return b; - } - if (a.equals(b)) { - return a; - } - return a || b; - } } From f1786653dd3c5fa3fe61aedd0893a5e422de569b Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 30 Mar 2024 20:04:13 -0700 Subject: [PATCH 54/60] remove unneeded element delete processing --- .../openmbee/mms/crud/domain/NodeChangeDomain.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java index 016573da2..f67329e26 100644 --- a/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java +++ b/crud/src/main/java/org/openmbee/mms/crud/domain/NodeChangeDomain.java @@ -84,19 +84,10 @@ protected void processElementAddedOrUpdated(NodeChangeInfo info, ElementJson ele } public void processElementDeleted(NodeChangeInfo info, ElementJson element) { - //TODO Probably don't need this (see method below) - element.setIsDeleted("true"); } public NodeChangeInfo processDeleteJson(NodeChangeInfo info, Collection elements) { - - for(ElementJson element : elements) { - //TODO I don't think we need to do this by default, it shouldn't come back if deleted - if (Boolean.parseBoolean(element.getIsDeleted())) { - info.addRejection(element.getId(), new Rejection(element, 304, "Already deleted")); - continue; - } - + for (ElementJson element : elements) { ElementJson request = info.getReqElementMap().get(element.getId()); request.putAll(element); processElementDeleted(info, request); From 7162f862876c4e047a0b76abdf8ec9b206a8c785 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 30 Mar 2024 20:50:49 -0700 Subject: [PATCH 55/60] users list is available to authenticated users --- .../openmbee/mms/localuser/controllers/LocalUserController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localuser/src/main/java/org/openmbee/mms/localuser/controllers/LocalUserController.java b/localuser/src/main/java/org/openmbee/mms/localuser/controllers/LocalUserController.java index 5dfcba1cc..858ea4a1f 100644 --- a/localuser/src/main/java/org/openmbee/mms/localuser/controllers/LocalUserController.java +++ b/localuser/src/main/java/org/openmbee/mms/localuser/controllers/LocalUserController.java @@ -48,7 +48,7 @@ public UserCreateRequest createUser(@RequestBody UserCreateRequest req) { } @GetMapping(value = "/users") - @PreAuthorize(AuthorizationConstants.IS_MMSADMIN) + @PreAuthorize("isAuthenticated()") public UsersResponse getUsers(@RequestParam(required = false) String user) { UsersResponse res = new UsersResponse(); Collection users = new ArrayList<>(); From 844e9eba52209c10c1b1fac3e3e660d024c106c0 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 30 Mar 2024 21:01:21 -0700 Subject: [PATCH 56/60] tweak commit update in case twc module is used --- .../federatedpersistence/dao/FederatedCommitPersistence.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java index 29adccb08..9326f0ac4 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java @@ -68,10 +68,10 @@ public CommitJson update(CommitJson commitJson) { if (commitOptional.isPresent()) { Instant now = Instant.now(); commitJson.setModified(Formats.FORMATTER.format(now)); - + commitJson.setDocId(commitJson.getId()); Commit commit = commitOptional.get(); commit.setComment(commitJson.getComment()); - commit.setTimestamp(now); + //commit.setTimestamp(now); commitDAO.save(commit); return commitIndexDAO.update(commitJson); } From 7858dfbe019efa2d2762698bc9fe532e671c5ec0 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 30 Mar 2024 21:18:40 -0700 Subject: [PATCH 57/60] commit fail cleanup --- .../dao/FederatedCommitPersistence.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java index 9326f0ac4..58bb5f1a9 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java @@ -54,7 +54,11 @@ public CommitJson save(CommitJson commitJson, Instant now) { } catch (Exception e) { logger.error("Couldn't create commit: {}", commitJson.getId(), e); //Need to clean up in case of partial creation - deleteById(commitJson.getProjectId(), commitJson.getId()); + try { + deleteById(commitJson.getProjectId(), commitJson.getId()); + } catch (Exception ex) { + logger.error("Commit cleanup error: ", ex); + } throw new InternalErrorException("Could not create commit"); } } @@ -176,18 +180,16 @@ public List elementHistory(String projectId, String refId, String el @Override public Optional deleteById(String projectId, String commitId) { + // this is used for potentially cleaning up a failed commit save + // should not be used intentionally ContextHolder.setContext(projectId); Optional commitOptional = commitDAO.findByCommitId(commitId); - try { - Optional commitJsonOptional = commitOptional.isPresent() ? - commitIndexDAO.findById(commitOptional.get().getCommitId()) : Optional.empty(); - if (commitJsonOptional.isPresent()) { - commitIndexDAO.deleteById(commitJsonOptional.get().getDocId()); - return commitJsonOptional; - } - } catch (Exception e) { - throw new InternalErrorException("Could not delete commit"); + Optional commitJsonOptional = commitOptional.isPresent() ? + commitIndexDAO.findById(commitOptional.get().getCommitId()) : Optional.empty(); + if (commitJsonOptional.isPresent()) { + commitIndexDAO.deleteById(commitJsonOptional.get().getDocId()); + return commitJsonOptional; } - throw new NotFoundException("Could not delete commit"); + return Optional.empty(); } } From 26300d3461ce0d2e0af8a7dadc2f3ebb771b0694 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 30 Mar 2024 21:23:54 -0700 Subject: [PATCH 58/60] fix commit delete --- .../dao/FederatedCommitPersistence.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java index 58bb5f1a9..9b3abc81d 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/dao/FederatedCommitPersistence.java @@ -183,11 +183,9 @@ public Optional deleteById(String projectId, String commitId) { // this is used for potentially cleaning up a failed commit save // should not be used intentionally ContextHolder.setContext(projectId); - Optional commitOptional = commitDAO.findByCommitId(commitId); - Optional commitJsonOptional = commitOptional.isPresent() ? - commitIndexDAO.findById(commitOptional.get().getCommitId()) : Optional.empty(); + Optional commitJsonOptional = commitIndexDAO.findById(commitId); if (commitJsonOptional.isPresent()) { - commitIndexDAO.deleteById(commitJsonOptional.get().getDocId()); + commitIndexDAO.deleteById(commitId); return commitJsonOptional; } return Optional.empty(); From 404ef002c2d814e001e0cd983188155bcdfd8624 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 30 Mar 2024 22:12:30 -0700 Subject: [PATCH 59/60] in FederatedNodeChangeDomain.primeNodeChangeInfo, add to any existing info instead of reinit data --- .../domain/FederatedNodeChangeDomain.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index 5bbad8e5a..e486a0393 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -58,14 +58,14 @@ protected NodeChangeInfo createNodeChangeInfo() { @Override public NodeChangeInfo initInfo(CommitJson commitJson, boolean overwrite, boolean preserveTimestamps) { commitJson.setId(UUID.randomUUID().toString()); - commitJson.setDocId(commitJson.getId()); + commitJson.setDocId(commitJson.getId()); NodeChangeInfo info = super.initInfo(commitJson, overwrite, preserveTimestamps); if(info instanceof FederatedNodeChangeInfo) { ((FederatedNodeChangeInfo)info).setOldDocIds(new HashSet<>()); ((FederatedNodeChangeInfo)info).setReqIndexIds(new HashSet<>()); ((FederatedNodeChangeInfo)info).setToSaveNodeMap(new HashMap<>()); - } else { + } else { throw new InternalErrorException("Unexpected NodeChangeInfo type in FederatedNodeChangeDomain"); } return info; @@ -140,7 +140,7 @@ protected void processElementAddedOrUpdated(NodeChangeInfo info, ElementJson ele String docId = UUID.randomUUID().toString(); element.setDocId(docId); - super.processElementAddedOrUpdated(info, element); + super.processElementAddedOrUpdated(info, element); n.setDocId(element.getDocId()); n.setLastCommit(info.getCommitJson().getId()); n.setDeleted(false); @@ -255,35 +255,39 @@ public FederatedNodeChangeInfo processPostJson(NodeChangeInfo info, Collection transactedElements) { Set elementIds = transactedElements.stream().map(BaseJson::getId).filter(id->null!=id).collect(Collectors.toSet()); List existingNodes = nodeRepository.findAllByNodeIds(elementIds); - Set indexIds = new HashSet<>(); - Map existingNodeMap = new HashMap<>(); - Map reqElementMap = new HashMap<>(); + if (!(nodeChangeInfo instanceof FederatedNodeChangeInfo)) { + throw new InternalErrorException("Node processing is not using FederatedNodeChangeInfo"); + } + + FederatedNodeChangeInfo info = (FederatedNodeChangeInfo)nodeChangeInfo; + Set indexIds = Optional.ofNullable(info.getReqIndexIds()).orElse(new HashSet<>()); + Map existingNodeMap = Optional.ofNullable(info.getExistingNodeMap()).orElse(new HashMap<>()); + Map reqElementMap = Optional.ofNullable(info.getReqElementMap()).orElse(new HashMap<>()); + Map existingElementMap = Optional.ofNullable(info.getExistingElementMap()).orElse(new HashMap<>()); + for (Node node : existingNodes) { indexIds.add(node.getDocId()); existingNodeMap.put(node.getNodeId(), node); } - if(!transactedElements.isEmpty()){ + if (!transactedElements.isEmpty()) { reqElementMap.putAll(convertJsonToMap(transactedElements)); } // bulk read existing elements in elastic List existingElements = nodeIndex.findAllById(indexIds); - Map existingElementMap = convertJsonToMap(existingElements); - - nodeChangeInfo.setExistingElementMap(existingElementMap); - nodeChangeInfo.setReqElementMap(reqElementMap); - nodeChangeInfo.setRejected(new HashMap<>()); - nodeChangeInfo.setActiveElementMap(new HashMap<>()); - if (nodeChangeInfo instanceof FederatedNodeChangeInfo) { - ((FederatedNodeChangeInfo) nodeChangeInfo).setExistingNodeMap(existingNodeMap); - ((FederatedNodeChangeInfo) nodeChangeInfo).setReqIndexIds(indexIds); - } + existingElementMap.putAll(convertJsonToMap(existingElements)); + + info.setExistingElementMap(existingElementMap); + info.setReqElementMap(reqElementMap); + info.setRejected(Optional.ofNullable(info.getRejected()).orElse(new HashMap<>())); + info.setActiveElementMap(Optional.ofNullable(info.getActiveElementMap()).orElse(new HashMap<>())); + info.setExistingNodeMap(existingNodeMap); + info.setReqIndexIds(indexIds); } } From 27f056a6e8a949baab01dac17483a425eaf15377 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Sat, 30 Mar 2024 22:46:17 -0700 Subject: [PATCH 60/60] only further process transacted elements but keep other existing info from passed in nodeChangeInfo --- .../domain/FederatedNodeChangeDomain.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java index e486a0393..1d4ecc866 100644 --- a/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java +++ b/federatedpersistence/src/main/java/org/openmbee/mms/federatedpersistence/domain/FederatedNodeChangeDomain.java @@ -267,10 +267,12 @@ public void primeNodeChangeInfo(NodeChangeInfo nodeChangeInfo, Collection indexIds = Optional.ofNullable(info.getReqIndexIds()).orElse(new HashSet<>()); Map existingNodeMap = Optional.ofNullable(info.getExistingNodeMap()).orElse(new HashMap<>()); - Map reqElementMap = Optional.ofNullable(info.getReqElementMap()).orElse(new HashMap<>()); + Map reqElementMap = new HashMap<>(); // don't add to any existing since this change processing is only for transactedElements Map existingElementMap = Optional.ofNullable(info.getExistingElementMap()).orElse(new HashMap<>()); + Set transactedIndexIds = new HashSet<>(); for (Node node : existingNodes) { + transactedIndexIds.add(node.getDocId()); indexIds.add(node.getDocId()); existingNodeMap.put(node.getNodeId(), node); } @@ -279,7 +281,7 @@ public void primeNodeChangeInfo(NodeChangeInfo nodeChangeInfo, Collection existingElements = nodeIndex.findAllById(indexIds); + List existingElements = nodeIndex.findAllById(transactedIndexIds); existingElementMap.putAll(convertJsonToMap(existingElements)); info.setExistingElementMap(existingElementMap);