Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
855a17e
Introduced infra for the mod. events pipeline
vladsha-dev Oct 21, 2024
d18c3cd
Updated clear_opensearch.sh.tpl file
vladsha-dev Oct 21, 2024
5d7461a
Add moderation-event GET endpoint
VadimKovalenkoSNF Oct 25, 2024
b74225e
Change moderation_status to status
VadimKovalenkoSNF Oct 25, 2024
ab7e818
Create index mapping and SQL script for the moderation events pipeline
vladsha-dev Oct 25, 2024
3dd3929
Create index mapping and SQL script for the moderation events pipeline
vladsha-dev Oct 25, 2024
af8c7ad
Create empty contributor_name Ruby filter
vladsha-dev Oct 28, 2024
2cdb941
Create empty contributor_name Ruby filter
vladsha-dev Oct 28, 2024
2b66ba6
Merge branch 'main' into OSDEV-1335-deploy-new-moderation-events-logs…
vladsha-dev Oct 28, 2024
ca574c5
Connect moderation-event logstash pipeline
VadimKovalenkoSNF Oct 28, 2024
d7ed97a
WIP
roman-stolar Oct 28, 2024
2bfd293
WIP
roman-stolar Oct 28, 2024
f10edfd
WIP
roman-stolar Oct 28, 2024
43d2105
Create Ruby filters for particular fields, update mapping
vladsha-dev Oct 29, 2024
b867ed8
Delete mock generator
vladsha-dev Oct 29, 2024
6ee98cf
Add moderation-event GET endpoint
VadimKovalenkoSNF Oct 25, 2024
e91fd51
Change moderation_status to status
VadimKovalenkoSNF Oct 25, 2024
78c0b55
Create index mapping and SQL script for the moderation events pipeline
vladsha-dev Oct 25, 2024
3c386a2
Connect moderation-event logstash pipeline
VadimKovalenkoSNF Oct 28, 2024
0105c91
update error handling and provide 202 response data
roman-stolar Oct 29, 2024
378fa7e
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Oct 29, 2024
2491919
fix
roman-stolar Oct 29, 2024
048db32
fix linter errors
roman-stolar Oct 30, 2024
b281e82
fix
roman-stolar Oct 30, 2024
c781d48
fix
roman-stolar Oct 30, 2024
e4db5c2
add custom fields to serializer
roman-stolar Oct 30, 2024
d370a2c
fix status code
roman-stolar Oct 30, 2024
8b48814
allow null in response for few fields
roman-stolar Oct 30, 2024
34ba7ce
update errors response format
roman-stolar Oct 30, 2024
8a67860
Add moderation-events fixture
VadimKovalenkoSNF Oct 30, 2024
89ec4d2
Merge branch 'main' into OSDEV-1346-implement-get-moderation-events
VadimKovalenkoSNF Oct 30, 2024
868118f
Remove redundant code
VadimKovalenkoSNF Oct 30, 2024
ff68d9b
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Oct 30, 2024
03a4970
Temp commit message
VadimKovalenkoSNF Oct 30, 2024
22b3caa
Wrote initial test
roman-stolar Oct 30, 2024
7507867
covered different test cases for endpoint
roman-stolar Oct 30, 2024
f07ae4f
fix linter
roman-stolar Oct 30, 2024
359758c
fix
roman-stolar Oct 30, 2024
33f54c7
update error message
roman-stolar Oct 31, 2024
e5fd105
removed redundant comments
roman-stolar Oct 31, 2024
b8bd00f
prepare method as private
roman-stolar Oct 31, 2024
473fafb
Updated RELEASE-NOTES
roman-stolar Oct 31, 2024
1d71cd9
fix linter
roman-stolar Oct 31, 2024
9472bc4
fix linter error
roman-stolar Oct 31, 2024
9837cc8
Add boilerplate for moderation events and production locations query …
VadimKovalenkoSNF Nov 1, 2024
7cccd84
Make query builder methods as protected
VadimKovalenkoSNF Nov 1, 2024
e329437
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 1, 2024
be6e624
Refactor modration-event and producion-locations query builder
VadimKovalenkoSNF Nov 1, 2024
b8a7b00
addressed coderabbit comments
roman-stolar Nov 1, 2024
950dae6
one more fix
roman-stolar Nov 1, 2024
cd771cf
remove logger
roman-stolar Nov 1, 2024
89f0090
Merge branch 'main' into OSDEV-1346-implement-get-moderation-events
VadimKovalenkoSNF Nov 1, 2024
0493dff
fix
roman-stolar Nov 1, 2024
f57c5b8
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 1, 2024
3b87e91
addressed Vlad comment
roman-stolar Nov 1, 2024
5fce52c
update error message
roman-stolar Nov 1, 2024
e95cc6b
addressed Olek comment
roman-stolar Nov 4, 2024
648aa82
add interim changes
VadimKovalenkoSNF Nov 2, 2024
16533fb
Add sorting
VadimKovalenkoSNF Nov 4, 2024
bbf9c90
updated status choices
roman-stolar Nov 4, 2024
df87af2
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 4, 2024
f10eb81
Add pagination using from property
VadimKovalenkoSNF Nov 5, 2024
7f393c0
Update moderation status
VadimKovalenkoSNF Nov 5, 2024
2942919
fix tests
roman-stolar Nov 5, 2024
d5b21c1
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 5, 2024
5ae88c1
fix tests
roman-stolar Nov 5, 2024
0f9f500
More refactoring of moderation status
VadimKovalenkoSNF Nov 5, 2024
fca9271
Update fixture for moderation events
VadimKovalenkoSNF Nov 5, 2024
0003c80
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 5, 2024
e809df4
Inherit _add_terms method to production locations and moderation-events
VadimKovalenkoSNF Nov 5, 2024
1e797e7
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 6, 2024
31aceaf
linter fix
roman-stolar Nov 6, 2024
8995a5b
Refactor validators, update index mapping
VadimKovalenkoSNF Nov 6, 2024
bf76754
Merge branch 'main' into OSDEV-1346-implement-get-moderation-events
VadimKovalenkoSNF Nov 6, 2024
00c7dd6
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 6, 2024
b630f47
Add sorting for cleaned name, address and country
VadimKovalenkoSNF Nov 6, 2024
5bb2ab7
Minor fix
VadimKovalenkoSNF Nov 6, 2024
5f4ea0d
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 6, 2024
aeda7aa
Merge branch 'main' into OSDEV-1346-implement-get-moderation-events
VadimKovalenkoSNF Nov 7, 2024
bfe2759
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 7, 2024
00ac5fa
fix import path
roman-stolar Nov 7, 2024
28f5518
fix import path
roman-stolar Nov 7, 2024
cf7f528
Minor lint fixes
VadimKovalenkoSNF Nov 8, 2024
94e6bf4
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 8, 2024
c45c39a
fix linter
roman-stolar Nov 8, 2024
f8e52b8
Remove serializers for os_id and contributor_id
VadimKovalenkoSNF Nov 8, 2024
0a26b2e
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 11, 2024
ae4f0f6
Add integration tests (partial impl)
VadimKovalenkoSNF Nov 11, 2024
0b60f49
Add base integration tests
VadimKovalenkoSNF Nov 11, 2024
2034e0c
Add integration tests and auth check
VadimKovalenkoSNF Nov 12, 2024
d1d63d9
Merge branch 'main' into OSDEV-1346-implement-get-moderation-events
VadimKovalenkoSNF Nov 12, 2024
3e6a83f
Update release notes
VadimKovalenkoSNF Nov 12, 2024
69301d0
Create new migration for moderation event table
VadimKovalenkoSNF Nov 12, 2024
b93efc6
Update release notes
VadimKovalenkoSNF Nov 12, 2024
227aa5f
Fix minor reivew issues
VadimKovalenkoSNF Nov 12, 2024
0c63b0f
Minor lint fix
VadimKovalenkoSNF Nov 12, 2024
31f9979
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 12, 2024
66ff008
Update types
VadimKovalenkoSNF Nov 12, 2024
40a3023
Minor lint fixes
VadimKovalenkoSNF Nov 12, 2024
86bc01c
Integration test fix
VadimKovalenkoSNF Nov 12, 2024
4cbd2c2
Minor fix of integration test
VadimKovalenkoSNF Nov 12, 2024
11129de
Remove TODOs and link them to OSDEV-1441
VadimKovalenkoSNF Nov 12, 2024
150e988
Revert recent changes
VadimKovalenkoSNF Nov 12, 2024
e42a443
Minor fixes to check pipeline
VadimKovalenkoSNF Nov 12, 2024
86c01ca
Rename test for opensearch query builder
VadimKovalenkoSNF Nov 12, 2024
bf896a6
Fix django import
VadimKovalenkoSNF Nov 12, 2024
ffdeeca
Remove django model import from integrational test
VadimKovalenkoSNF Nov 12, 2024
69c9474
Merge branch 'main' into OSDEV-1346-implement-get-moderation-events
VadimKovalenkoSNF Nov 12, 2024
a5d6963
Rename TestOpenSearchQueryBuilder to TestProductionLocationsQueryBuilder
VadimKovalenkoSNF Nov 12, 2024
b46ae8a
Add unit tests for moderation-events query builder
VadimKovalenkoSNF Nov 13, 2024
ca99fda
Refactor track_total_hits property
VadimKovalenkoSNF Nov 13, 2024
2f92ac3
Update moderation_id validation
VadimKovalenkoSNF Nov 13, 2024
0aeaab3
Fix lint issues
VadimKovalenkoSNF Nov 13, 2024
ad2e941
Update error msg for moderation ids validation
VadimKovalenkoSNF Nov 13, 2024
5e58354
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 13, 2024
1f057e2
Minor fix for integration test
VadimKovalenkoSNF Nov 13, 2024
aa8203b
Minor improve for integration test
VadimKovalenkoSNF Nov 13, 2024
254e1bc
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 13, 2024
d48b8b6
Fix version in release notes
VadimKovalenkoSNF Nov 14, 2024
17ff064
Refactor _build_date_range method
VadimKovalenkoSNF Nov 14, 2024
87da165
Make __build_number_of_workers as private in ProductionLocationsQuery…
VadimKovalenkoSNF Nov 14, 2024
9adb9c9
Refactor OpenSearch query builder inheritance
VadimKovalenkoSNF Nov 14, 2024
c22e1fb
Inherit OpenSearchValidationInterface fron ABC
VadimKovalenkoSNF Nov 14, 2024
f3347cf
Merge branch 'OSDEV-1346-implement-get-moderation-events' into OSDEV-…
roman-stolar Nov 14, 2024
d6a577d
move the task to release 1.25.0
roman-stolar Nov 14, 2024
5660a23
Merge branch 'main' into OSDEV-1332-implement-patch-moderation-event
roman-stolar Nov 14, 2024
0b64e17
refactor
roman-stolar Nov 14, 2024
1b32dd7
fix linter
roman-stolar Nov 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/release/RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html

### Code/API changes
* [OSDEV-1346](https://opensupplyhub.atlassian.net/browse/OSDEV-1346) - Create GET request for `v1/moderation-events` endpoint.
* [OSDEV-1332](https://opensupplyhub.atlassian.net/browse/OSDEV-1332) - Introduced new `PATCH api/v1/moderation-events/{moderation_id}` endpoint
to modify moderation event `status`.

### Architecture/Environment changes
* *Describe architecture/environment changes here.*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from rest_framework.serializers import (
ModelSerializer,
ValidationError,
CharField,
IntegerField
)
from api.models.moderation_event \
import ModerationEvent
from django.utils.timezone import now


class ModerationEventUpdateSerializer(ModelSerializer):

contributor_id = IntegerField(source='contributor.id', read_only=True)
contributor_name = CharField(source='contributor.name', read_only=True)
os_id = IntegerField(source='os.id', read_only=True, allow_null=True)
claim_id = IntegerField(source='claim.id', read_only=True, allow_null=True)

class Meta:
model = ModerationEvent
fields = [
'uuid',
'created_at',
'updated_at',
'os_id',
'contributor_id',
'contributor_name',
'cleaned_data',
'request_type',
'source',
'status',
'status_change_date',
'claim_id'
]

def to_internal_value(self, data):
status = data.get('status')

if status is None:
raise ValidationError({
"field": "status",
"message": "This field is required."
})

self.__validate_status(status)
return super().to_internal_value(data)

def __validate_status(self, value):
if value not in [
ModerationEvent.Status.PENDING,
ModerationEvent.Status.APPROVED,
ModerationEvent.Status.REJECTED
]:
raise ValidationError({
"field": "status",
"message": (
"Moderation status must be one of "
"PENDING, APPROVED or REJECTED."
)
})
return value

def update(self, instance, validated_data):
if 'status' in validated_data:
value = validated_data['status']
instance.status = value
instance.status_change_date = now()

instance.save()
return instance
118 changes: 118 additions & 0 deletions src/django/api/tests/test_moderation_events_update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import json
from django.test import override_settings
from api.models import (
ModerationEvent,
User,
Contributor
)
from django.utils.timezone import now
from rest_framework.test import APITestCase


@override_settings(DEBUG=True)
class ModerationEventsUpdateTest(APITestCase):
def setUp(self):
super().setUp()

self.email = "test@example.com"
self.password = "example123"
self.user = User.objects.create(email=self.email)
self.user.set_password(self.password)
self.user.save()

self.contributor = Contributor.objects.create(
admin=self.user,
name="test contributor",
contrib_type=Contributor.OTHER_CONTRIB_TYPE,
)

self.superemail = "admin@example.com"
self.superpassword = "example123"
self.superuser = User.objects.create_superuser(
email=self.superemail,
password=self.superpassword
)

self.moderation_event = ModerationEvent.objects.create(
uuid='f65ec710-f7b9-4f50-b960-135a7ab24ee6',
created_at=now(),
updated_at=now(),
request_type='UPDATE',
raw_data={"name": "raw_name", "country_code": "UK"},
cleaned_data={"name": "cleaned_name", "country_code": "UK"},
geocode_result={"latitude": -53, "longitude": 142},
status='PENDING',
source='API',
contributor=self.contributor
)

def test_moderation_event_permission(self):
self.client.login(
email=self.email,
password=self.password
)
response = self.client.patch(
"/api/v1/moderation-events/{}/"
.format("f65ec710-f7b9-4f50-b960-135a7ab24ee6"),
data=json.dumps({"status": "APPROVED"}),
content_type="application/json"
)
self.assertEqual(403, response.status_code)

self.client.login(
email=self.superemail,
password=self.superpassword
)
response = self.client.patch(
"/api/v1/moderation-events/{}/"
.format("f65ec710-f7b9-4f50-b960-135a7ab24ee6"),
data=json.dumps({"status": "APPROVED"}),
content_type="application/json"
)
print(response.json())
self.assertEqual(200, response.status_code)

def test_moderation_event_not_found(self):
self.client.login(
email=self.superemail,
password=self.superpassword
)
response = self.client.patch(
"/api/v1/moderation-events/{}/"
.format("f65ec710-f7b9-4f50-b960-135a7ab24ee1"),
data=json.dumps({"status": "APPROVED"}),
content_type="application/json"
)

self.assertEqual(404, response.status_code)

def test_moderation_event_invalid_status(self):
self.client.login(
email=self.superemail,
password=self.superpassword
)
response = self.client.patch(
"/api/v1/moderation-events/{}/"
.format("f65ec710-f7b9-4f50-b960-135a7ab24ee6"),
data=json.dumps({"status": "NEW"}),
content_type="application/json"
)

self.assertEqual(400, response.status_code)

def test_moderation_event_status_changed(self):
self.client.login(
email=self.superemail,
password=self.superpassword
)
response = self.client.patch(
"/api/v1/moderation-events/{}/"
.format("f65ec710-f7b9-4f50-b960-135a7ab24ee6"),
data=json.dumps({"status": "APPROVED"}),
content_type="application/json"
)

self.assertEqual(200, response.status_code)
self.moderation_event.refresh_from_db()
self.assertEqual(self.moderation_event.status, "APPROVED")
self.assertIsNotNone(self.moderation_event.status_change_date)
52 changes: 51 additions & 1 deletion src/django/api/views/v1/moderation_events.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from rest_framework import status
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from rest_framework.exceptions import PermissionDenied
from api.views.v1.utils import (
serialize_params,
handle_errors_decorator
handle_errors_decorator,
handle_path_error
)
from api.permissions import IsRegisteredAndConfirmed
from api.services.opensearch.search import OpenSearchService
Expand All @@ -13,7 +15,13 @@
import OpenSearchQueryDirector
from api.serializers.v1.moderation_events_serializer \
import ModerationEventsSerializer
from api.serializers.v1.opensearch_common_validators.moderation_id_validator \
import ModerationIdValidator
from api.serializers.v1.moderation_event_update_serializer \
import ModerationEventUpdateSerializer
from api.views.v1.index_names import OpenSearchIndexNames
from api.models.moderation_event \
import ModerationEvent


class ModerationEvents(ViewSet):
Expand Down Expand Up @@ -49,3 +57,45 @@ def list(self, request):
query_body
)
return Response(response)

@handle_errors_decorator
def patch(self, request, moderation_id):
if not (request.user.is_superuser or request.user.is_staff):
raise PermissionDenied(
detail="Only the Moderator can perform this action."
)

if not ModerationIdValidator.is_valid_uuid(moderation_id):
return handle_path_error(
field="moderation_id",
message="Invalid UUID format.",
status_code=status.HTTP_400_BAD_REQUEST
)

try:
event = ModerationEvent.objects.get(uuid=moderation_id)
except ModerationEvent.DoesNotExist:
return handle_path_error(
field="moderation_id",
message="Moderation event not found.",
status_code=status.HTTP_404_NOT_FOUND
)

serializer = ModerationEventUpdateSerializer(
event,
data=request.data,
partial=True
)

if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)

return Response(
{
"message": 'The request body contains '
'invalid or missing fields.',
"error": [serializer.errors]
},
status=status.HTTP_400_BAD_REQUEST
)
15 changes: 15 additions & 0 deletions src/django/api/views/v1/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,18 @@ def _wrapped_view(self, request, *args, **kwargs):
except OpenSearchServiceException as e:
return handle_opensearch_exception(e)
return _wrapped_view


def handle_path_error(field, message, status_code):
return Response(
{
"message": "The request path parameter is invalid.",
"errors": [
{
"field": field,
"message": message
}
]
},
status=status_code
)
5 changes: 5 additions & 0 deletions src/django/oar/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@
ProductionLocations.as_view({'get': 'retrieve'}),
name='production-locations-details'
),
path(
'api/v1/moderation-events/<str:moderation_id>/',
ModerationEvents.as_view({'patch': 'patch'}),
name='moderation-event-update'
),
]

schema_view = get_schema_view(
Expand Down