Skip to content

Commit

Permalink
Merge main into stable/4.0.x. Update 20231101
Browse files Browse the repository at this point in the history
* commit 'daba71c3e49e414f9455a542104a0e181b22d6d9':
  Fix speaker.delete and speaker.update for deleted users (#1935)
  Fix opentelemetry in backend (#1915)
  Allow strikthrough short tag (#1932)
  Introduce speaker deletion logic to user.update and user.delete (#1921)
  • Loading branch information
m-schieder committed Nov 1, 2023
2 parents dc894be + daba71c commit 377f01c
Show file tree
Hide file tree
Showing 16 changed files with 360 additions and 45 deletions.
2 changes: 0 additions & 2 deletions global/meta/models.yml
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,6 @@ meeting_user:
speaker_ids:
type: relation-list
to: speaker/meeting_user_id
on_delete: CASCADE
restriction_mode: A
supported_motion_ids:
type: relation-list
Expand Down Expand Up @@ -2118,7 +2117,6 @@ speaker:
meeting_user_id:
type: relation
to: meeting_user/speaker_ids
required: true
equal_fields: meeting_id
restriction_mode: A
point_of_order_category_id:
Expand Down
8 changes: 8 additions & 0 deletions openslides_backend/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import os

from .shared.env import Environment
from .shared.otel import init as otel_init
from .shared.otel import instrument_requests as otel_instrument_requests

otel_init(Environment(os.environ), "backend")
otel_instrument_requests()
13 changes: 7 additions & 6 deletions openslides_backend/action/actions/speaker/delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ def check_permissions(self, instance: Dict[str, Any]) -> None:
["meeting_user_id"],
lock_result=False,
)
meeting_user = self.datastore.get(
fqid_from_collection_and_id("meeting_user", speaker["meeting_user_id"]),
["user_id"],
)
if speaker.get("meeting_user_id"):
meeting_user = self.datastore.get(
fqid_from_collection_and_id("meeting_user", speaker["meeting_user_id"]),
["user_id"],
)

if meeting_user.get("user_id") == self.user_id:
return
if meeting_user.get("user_id") == self.user_id:
return
super().check_permissions(instance)
39 changes: 20 additions & 19 deletions openslides_backend/action/actions/speaker/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,25 @@ def check_permissions(self, instance: Dict[str, Any]) -> None:
["meeting_user_id", "meeting_id"],
lock_result=False,
)
meeting_user = self.datastore.get(
fqid_from_collection_and_id("meeting_user", speaker["meeting_user_id"]),
["user_id"],
lock_result=False,
)
if meeting_user.get("user_id") == self.user_id and (
has_perm(
self.datastore,
self.user_id,
Permissions.ListOfSpeakers.CAN_SEE,
speaker["meeting_id"],
)
or has_perm(
self.datastore,
self.user_id,
Permissions.ListOfSpeakers.CAN_BE_SPEAKER,
speaker["meeting_id"],
if speaker.get("meeting_user_id"):
meeting_user = self.datastore.get(
fqid_from_collection_and_id("meeting_user", speaker["meeting_user_id"]),
["user_id"],
lock_result=False,
)
):
return
if meeting_user.get("user_id") == self.user_id and (
has_perm(
self.datastore,
self.user_id,
Permissions.ListOfSpeakers.CAN_SEE,
speaker["meeting_id"],
)
or has_perm(
self.datastore,
self.user_id,
Permissions.ListOfSpeakers.CAN_BE_SPEAKER,
speaker["meeting_id"],
)
):
return
super().check_permissions(instance)
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from typing import Any, Dict, Optional

from openslides_backend.shared.filters import And, FilterOperator

from ....services.datastore.commands import GetManyRequest
from ...action import Action
from ..speaker.delete import SpeakerDeleteAction


class ConditionalSpeakerCascadeMixin(Action):
"""
Mixin for user actions that deletes unstarted speeches of users that were either deleted, or removed from a meeting
"""

def update_instance(self, instance: Dict[str, Any]) -> Dict[str, Any]:
removed_meeting_id = self.get_removed_meeting_id(instance)
if removed_meeting_id is not None:
filter_: Any = FilterOperator("user_id", "=", instance["id"])
if removed_meeting_id:
filter_ = And(
filter_, FilterOperator("meeting_id", "=", removed_meeting_id)
)
meeting_users = self.datastore.filter(
"meeting_user", filter_, ["speaker_ids"]
)
speaker_ids = [
speaker_id
for val in meeting_users.values()
if val.get("speaker_ids")
for speaker_id in val.get("speaker_ids", [])
]
speakers = self.datastore.get_many(
[
GetManyRequest(
"speaker",
speaker_ids,
[
"begin_time",
"id",
],
)
]
)
speakers_to_delete = [
speaker
for speaker in speakers.get("speaker", {}).values()
if speaker.get("begin_time") is None
]

if len(speakers_to_delete):
self.execute_other_action(
SpeakerDeleteAction,
[{"id": speaker["id"]} for speaker in speakers_to_delete],
)

return super().update_instance(instance)

def get_removed_meeting_id(self, instance: Dict[str, Any]) -> Optional[int]:
"""
Get the id of the meetings from which the user is removed.
If the user is removed from all meetings, the return value will be 0.
If the user is removed from no meetings, it will be None.
"""
raise NotImplementedError()
10 changes: 7 additions & 3 deletions openslides_backend/action/actions/user/delete.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
from typing import Any, Dict
from typing import Any, Dict, Optional

from ....models.models import User
from ....shared.exceptions import ActionException
from ....shared.mixins.user_scope_mixin import UserScopeMixin
from ...generics.delete import DeleteAction
from ...util.default_schema import DefaultSchema
from ...util.register import register_action
from .conditional_speaker_cascade_mixin import ConditionalSpeakerCascadeMixin


@register_action("user.delete")
class UserDelete(UserScopeMixin, DeleteAction):
class UserDelete(UserScopeMixin, ConditionalSpeakerCascadeMixin, DeleteAction):
"""
Action to delete a user.
"""
Expand All @@ -22,7 +23,10 @@ class UserDelete(UserScopeMixin, DeleteAction):
def update_instance(self, instance: Dict[str, Any]) -> Dict[str, Any]:
if instance["id"] == self.user_id:
raise ActionException("You cannot delete yourself.")
return instance
return super().update_instance(instance)

def check_permissions(self, instance: Dict[str, Any]) -> None:
self.check_permissions_for_scope(instance["id"])

def get_removed_meeting_id(self, instance: Dict[str, Any]) -> Optional[int]:
return 0
9 changes: 8 additions & 1 deletion openslides_backend/action/actions/user/update.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict
from typing import Any, Dict, Optional

from ....models.models import User
from ....permissions.management_levels import OrganizationManagementLevel
Expand All @@ -9,6 +9,7 @@
from ...mixins.send_email_mixin import EmailCheckMixin
from ...util.default_schema import DefaultSchema
from ...util.register import register_action
from .conditional_speaker_cascade_mixin import ConditionalSpeakerCascadeMixin
from .create_update_permissions_mixin import CreateUpdatePermissionsMixin
from .user_mixin import (
LimitOfUserMixin,
Expand All @@ -25,6 +26,7 @@ class UserUpdate(
UpdateAction,
LimitOfUserMixin,
UpdateHistoryMixin,
ConditionalSpeakerCascadeMixin,
):
"""
Action to update a user.
Expand Down Expand Up @@ -103,3 +105,8 @@ def update_instance(self, instance: Dict[str, Any]) -> Dict[str, Any]:

check_gender_helper(self.datastore, instance)
return instance

def get_removed_meeting_id(self, instance: Dict[str, Any]) -> Optional[int]:
if instance.get("group_ids") == []:
return instance.get("meeting_id")
return None
4 changes: 0 additions & 4 deletions openslides_backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
from .shared.env import Environment
from .shared.interfaces.logging import LoggingModule
from .shared.interfaces.wsgi import WSGIApplication
from .shared.otel import init as otel_init
from .shared.otel import instrument_requests as otel_instrument_requests

register_services()

Expand Down Expand Up @@ -79,8 +77,6 @@ def load(self) -> WSGIApplication:
# TODO: Fix this typing problem.
logging_module: LoggingModule = logging # type: ignore

otel_instrument_requests()
otel_init(self.env, "backend")
return create_wsgi_application(logging_module, self.view_name, self.env)


Expand Down
8 changes: 3 additions & 5 deletions openslides_backend/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .base import Model
from .mixins import AgendaItemModelMixin, MeetingModelMixin, PollModelMixin

MODELS_YML_CHECKSUM = "566944b1e2fa1b4b216537f3acbb3e5f"
MODELS_YML_CHECKSUM = "34d6dfe51212a0566f81e5c2f67675d6"


class Organization(Model):
Expand Down Expand Up @@ -157,9 +157,7 @@ class MeetingUser(Model):
personal_note_ids = fields.RelationListField(
to={"personal_note": "meeting_user_id"}, on_delete=fields.OnDelete.CASCADE
)
speaker_ids = fields.RelationListField(
to={"speaker": "meeting_user_id"}, on_delete=fields.OnDelete.CASCADE
)
speaker_ids = fields.RelationListField(to={"speaker": "meeting_user_id"})
supported_motion_ids = fields.RelationListField(
to={"motion": "supporter_meeting_user_ids"}
)
Expand Down Expand Up @@ -1051,7 +1049,7 @@ class Speaker(Model):
to={"list_of_speakers": "speaker_ids"}, required=True, equal_fields="meeting_id"
)
meeting_user_id = fields.RelationField(
to={"meeting_user": "speaker_ids"}, required=True, equal_fields="meeting_id"
to={"meeting_user": "speaker_ids"}, equal_fields="meeting_id"
)
point_of_order_category_id = fields.RelationField(
to={"point_of_order_category": "speaker_ids"}, equal_fields="meeting_id"
Expand Down
10 changes: 9 additions & 1 deletion openslides_backend/shared/otel.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@

from .interfaces.env import Env

otel_initialized = False


def init(env: Env, service_name: str) -> None:
"""
Initializes the opentelemetry components and connection to the otel collector.
"""
if not env.is_otel_enabled():
return

span_exporter = OTLPSpanExporter(
endpoint="http://collector:4317",
insecure=True
Expand All @@ -31,6 +32,8 @@ def init(env: Env, service_name: str) -> None:
trace.set_tracer_provider(tracer_provider)
span_processor = BatchSpanProcessor(span_exporter)
tracer_provider.add_span_processor(span_processor)
global otel_initialized
otel_initialized = True


def instrument_requests() -> None:
Expand All @@ -55,6 +58,11 @@ def make_span(env: Env, name: str, attributes: Optional[Dict[str, str]] = None)
if not env.is_otel_enabled():
return nullcontext()

# global otel_initialized
# assert (
# otel_initialized
# ), "backend:Opentelemetry span to be set before having set a TRACER_PROVIDER"

tracer = trace.get_tracer_provider().get_tracer(__name__)
span = tracer.start_as_current_span(name, attributes=attributes)

Expand Down
1 change: 1 addition & 0 deletions openslides_backend/shared/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"blockquote",
# text formating
"strike",
"s",
"del",
"ins",
"strong",
Expand Down
2 changes: 1 addition & 1 deletion requirements/export_datastore_commit.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/bin/bash
export DATASTORE_COMMIT_HASH=0720dd02b04b5d77a6269b5cd8611b1eab154e9f
export DATASTORE_COMMIT_HASH=3d217923ab5ed5a4a4bae8f334f6e8404ab884be
48 changes: 48 additions & 0 deletions tests/system/action/speaker/test_delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,51 @@ def test_delete_correct_on_closed_los(self) -> None:
response = self.request("speaker.delete", {"id": 890})
self.assert_status_code(response, 200)
self.assert_model_deleted("speaker/890")

def test_delete_with_removed_user(self) -> None:
self.set_models(
{
"meeting/111": {
"speaker_ids": [890],
"is_active_in_organization_id": 1,
},
"user/7": {
"username": "test_username1",
"meeting_user_ids": [7],
},
"meeting_user/7": {
"meeting_id": 111,
"user_id": 7,
"speaker_ids": [890],
"group_ids": [],
},
"list_of_speakers/23": {"speaker_ids": [890], "meeting_id": 111},
"speaker/890": {
"meeting_user_id": 7,
"list_of_speakers_id": 23,
"meeting_id": 111,
},
}
)
response = self.request("speaker.delete", {"id": 890})
self.assert_status_code(response, 200)
self.assert_model_deleted("speaker/890")
self.assert_model_exists("meeting_user/7", {"speaker_ids": []})

def test_delete_with_deleted_user(self) -> None:
self.set_models(
{
"meeting/111": {
"speaker_ids": [890],
"is_active_in_organization_id": 1,
},
"list_of_speakers/23": {"speaker_ids": [890], "meeting_id": 111},
"speaker/890": {
"list_of_speakers_id": 23,
"meeting_id": 111,
},
}
)
response = self.request("speaker.delete", {"id": 890})
self.assert_status_code(response, 200)
self.assert_model_deleted("speaker/890")
Loading

0 comments on commit 377f01c

Please sign in to comment.