diff --git a/invenio_communities/communities/dumpers/featured.py b/invenio_communities/communities/dumpers/featured.py index 19f6cf3c8..38ccfd1ab 100644 --- a/invenio_communities/communities/dumpers/featured.py +++ b/invenio_communities/communities/dumpers/featured.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Invenio. -# Copyright (C) 2022 Graz University of Technology. +# Copyright (C) 2022-2024 Graz University of Technology. # # Invenio is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -14,6 +14,7 @@ from datetime import datetime +from invenio_db import db from invenio_records.dumpers import SearchDumperExt from invenio_communities.communities.records.models import CommunityFeatured @@ -30,7 +31,8 @@ def dump(self, record, data): """Dump featured entries.""" now_ = datetime.utcnow() future_entries = ( - CommunityFeatured.query.filter( + db.session.query(CommunityFeatured) + .filter( CommunityFeatured.community_id == record.id, CommunityFeatured.start_date > now_, ) @@ -39,7 +41,8 @@ def dump(self, record, data): ) past_entries = ( - CommunityFeatured.query.filter( + db.session.query(CommunityFeatured) + .filter( CommunityFeatured.community_id == record.id, CommunityFeatured.start_date <= now_, ) diff --git a/invenio_communities/communities/records/systemfields/pidslug.py b/invenio_communities/communities/records/systemfields/pidslug.py index 83ef7af58..b69db71c6 100644 --- a/invenio_communities/communities/records/systemfields/pidslug.py +++ b/invenio_communities/communities/records/systemfields/pidslug.py @@ -2,6 +2,7 @@ # # This file is part of Invenio. # Copyright (C) 2022 CERN. +# Copyright (C) 2024 Graz University of Technology. # # Invenio is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -41,9 +42,11 @@ def resolve(self, pid_value, registered_only=True): raise PIDDoesNotExistError("comid", "") with db.session.no_autoflush: # avoid flushing the current session - model = self.record_cls.model_cls.query.filter_by( - **{field_name: pid_value} - ).one_or_none() + model = ( + db.session.query(self.record_cls.model_cls) + .filter_by(**{field_name: pid_value}) + .one_or_none() + ) if model is None: raise PIDDoesNotExistError("comid", str(pid_value)) record = self.record_cls(model.data, model=model) diff --git a/invenio_communities/communities/services/components.py b/invenio_communities/communities/services/components.py index 4c5bdebba..c8aa71734 100644 --- a/invenio_communities/communities/services/components.py +++ b/invenio_communities/communities/services/components.py @@ -3,7 +3,7 @@ # This file is part of Invenio. # Copyright (C) 2016-2024 CERN. # Copyright (C) 2022 Northwestern University. -# Copyright (C) 2022-2023 Graz University of Technology. +# Copyright (C) 2022-2024 Graz University of Technology. # # Invenio is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -152,7 +152,11 @@ class OAISetComponent(ServiceComponent): """Service component for OAI set integration.""" def _retrieve_set(self, slug): - return OAISet.query.filter(OAISet.spec == self._create_set_spec(slug)).first() + return ( + db.session.query(OAISet) + .filter(OAISet.spec == self._create_set_spec(slug)) + .first() + ) def _create_set_spec(self, community_slug): oai_sets_prefix = current_app.config["COMMUNITIES_OAI_SETS_PREFIX"] diff --git a/invenio_communities/communities/services/service.py b/invenio_communities/communities/services/service.py index e653125ec..605d9b68d 100644 --- a/invenio_communities/communities/services/service.py +++ b/invenio_communities/communities/services/service.py @@ -13,6 +13,7 @@ from flask import current_app from invenio_cache.decorators import cached_with_expiration +from invenio_db import db from invenio_records_resources.proxies import current_service_registry from invenio_records_resources.services.base import LinksTemplate from invenio_records_resources.services.records import ( @@ -295,7 +296,9 @@ def _get_featured_entry(self, raise_error=True, **kwargs): """Retrieve featured entry based on provided arguments.""" errors = [] try: - featured_entry = CommunityFeatured.query.filter_by(**kwargs).one() + featured_entry = ( + db.session.query(CommunityFeatured).filter_by(**kwargs).one() + ) except NoResultFound as e: if raise_error: raise CommunityFeaturedEntryDoesNotExistError(kwargs) @@ -345,10 +348,14 @@ def featured_list(self, identity, community_id): # Permissions self.require_permission(identity, "featured_list", record=record) - featured_entries = CommunityFeatured.query.filter( - CommunityFeatured.community_id == record.id, - ).paginate( - per_page=1000, + featured_entries = ( + db.session.query(CommunityFeatured) + .filter( + CommunityFeatured.community_id == record.id, + ) + .paginate( + per_page=1000, + ) ) return self.config.result_list_cls_featured( diff --git a/invenio_communities/generators.py b/invenio_communities/generators.py index 6d5cb9031..bad819125 100644 --- a/invenio_communities/generators.py +++ b/invenio_communities/generators.py @@ -2,7 +2,7 @@ # # This file is part of Invenio. # Copyright (C) 2016-2024 CERN. -# Copyright (C) 2021 Graz University of Technology. +# Copyright (C) 2021-2024 Graz University of Technology. # Copyright (C) 2021 TU Wien. # Copyright (C) 2022 Northwestern University. # diff --git a/invenio_communities/members/records/api.py b/invenio_communities/members/records/api.py index 5bf3ac28b..499414000 100644 --- a/invenio_communities/members/records/api.py +++ b/invenio_communities/members/records/api.py @@ -2,7 +2,7 @@ # # Copyright (C) 2022 Northwestern University. # Copyright (C) 2022 CERN. -# Copyright (C) 2022 Graz University of Technology. +# Copyright (C) 2022-2024 Graz University of Technology. # # Invenio-Communities is free software; you can redistribute it and/or modify # it under the terms of the MIT License; see LICENSE file for more details. @@ -105,7 +105,7 @@ def get_memberships_from_group_ids(cls, identity, group_ids): def get_memberships(cls, identity): """Get community memberships for a given identity.""" group_ids = [] - user = User.query.filter(User.id == identity.id).one_or_none() + user = db.session.query(User).filter(User.id == identity.id).one_or_none() if user: group_ids = [r.id for r in user.roles] @@ -119,9 +119,11 @@ def get_member_by_request(cls, request_id): """Get a membership by request id.""" assert request_id is not None with db.session.no_autoflush: - obj = cls.model_cls.query.filter( - cls.model_cls.request_id == request_id - ).one() + obj = ( + db.session.query(cls.model_cls) + .filter(cls.model_cls.request_id == request_id) + .one() + ) return cls(obj.data, model=obj) @classmethod @@ -139,7 +141,9 @@ def get_members(cls, community_id, members=None): raise InvalidMemberError(m) with db.session.no_autoflush: - q = cls.model_cls.query.filter(cls.model_cls.community_id == community_id) + q = db.session.query(cls.model_cls).filter( + cls.model_cls.community_id == community_id + ) # Apply user and group query if applicable user_q = cls.model_cls.user_id.in_(user_ids) diff --git a/invenio_communities/members/records/models.py b/invenio_communities/members/records/models.py index d7530a4a1..418e88bf9 100644 --- a/invenio_communities/members/records/models.py +++ b/invenio_communities/members/records/models.py @@ -2,7 +2,7 @@ # # Copyright (C) 2022 Northwestern University. # Copyright (C) 2022-2024 CERN. -# Copyright (C) 2022 Graz University of Technology. +# Copyright (C) 2022-2024 Graz University of Technology. # # Invenio-Communities is free software; you can redistribute it and/or modify # it under the terms of the MIT License; see LICENSE file for more details. @@ -105,7 +105,9 @@ def query_memberships(cls, user_id=None, group_ids=None, active=True): @classmethod def count_members(cls, community_id, role=None, active=True): """Count number of members.""" - q = cls.query.filter(cls.community_id == community_id, cls.active == active) + q = db.session.query(cls).filter( + cls.community_id == community_id, cls.active == active + ) if role is not None: q = q.filter(cls.role == role) return q.count() diff --git a/invenio_communities/members/services/components.py b/invenio_communities/members/services/components.py index f4c133b4d..c7359c32e 100644 --- a/invenio_communities/members/services/components.py +++ b/invenio_communities/members/services/components.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Invenio. -# Copyright (C) 2022 Graz University of Technology. +# Copyright (C) 2022-2024 Graz University of Technology. # # Invenio is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -10,6 +10,7 @@ from flask_principal import Identity from invenio_accounts.models import Role +from invenio_db import db from invenio_records_resources.services.records.components import ServiceComponent from invenio_communities.members.records.api import MemberMixin @@ -59,7 +60,7 @@ def accept_invite(self, identity, record=None, data=None, **kwargs): def members_add(self, identity, record=None, community=None, data=None, **kwargs): """On member add (only for groups).""" if record["type"] == "group": - role = Role.query.filter_by(id=record["id"]).one_or_none() + role = db.session.query(Role).filter_by(id=record["id"]).one_or_none() if role.is_managed: users = role.users.all() for user in users: diff --git a/invenio_communities/members/services/service.py b/invenio_communities/members/services/service.py index 27edcc5dc..6448b44d6 100644 --- a/invenio_communities/members/services/service.py +++ b/invenio_communities/members/services/service.py @@ -2,7 +2,7 @@ # # Copyright (C) 2022 Northwestern University. # Copyright (C) 2022-2024 CERN. -# Copyright (C) 2022-2023 Graz University of Technology. +# Copyright (C) 2022-2024 Graz University of Technology. # # Invenio-Communities is free software; you can redistribute it and/or modify # it under the terms of the MIT License; see LICENSE file for more details. @@ -14,6 +14,7 @@ from flask import current_app from invenio_access.permissions import system_identity from invenio_accounts.models import Role +from invenio_db import db from invenio_i18n import gettext as _ from invenio_notifications.services.uow import NotificationOp from invenio_records_resources.services import LinksTemplate @@ -533,6 +534,7 @@ def update(self, identity, community_id, data, uow=None, refresh=False): # Perform updates (and check permissions) for m in members: + print(f"MemberService.update m: {m}") self._update(identity, community, m, role, visible, uow) # Make sure we're not left owner-less if a role was changed. @@ -731,12 +733,18 @@ def rebuild_index(self, identity, uow=None): Note: Skips (soft) deleted records. """ - members = self.record_cls.model_cls.query.filter_by(is_deleted=False).all() + members = ( + db.session.query(self.record_cls.model_cls) + .filter_by(is_deleted=False) + .all() + ) self.indexer.bulk_index([member.id for member in members]) - archived_invitations = ArchivedInvitation.model_cls.query.filter_by( - is_deleted=False - ).all() + archived_invitations = ( + db.session.query(ArchivedInvitation.model_cls) + .filter_by(is_deleted=False) + .all() + ) self.archive_indexer.bulk_index([inv.id for inv in archived_invitations]) return True diff --git a/invenio_communities/records/records/systemfields/communities/context.py b/invenio_communities/records/records/systemfields/communities/context.py index ec8009396..2d03d6090 100644 --- a/invenio_communities/records/records/systemfields/communities/context.py +++ b/invenio_communities/records/records/systemfields/communities/context.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # # Copyright (C) 2021 CERN. +# Copyright (C) 2024 Graz University of Technology. # # Invenio-Communities is free software; you can redistribute it and/or # modify it under the terms of the MIT License; see LICENSE file for more @@ -8,6 +9,7 @@ """Field context.""" +from invenio_db import db from invenio_records.systemfields import SystemFieldContext @@ -25,4 +27,6 @@ class CommunitiesFieldContext(SystemFieldContext): def query_by_community(self, community_or_id): """Query community-record relations for a given community.""" - return self.field._m2m_model_class.query.filter(community_id=community_or_id) + return db.session.query(self.field._m2m_model_class).filter( + community_id=community_or_id + ) diff --git a/invenio_communities/records/records/systemfields/communities/manager.py b/invenio_communities/records/records/systemfields/communities/manager.py index aa291d111..9e35676dd 100644 --- a/invenio_communities/records/records/systemfields/communities/manager.py +++ b/invenio_communities/records/records/systemfields/communities/manager.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # # Copyright (C) 2021-2024 CERN. +# Copyright (C) 2024 Graz University of Technology. # # Invenio-Communities is free software; you can redistribute it and/or # modify it under the terms of the MIT License; see LICENSE file for more @@ -89,9 +90,11 @@ def remove(self, community_or_id): community_id = self._to_id(community_or_id) # Delete M2M row. - res = self._m2m_model_cls.query.filter_by( - community_id=community_id, record_id=self._record_id - ).delete() + res = ( + db.session.query(self._m2m_model_cls) + .filter_by(community_id=community_id, record_id=self._record_id) + .delete() + ) if res != 1: raise ValueError("The record has not been added to the community.") @@ -105,7 +108,9 @@ def remove(self, community_or_id): def clear(self): """Clear all communities from the record.""" # Remove all associations - res = self._m2m_model_cls.query.filter_by(record_id=self._record_id).delete() + db.session.query(self._m2m_model_cls).filter_by( + record_id=self._record_id + ).delete() self._communities_ids = set() self._default_id = None self._communities_cache = {} diff --git a/invenio_communities/requests/user_moderation/actions.py b/invenio_communities/requests/user_moderation/actions.py index 4a09cb051..2be6889e2 100644 --- a/invenio_communities/requests/user_moderation/actions.py +++ b/invenio_communities/requests/user_moderation/actions.py @@ -2,6 +2,7 @@ # # Copyright (C) 2023-2024 CERN. # Copyright (C) 2023 TU Wien. +# Copyright (C) 2024 Graz University of Technology. # # Invenio-Communities is free software; you can redistribute it and/or modify # it under the terms of the MIT License; see LICENSE file for more details. @@ -10,6 +11,7 @@ from collections import defaultdict from invenio_access.permissions import Identity, system_identity +from invenio_db import db from invenio_i18n import lazy_gettext as _ from invenio_pidstore.errors import PIDDoesNotExistError from invenio_search.engine import dsl @@ -41,7 +43,9 @@ def _get_communities_for_user(user_id): comm_owners = defaultdict(list) for comm_owner in [ mem_cls(m.data, model=m) - for m in mem_model_cls.query.filter(mem_model_cls.role == "owner").all() + for m in db.session.query(mem_model_cls) + .filter(mem_model_cls.role == "owner") + .all() ]: comm_owners[comm_owner.community_id].append(comm_owner) @@ -55,9 +59,9 @@ def _get_communities_for_user(user_id): # resolve the communities in question communities = [ comm_cls(m.data, model=m) - for m in comm_model_cls.query.filter( - comm_model_cls.id.in_(relevant_comm_ids) - ).all() + for m in db.session.query(comm_model_cls) + .filter(comm_model_cls.id.in_(relevant_comm_ids)) + .all() ] return communities diff --git a/invenio_communities/utils.py b/invenio_communities/utils.py index 0d4a7dddd..11d693e37 100644 --- a/invenio_communities/utils.py +++ b/invenio_communities/utils.py @@ -2,6 +2,7 @@ # # This file is part of Invenio. # Copyright (C) 2016-2022 CERN. +# Copyright (C) 2024 Graz University of Technology. # # Invenio is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -12,6 +13,7 @@ from flask_principal import Identity from invenio_accounts.models import Role from invenio_accounts.proxies import current_db_change_history +from invenio_db import db from .generators import CommunityRoleNeed from .proxies import current_communities, current_identities_cache @@ -105,7 +107,7 @@ def on_datastore_post_commit(sender, session): on_user_membership_change(Identity(user_id)) for role_id in current_db_change_history.sessions[sid].deleted_roles: - role = Role.query.filter_by(id=role_id).one_or_none() + role = db.session.query(Role).filter_by(id=role_id).one_or_none() users = role.users.all() for user in users: on_user_membership_change(Identity(user.id)) diff --git a/tests/communities/test_components.py b/tests/communities/test_components.py index 6b24c7071..56da9e7d4 100644 --- a/tests/communities/test_components.py +++ b/tests/communities/test_components.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2022 Graz University of Technology. +# Copyright (C) 2022-2024 Graz University of Technology. # # Invenio-Communities is free software; you can redistribute it and/or modify # it under the terms of the MIT License; see LICENSE file for more details. @@ -12,6 +12,7 @@ import pytest from invenio_access.permissions import system_identity +from invenio_db import db from invenio_oaiserver.models import OAISet from invenio_records_resources.services.errors import PermissionDeniedError @@ -20,9 +21,11 @@ def _retrieve_oaiset(service, community): comp = OAISetComponent(service) - return OAISet.query.filter( - OAISet.spec == comp._create_set_spec(community.get("slug")) - ).first() + return ( + db.session.query(OAISet) + .filter(OAISet.spec == comp._create_set_spec(community.get("slug"))) + .first() + ) @pytest.fixture() diff --git a/tests/communities/test_services.py b/tests/communities/test_services.py index 65827447b..f2e87f6fa 100644 --- a/tests/communities/test_services.py +++ b/tests/communities/test_services.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2022 Graz University of Technology. +# Copyright (C) 2022-2024 Graz University of Technology. # Copyright (C) 2022 Northwestern University. # # Invenio-Communities is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ @pytest.fixture() -def comm(community_service, minimal_community, location): +def comm(community_service, minimal_community, location, db): """Create minimal public community.""" c = deepcopy(minimal_community) c["slug"] = "{slug}".format( diff --git a/tests/conftest.py b/tests/conftest.py index fbc5abd85..4a4bfeb45 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,7 @@ # # This file is part of Invenio. # Copyright (C) 2016-2024 CERN. +# Copyright (C) 2024 Graz University of Technology. # # Invenio is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -234,16 +235,16 @@ def users(UserFixture, app, database): users[r] = u # when using `database` fixture (and not `db`), commit the creation of the # user because its implementation uses a nested session instead - database.session.commit() + # database.session.commit() return users -@pytest.fixture(scope="module") -def group(database): +@pytest.fixture(scope="function") +def group(db): """Group.""" r = Role(id="it-dep", name="it-dep") - database.session.add(r) - database.session.commit() + db.session.add(r) + # database.session.commit() return r @@ -263,7 +264,7 @@ def any_user(UserFixture, app, database): u.create(app, database) # when using `database` fixture (and not `db`), commit the creation of the # user because its implementation uses a nested session instead - database.session.commit() + # database.session.commit() u.identity # compute identity return u @@ -308,8 +309,6 @@ def admin_role_need(db): action_role = ActionRoles.create(action=action_admin_access, role=role) db.session.add(action_role) - db.session.commit() - return action_role.need @@ -328,13 +327,11 @@ def superuser_role_need(db): action_role = ActionRoles.create(action=superuser_access, role=role) db.session.add(action_role) - db.session.commit() - return action_role.need -@pytest.fixture(scope="module") -def new_user(UserFixture, app, database): +@pytest.fixture(scope="function") +def new_user(UserFixture, app, db): """A new user.""" u = UserFixture( email="newuser@newuser.org", @@ -354,12 +351,13 @@ def new_user(UserFixture, app, database): active=True, confirmed=True, ) - u.create(app, database) + u.create(app, db) + # when using `database` fixture (and not `db`), commit the creation of the # user because its implementation uses a nested session instead current_users_service.indexer.process_bulk_queue() current_users_service.record_cls.index.refresh() - database.session.commit() + # database.session.commit() return u @@ -397,7 +395,6 @@ def _create_user(data=None): u.create(app, db) current_users_service.indexer.process_bulk_queue() current_users_service.record_cls.index.refresh() - db.session.commit() return u return _create_user @@ -604,7 +601,6 @@ def members(member_service, community, users, db): # reindexing users to make sure the user service is up-to-date current_users_service.indexer.process_bulk_queue() current_users_service.record_cls.index.refresh() - db.session.commit() return users diff --git a/tests/members/test_members_resource.py b/tests/members/test_members_resource.py index 808b2dc36..5bdab4fd0 100644 --- a/tests/members/test_members_resource.py +++ b/tests/members/test_members_resource.py @@ -2,6 +2,7 @@ # # Copyright (C) 2022 Northwestern University. # Copyright (C) 2022-2024 CERN. +# Copyright (C) 2024 Graz University of Technology. # # Invenio-Communities is free software; you can redistribute it and/or modify # it under the terms of the MIT License; see LICENSE file for more details. @@ -60,7 +61,7 @@ def test_add_denied(client, headers, community_id, group_data, new_user): assert r.status_code == 403 -def test_add_bad_data(client, headers, community_id, owner): +def test_add_bad_data(client, headers, community_id, owner, db): """Test add REST API.""" client = owner.login(client) r = client.post( diff --git a/tests/members/test_members_services.py b/tests/members/test_members_services.py index 88d5e4b6f..94236eb3d 100644 --- a/tests/members/test_members_services.py +++ b/tests/members/test_members_services.py @@ -2,7 +2,7 @@ # # Copyright (C) 2022 Northwestern University. # Copyright (C) 2022-2024 CERN. -# Copyright (C) 2022-2023 Graz University of Technology. +# Copyright (C) 2022-2024 Graz University of Technology. # # Invenio-Communities is free software; you can redistribute it and/or modify # it under the terms of the MIT License; see LICENSE file for more details. @@ -111,6 +111,7 @@ def test_add_invalid_member_type(member_service, community, owner, new_user, db) "members": [{"type": "user", "id": str(new_user.id)}], "role": "reader", } + assert pytest.raises( PermissionDeniedError, member_service.add, @@ -618,7 +619,14 @@ def test_invite_actions_permissions( # # Leave community # -@pytest.mark.parametrize("role", ["manager", "curator", "reader"]) +@pytest.mark.parametrize( + "role", + [ + "curator", + "manager", + "reader", + ], +) def test_leave_allowed(member_service, community, members, role): """Managers, curators and readers can leave.""" user = members[role] @@ -626,7 +634,7 @@ def test_leave_allowed(member_service, community, members, role): assert member_service.delete(user.identity, community._record.id, data) -def test_leave_single_owner_denied(member_service, community, owner): +def test_leave_single_owner_denied(member_service, community, owner, db): """A single owner cannot leave""" data = {"members": [{"type": "user", "id": str(owner.id)}]} pytest.raises( @@ -757,7 +765,7 @@ def test_delete_member_type_group(member_service, community, owner, group, db): member_service.delete(owner.identity, community._record.id, data) -def test_delete_invalid_member(member_service, community, owner): +def test_delete_invalid_member(member_service, community, owner, db): """Invalid members and member types raises an error.""" data = {"members": [{"type": "group", "id": "invalid"}]} pytest.raises( @@ -888,7 +896,9 @@ def test_update_public_visibility_of_group_allowed( "members": [{"type": "group", "id": str(group.name)}], "role": "reader", } + member_service.add(system_identity, community._record.id, data) + # Update the member data = { "members": [{"type": "group", "id": str(group.name)}],