Skip to content

Commit

Permalink
Merge branch 'main' into snyk-fix-65401c03eb1696fc5c45bfa28884c92c
Browse files Browse the repository at this point in the history
  • Loading branch information
samonaisi authored Dec 5, 2023
2 parents 08fd09b + 53d09ed commit 09dd7d3
Show file tree
Hide file tree
Showing 81 changed files with 3,030 additions and 2,362 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ coverage.xml
.hypothesis/
.pytest_cache/
cover/
_tests_report_*.txt

# Translations
*.mo
Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,23 @@ cd projects-back
```bash
make
```

### Migrate the database
### Execute into the backend container
*The stack need to be running.*
Get a shell access to the backend container
```bash
make bash
```

### Migrate the database
```bash
python manage.py migrate
```

### Default user
A default superadmin is created in keycloak. To import it in Projects, you need to login at least once in the [swagger](http://localhost:8000/api/schema/swagger-ui) using these credentials:
- username: `[email protected]`
- password: `admin`

### Seed the database
*The stack need to be running.*
```bash
Expand Down
10 changes: 10 additions & 0 deletions apps/accounts/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import APIException


class EmailTypeMissingError(APIException):
status_code = 400
default_detail = _(
"email_type query parameter is missing. Choices are : admin_created, invitation, reset_password"
)
default_code = "keycloak_email_type_missing"
2 changes: 1 addition & 1 deletion apps/accounts/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class PeopleGroupFactory(factory.django.DjangoModelFactory):
description = factory.Faker("text")
email = factory.Faker("email")
name = factory.Faker("name")
publication_status = PeopleGroup.PublicationStatus.ORG
publication_status = PeopleGroup.PublicationStatus.PUBLIC

class Meta:
model = PeopleGroup
Expand Down
13 changes: 12 additions & 1 deletion apps/accounts/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from apps.commons.filters import MultiValueCharFilter

from .models import PeopleGroup, Skill
from .models import PeopleGroup, ProjectUser, Skill


class SkillFilter(filters.FilterSet):
Expand All @@ -22,3 +22,14 @@ def filter_organizations(self, queryset, name, value):
class Meta:
model = PeopleGroup
fields = ["organizations", "type", "is_root"]


class UserFilter(filters.FilterSet):
organizations = MultiValueCharFilter(method="filter_organizations")

def filter_organizations(self, queryset, name, value):
return queryset.filter(groups__organizations__code__in=value).distinct()

class Meta:
model = ProjectUser
fields = ["organizations"]
2 changes: 1 addition & 1 deletion apps/accounts/migrations/0004_auto_20220410_0513.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,5 @@ class Migration(migrations.Migration):
name='permissions',
field=models.ManyToManyField(related_name='users', to='accounts.Permission'),
),
migrations.RunPython(init_groups_and_permissions, migrations.RunPython.noop)
# migrations.RunPython(init_groups_and_permissions, migrations.RunPython.noop)
]
2 changes: 1 addition & 1 deletion apps/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ def get_project_queryset(self, *prefetch) -> QuerySet["Project"]:
if self._project_queryset is None:
self._project_queryset = Project.objects.filter(
publication_status=Project.PublicationStatus.PUBLIC
)
).distinct()
return self._project_queryset.prefetch_related(*prefetch)

def get_user_queryset(self, *prefetch) -> QuerySet["ProjectUser"]:
Expand Down
6 changes: 6 additions & 0 deletions apps/accounts/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,14 @@ def get_related_people_groups(
view.lookup_field
)
if pk is not None:
queryset = PeopleGroup.objects.filter(slug=pk)
if queryset.exists():
return queryset
return PeopleGroup.objects.filter(pk=pk)
if "people_group_id" in view.kwargs:
queryset = PeopleGroup.objects.filter(slug=view.kwargs["people_group_id"])
if queryset.exists():
return queryset
return PeopleGroup.objects.filter(id=view.kwargs["people_group_id"])
return []

Expand Down
11 changes: 7 additions & 4 deletions apps/accounts/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,18 @@ def _clean_user_data_from_csv(user_data):

def _create_user_from_csv_data(request, user_data):
redirect_organization_code = user_data.pop("redirect_organization_code", "")
user_data["keycloak_id"] = KeycloakService.create_user(
user_data, redirect_organization_code
)
user_data["keycloak_id"] = KeycloakService.create_user(user_data)
serializer = UserSerializer(
data=user_data,
context={"request": request},
)
if serializer.is_valid():
serializer.save()
instance = serializer.save()
KeycloakService.send_email(
user=instance,
email_type=KeycloakService.EmailType.ADMIN_CREATED,
redirect_organization_code=redirect_organization_code,
)
return {"email": user_data["email"], "status": "created", "error": ""}
return {
"email": user_data["email"],
Expand Down
159 changes: 81 additions & 78 deletions apps/accounts/tests/views/test_people_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def test_create_people_group(self, role, expected_code):
managers = self.managers
leaders = self.leaders
projects = self.projects
user = self.get_parameterized_test_user(role, organization=organization)
user = self.get_parameterized_test_user(role, instances=[organization])
self.client.force_authenticate(user)
payload = {
"name": faker.name(),
Expand Down Expand Up @@ -103,7 +103,7 @@ def test_update_people_group(self, role, expected_code):
people_group = PeopleGroupFactory(organization=organization)
people_group.description = faker.text()
people_group.save()
user = self.get_parameterized_test_user(role, people_group=people_group)
user = self.get_parameterized_test_user(role, instances=[people_group])
self.client.force_authenticate(user)
payload = {
"description": faker.text(),
Expand Down Expand Up @@ -142,7 +142,7 @@ def setUpTestData(cls):
def test_delete_people_group(self, role, expected_code):
organization = self.organization
people_group = PeopleGroupFactory(organization=organization)
user = self.get_parameterized_test_user(role, people_group=people_group)
user = self.get_parameterized_test_user(role, instances=[people_group])
self.client.force_authenticate(user)
response = self.client.delete(
reverse(
Expand Down Expand Up @@ -183,7 +183,7 @@ def test_add_people_group_member(self, role, expected_code):
managers = self.managers
leaders = self.leaders
people_group = PeopleGroupFactory(organization=organization)
user = self.get_parameterized_test_user(role, people_group=people_group)
user = self.get_parameterized_test_user(role, instances=[people_group])
self.client.force_authenticate(user)
payload = {
"members": [m.keycloak_id for m in members],
Expand Down Expand Up @@ -222,7 +222,7 @@ def test_remove_people_group_member(self, role, expected_code):
managers = self.managers
leaders = self.leaders
people_group = PeopleGroupFactory(organization=organization)
user = self.get_parameterized_test_user(role, people_group=people_group)
user = self.get_parameterized_test_user(role, instances=[people_group])
self.client.force_authenticate(user)
people_group.members.add(*members)
people_group.managers.add(*managers)
Expand Down Expand Up @@ -252,6 +252,24 @@ def setUpTestData(cls):
super().setUpTestData()
cls.organization = OrganizationFactory()
cls.projects = ProjectFactory.create_batch(3, organizations=[cls.organization])
cls.retrieved_people_group_projects = {
"public": ProjectFactory(
publication_status=Project.PublicationStatus.PUBLIC,
organizations=[cls.organization],
),
"private": ProjectFactory(
publication_status=Project.PublicationStatus.PRIVATE,
organizations=[cls.organization],
),
"org": ProjectFactory(
publication_status=Project.PublicationStatus.ORG,
organizations=[cls.organization],
),
}
cls.retrieved_people_group = PeopleGroupFactory(organization=cls.organization)
cls.retrieved_people_group.featured_projects.add(
*list(cls.retrieved_people_group_projects.values())
)

@parameterized.expand(
[
Expand All @@ -270,7 +288,7 @@ def test_add_featured_project(self, role, expected_code):
organization = self.organization
projects = self.projects
people_group = PeopleGroupFactory(organization=organization)
user = self.get_parameterized_test_user(role, people_group=people_group)
user = self.get_parameterized_test_user(role, instances=[people_group])
self.client.force_authenticate(user)
payload = {"featured_projects": [p.pk for p in projects]}
response = self.client.post(
Expand Down Expand Up @@ -303,7 +321,7 @@ def test_remove_featured_project(self, role, expected_code):
organization = self.organization
projects = self.projects
people_group = PeopleGroupFactory(organization=organization)
user = self.get_parameterized_test_user(role, people_group=people_group)
user = self.get_parameterized_test_user(role, instances=[people_group])
self.client.force_authenticate(user)
people_group.featured_projects.add(*projects)
payload = {"featured_projects": [p.pk for p in projects]}
Expand All @@ -321,6 +339,62 @@ def test_remove_featured_project(self, role, expected_code):
for project in projects
)

@parameterized.expand(
[
(TestRoles.ANONYMOUS, ("public",)),
(TestRoles.DEFAULT, ("public",)),
(TestRoles.SUPERADMIN, ("public", "private", "org")),
(TestRoles.ORG_ADMIN, ("public", "private", "org")),
(TestRoles.ORG_FACILITATOR, ("public", "private", "org")),
(TestRoles.ORG_USER, ("public", "org")),
(TestRoles.GROUP_LEADER, ("public",)),
(TestRoles.GROUP_MANAGER, ("public",)),
(TestRoles.GROUP_MEMBER, ("public",)),
]
)
def test_retrieve_featured_projects(self, role, retrieved_projects):
organization = self.organization
people_group = self.retrieved_people_group
projects = self.retrieved_people_group_projects
user = self.get_parameterized_test_user(role, instances=[people_group])
self.client.force_authenticate(user)
response = self.client.get(
reverse(
"PeopleGroup-project",
args=(organization.code, people_group.pk),
),
)
assert response.status_code == status.HTTP_200_OK
content = response.json()["results"]
assert {p["id"] for p in content} == {
projects[p].id for p in retrieved_projects
}

@parameterized.expand(
[
(TestRoles.PROJECT_OWNER, ("public", "private", "org")),
(TestRoles.PROJECT_REVIEWER, ("public", "private", "org")),
(TestRoles.PROJECT_MEMBER, ("public", "private", "org")),
]
)
def test_retrieve_featured_projects_project_roles(self, role, retrieved_projects):
organization = self.organization
people_group = self.retrieved_people_group
projects = self.retrieved_people_group_projects
user = self.get_parameterized_test_user(role, instances=list(projects.values()))
self.client.force_authenticate(user)
response = self.client.get(
reverse(
"PeopleGroup-project",
args=(organization.code, people_group.pk),
),
)
assert response.status_code == status.HTTP_200_OK
content = response.json()["results"]
assert {p["id"] for p in content} == {
projects[p].id for p in retrieved_projects
}


class ValidatePeopleGroupTestCase(JwtAPITestCase):
@classmethod
Expand Down Expand Up @@ -570,77 +644,6 @@ def test_annotate_members(self):
assert all(user["is_manager"] is False for user in batch_4)
assert all(user["is_leader"] is False for user in batch_4)

def test_annotate_projects(self):
people_group = PeopleGroupFactory(
publication_status=PeopleGroup.PublicationStatus.PUBLIC,
organization=self.organization,
)
member = UserFactory()
people_group.members.add(member)

members_public_projects = ProjectFactory.create_batch(
2, organizations=[self.organization]
)
members_private_projects = ProjectFactory.create_batch(
2,
publication_status=Project.PublicationStatus.PRIVATE,
organizations=[self.organization],
)
members_featured_public_projects = ProjectFactory.create_batch(
2, organizations=[self.organization]
)

featured_public_projects = ProjectFactory.create_batch(
2, organizations=[self.organization]
)
featured_private_projects = ProjectFactory.create_batch(
2,
publication_status=Project.PublicationStatus.PRIVATE,
organizations=[self.organization],
)

member_projects = (
members_public_projects
+ members_private_projects
+ members_featured_public_projects
)
featured_projects = (
featured_public_projects
+ featured_private_projects
+ members_featured_public_projects
)

for project in member_projects:
project.members.add(member)

people_group.featured_projects.add(*(featured_projects))

response = self.client.get(
reverse(
"PeopleGroup-project",
args=(people_group.organization.code, people_group.pk),
)
)
self.assertEqual(response.status_code, 200)
content = response.json()

results = content["results"]
assert len(results) == 6

nine_first = results[:4]
featured_projects_ids = [project.id for project in featured_projects]
nine_first_ids = [project["id"] for project in nine_first]
assert featured_projects_ids.sort() == nine_first_ids.sort()
nine_first_projects = [project["is_featured"] is True for project in nine_first]
assert all(nine_first_projects) is True

nine_last = results[-2:]
member_projects_ids = [project.id for project in member_projects]
nine_last_ids = [project["id"] for project in nine_last]
assert member_projects_ids.sort() == nine_last_ids.sort()
nine_last_projects = [project["is_featured"] is True for project in nine_last]
assert all(nine_last_projects) is False

def test_root_group_creation(self):
organization = OrganizationFactory()
root_people_group = PeopleGroup.objects.filter(
Expand Down
6 changes: 3 additions & 3 deletions apps/accounts/tests/views/test_people_group_header.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def setUpTestData(cls):
def test_create_people_group_header(self, role, expected_code):
organization = self.organization
people_group = PeopleGroupFactory(organization=organization)
user = self.get_parameterized_test_user(role, people_group=people_group)
user = self.get_parameterized_test_user(role, instances=[people_group])
self.client.force_authenticate(user)
payload = {"file": self.get_test_image_file()}
response = self.client.post(
Expand Down Expand Up @@ -71,7 +71,7 @@ def test_update_people_group_header(self, role, expected_code):
people_group.header_image = self.get_test_image()
people_group.save()
user = self.get_parameterized_test_user(
role, owned_instance=people_group.header_image, people_group=people_group
role, owned_instance=people_group.header_image, instances=[people_group]
)
self.client.force_authenticate(user)
payload = {
Expand Down Expand Up @@ -124,7 +124,7 @@ def test_delete_people_group_header(self, role, expected_code):
people_group.header_image = self.get_test_image()
people_group.save()
user = self.get_parameterized_test_user(
role, owned_instance=people_group.header_image, people_group=people_group
role, owned_instance=people_group.header_image, instances=[people_group]
)
self.client.force_authenticate(user)
response = self.client.delete(
Expand Down
Loading

0 comments on commit 09dd7d3

Please sign in to comment.