Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

Commit

Permalink
Merge pull request #225 from communitiesuk/FS-4063-assessors-api
Browse files Browse the repository at this point in the history
Added API endpoint to retrieve users for fund
  • Loading branch information
ksp37-dluhc authored Jun 27, 2024
2 parents d388cff + 2345742 commit c4a7a05
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 0 deletions.
31 changes: 31 additions & 0 deletions core/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from sqlalchemy import delete
from sqlalchemy import or_
from sqlalchemy import select
from sqlalchemy.orm import selectinload

from db import db
from db.models.account import Account
Expand Down Expand Up @@ -207,3 +208,33 @@ def post_account() -> Tuple[dict, int]:
"An account with that email or azure_ad_subject_id already exists",
409,
)


def get_accounts_for_fund(fund_short_name):
include_assessors = True if request.args.get("include_assessors", "true").lower() == "true" else False
include_commenters = True if request.args.get("include_commenters", "true").lower() == "true" else False
round_short_name = request.args.get("round_short_name")
if not include_assessors and not include_commenters:
return {"error": "One of include_assessors or include_commenters must be true"}, 400
query = (
db.session.query(Account)
.join(Role) # Explicitly join the tables
.filter(Role.role.like(f"%{fund_short_name}%")) # Filter based on the Role attribute
.options(selectinload(Account.roles))
)
if round_short_name:
query = query.filter(Role.role.like(f"%{round_short_name}%"))

if include_commenters and not include_assessors:
query = query.filter(Role.role.like("%COMMENTER%"))
elif include_assessors and not include_commenters:
query = query.filter(Role.role.like("%ASSESSOR%"))
else:
query = query.filter(or_(Role.role.like("%ASSESSOR%"), Role.role.like("%COMMENTER%")))

results = query.all()
if not results:
return {"error": "No matching accounts found"}, 404

account_schema = AccountSchema()
return account_schema.dump(results, many=True), 200
44 changes: 44 additions & 0 deletions openapi/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,50 @@ paths:
schema:
type: string
format: path
/accounts/fund/{fund_short_name}:
get:
tags:
- accounts
summary: Return the users assigned roles related to the fund
description: "Given a fund, return the users assigned with a role. Filterable by assessors, commenters and round"
operationId: core.account.get_accounts_for_fund
parameters:
- name: fund_short_name
in: path
schema:
type: string
required: true
- name: include_assessors
in: query
description: "Results will include assessors"
required: false
schema:
type: string
default: "true"
- name: include_commenters
in: query
description: "Results will include commenters"
required: false
schema:
type: string
default: "true"
- name: round_short_name
in: query
description: "Results will include only roles for that round"
required: false
schema:
type: string
example: "R1"
responses:
200:
description: One or more account exist and are associated with the fund and round
content:
application/json:
schema:
type: array
$ref: '#/components/schemas/account'
404:
description: "No associated accounts found."
/bulk-accounts:
get:
tags:
Expand Down
15 changes: 15 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,18 @@ def seed_test_data(request, app, clear_test_data, _db):
for user in users_to_create:
create_user_with_roles(user, _db)
yield users_to_create


@pytest.fixture(scope="function")
def seed_test_data_fn(request, app, clear_test_data, _db):
marker = request.node.get_closest_marker("user_config")
if not marker:
users_to_create = [test_user_1, test_user_2, test_user_to_update]
else:
users_to_create = marker.args[0]
for user in users_to_create:
create_user_with_roles(user, _db)
yield users_to_create
Role.query.delete()
Account.query.delete()
_db.session.commit()
102 changes: 102 additions & 0 deletions tests/test_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Tests the GET and POST functionality of our api.
"""

import uuid

import pytest

from tests.conftest import test_user_1
Expand Down Expand Up @@ -327,3 +329,103 @@ def test_update_role_with_non_existent_role_allows(self, flask_test_client, clea
response = flask_test_client.put(url, json=params)

assert response.status_code == 201


class TestGetAccountsForFund:
@pytest.mark.user_config(
[
{
"email": "[email protected]",
"subject_id": "1",
"account_id": uuid.uuid4(),
"roles": ["COF_ASSESSOR_R1"],
},
{
"email": "[email protected]",
"subject_id": "2",
"account_id": uuid.uuid4(),
"roles": ["COF_COMMENTER_R1"],
},
{
"email": "[email protected]",
"subject_id": "3",
"account_id": uuid.uuid4(),
"roles": ["COF_COMMENTER_R2"],
},
{
"email": "[email protected]",
"subject_id": "4",
"account_id": uuid.uuid4(),
"roles": ["HSRA_ASSESSOR_R1"],
},
]
)
def test_successful_retrieval(self, flask_test_client, seed_test_data_fn):
response = flask_test_client.get("/accounts/fund/COF")
assert response.status_code == 200
assert len(response.json) == 3

@pytest.mark.user_config(
[
{
"email": "[email protected]",
"subject_id": "1",
"account_id": uuid.uuid4(),
"roles": ["COF_ASSESSOR_R1"],
},
{
"email": "[email protected]",
"subject_id": "2",
"account_id": uuid.uuid4(),
"roles": ["COF_COMMENTER_R1"],
},
]
)
def test_assessors_only(self, flask_test_client, seed_test_data_fn):
response = flask_test_client.get("/accounts/fund/COF?include_assessors=true&include_commenters=false")
assert response.status_code == 200
assert len(response.json) == 1 # Only the assessor should be returned
assert all("ASSESSOR" in role for role in response.json[0]["roles"])

@pytest.mark.user_config(
[
{
"email": "[email protected]",
"subject_id": "1",
"account_id": uuid.uuid4(),
"roles": ["COF_ASSESSOR_R1"],
},
{
"email": "[email protected]",
"subject_id": "2",
"account_id": uuid.uuid4(),
"roles": ["COF_COMMENTER_R1"],
},
]
)
def test_commenters_only(self, flask_test_client, seed_test_data_fn):
response = flask_test_client.get("/accounts/fund/COF?include_assessors=false&include_commenters=true")
assert response.status_code == 200
assert len(response.json) == 1 # Only the commenter should be returned
assert all("COMMENTER" in role for role in response.json[0]["roles"])

@pytest.mark.user_config([]) # No users configured
def test_no_matching_accounts(self, flask_test_client, seed_test_data_fn):
response = flask_test_client.get("/accounts/fund/unknownfund")
assert response.status_code == 404
assert response.json == {"error": "No matching accounts found"}

@pytest.mark.user_config(
[
{
"email": "[email protected]",
"subject_id": "3",
"account_id": uuid.uuid4(),
"roles": ["COF_ASSESSOR_R1", "COF_COMMENTER_R1"],
}
]
)
def test_bad_request(self, flask_test_client, seed_test_data_fn):
response = flask_test_client.get("/accounts/fund/COF?include_assessors=false&include_commenters=false")
assert response.status_code == 400
assert response.json == {"error": "One of include_assessors or include_commenters must be true"}

0 comments on commit c4a7a05

Please sign in to comment.