Skip to content

Commit

Permalink
18522, 18525 - updates to REQUEST affiliation (bcgov#2642)
Browse files Browse the repository at this point in the history
* 18522, 18525 - backend changes for filtering and branch name in fetching org details for affiliated orgs.

* 18522 - UI updates.

* fixed tests.

* npm run lint + lint fixes.

* Parametrizing the service.
  • Loading branch information
hfekete authored Nov 21, 2023
1 parent 2dae6ca commit a93a8e5
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 22 deletions.
10 changes: 9 additions & 1 deletion auth-api/src/auth_api/models/org.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Basic users will have an internal Org that is not created explicitly, but implicitly upon User account creation.
"""
from typing import List
from flask import current_app
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, and_, cast, event, func, text
from sqlalchemy.orm import contains_eager, relationship
Expand Down Expand Up @@ -158,12 +159,19 @@ def search_org(cls, search: OrgSearch, environment: str):
return pagination.items, pagination.total

@classmethod
def search_orgs_by_business_identifier(cls, business_identifier, pagination_info: PaginationInfo, environment):
def search_orgs_by_business_identifier(cls,
business_identifier,
pagination_info: PaginationInfo,
environment,
excluded_org_types: List[str] = None
):
"""Find all orgs affiliated with provided business identifier."""
query = db.session.query(Org)

query = cls._search_for_statuses(query, [])
query = cls._search_by_business_identifier(query, business_identifier, environment)
if excluded_org_types:
query = query.filter(Org.type_code.notin_(excluded_org_types))

pagination = query.order_by(Org.name.desc()) \
.paginate(per_page=pagination_info.limit, page=pagination_info.page)
Expand Down
13 changes: 8 additions & 5 deletions auth-api/src/auth_api/resources/v1/org.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
from auth_api.services.authorization import Authorization as AuthorizationService
from auth_api.tracer import Tracer
from auth_api.utils.endpoints_enums import EndpointEnum
from auth_api.utils.enums import AccessType, NotificationType, PatchActions, Status
from auth_api.utils.enums import AccessType, NotificationType, OrgType, PatchActions, Status
from auth_api.utils.role_validator import validate_roles
from auth_api.utils.roles import ALL_ALLOWED_ROLES, CLIENT_ADMIN_ROLES, STAFF, USER, Role # noqa: I005
from auth_api.utils.util import get_request_environment
Expand Down Expand Up @@ -390,17 +390,20 @@ def post_organization_affiliation(org_id):
@_jwt.has_one_of_roles(
[Role.SYSTEM.value, Role.STAFF_VIEW_ACCOUNTS.value, Role.PUBLIC_USER.value])
def get_org_details_by_affiliation(business_identifier):
"""Search orgs by BusinessIdentifier and return org Name and UUID."""
"""Search non staff orgs by BusinessIdentifier and return org Name, branch Name and UUID."""
environment = get_request_environment()
pagination_info = PaginationInfo(
limit=int(request.args.get('limit', 10)),
page=int(request.args.get('page', 1))
)

excluded_org_types = [OrgType.STAFF.value, OrgType.SBC_STAFF.value]
try:
data = OrgService.search_orgs_by_affiliation(business_identifier, pagination_info, environment)
data = OrgService.search_orgs_by_affiliation(
business_identifier, pagination_info, environment, excluded_org_types
)

org_details = [{'name': org.name, 'uuid': org.uuid} for org in data['orgs']]
org_details = \
[{'name': org.name, 'uuid': org.uuid, 'branchName': org.branch_name} for org in data['orgs']]
response, status = {'orgs_details': org_details}, http_status.HTTP_200_OK

except BusinessException as exception:
Expand Down
12 changes: 9 additions & 3 deletions auth-api/src/auth_api/services/org.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,10 +730,16 @@ def search_orgs(search: OrgSearch, environment): # pylint: disable=too-many-loc
return orgs_result

@staticmethod
def search_orgs_by_affiliation(business_identifier,
pagination_info: PaginationInfo, environment):
def search_orgs_by_affiliation(
business_identifier, pagination_info: PaginationInfo, environment, excluded_org_types
):
"""Search for orgs based on input parameters."""
orgs, total = OrgModel.search_orgs_by_business_identifier(business_identifier, pagination_info, environment)
orgs, total = OrgModel.search_orgs_by_business_identifier(
business_identifier,
pagination_info,
environment,
excluded_org_types
)

return {
'orgs': orgs,
Expand Down
58 changes: 52 additions & 6 deletions auth-api/tests/unit/api/test_org.py
Original file line number Diff line number Diff line change
Expand Up @@ -2136,11 +2136,11 @@ def test_new_active_search(client, jwt, session, keycloak_mock):
[('T12dfhsff1', CorpType.BC.value, 'NR 1234567'), ('T12dfhsff2', CorpType.GP.value, 'NR 1234566')],
['NR 1234567', 'NR 1234566'], []),
('affiliations_order', [], [],
[], [('abcde1', CorpType.BC.value, 'NR 123456'),
('abcde2', CorpType.BC.value, 'NR 123457'),
('abcde3', CorpType.BC.value, 'NR 123458'),
('abcde4', CorpType.BC.value, 'NR 123459')],
[datetime(2021, 1, 1), datetime(2022, 2, 1), datetime(2022, 3, 1), datetime(2023, 2, 1)]),
[], [('abcde1', CorpType.BC.value, 'NR 123456'),
('abcde2', CorpType.BC.value, 'NR 123457'),
('abcde3', CorpType.BC.value, 'NR 123458'),
('abcde4', CorpType.BC.value, 'NR 123459')],
[datetime(2021, 1, 1), datetime(2022, 2, 1), datetime(2022, 3, 1), datetime(2023, 2, 1)]),
('all', [('BC1234567', CorpType.BC.value), ('BC1234566', CorpType.BC.value)],
[('T12dfhsff1', CorpType.BC.value), ('T12dfhsff2', CorpType.GP.value)],
[('T12dfhsff3', CorpType.BC.value, 'NR 1234567'), ('T12dfhsff4', CorpType.GP.value, 'NR 1234566')],
Expand Down Expand Up @@ -2248,12 +2248,15 @@ def _create_orgs_entities_and_affiliations(client, jwt, count):

headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.public_user_role)
client.post('/api/v1/users', headers=headers, content_type='application/json')
new_org = TestOrgInfo.org_details
new_org = TestOrgInfo.org_details.copy()
new_org['name'] = new_org['name'] + ' ' + str(i)
new_org['branchName'] = 'branch-for-' + new_org['name']
rv = client.post('/api/v1/orgs', data=json.dumps(new_org),
headers=headers, content_type='application/json')
dictionary = json.loads(rv.data)
org_id = dictionary['id']
new_org['id'] = org_id
created_orgs.append(new_org)

client.post('/api/v1/orgs/{}/affiliations'.format(org_id), headers=headers,
data=json.dumps(TestAffliationInfo.affiliation3), content_type='application/json')
Expand Down Expand Up @@ -2292,4 +2295,47 @@ def test_get_orgs_by_affiliation(client, jwt, session, keycloak_mock,

for co in created_orgs:
names = [od['name'] for od in orgs_details]
branches = [od['branchName'] for od in orgs_details]
assert co['name'] in names
assert co['branchName'] in branches

for od in orgs_details:
assert 'name' in od
assert 'branchName' in od
assert 'uuid' in od
assert 'id' not in od


def test_get_orgs_by_affiliation_filtering_out_staff_orgs(app, client, jwt, session, keycloak_mock):
"""Assert that fetching orgs by affiliation do not return staff orgs."""
orig_val_max_number_of_orgs = app.config.get('MAX_NUMBER_OF_ORGS')
app.config.update(MAX_NUMBER_OF_ORGS=10)
create_org_count = 6

created_orgs = _create_orgs_entities_and_affiliations(client, jwt, create_org_count)
app.config.update(MAX_NUMBER_OF_ORGS=orig_val_max_number_of_orgs)

org3 = created_orgs[2]
org5 = created_orgs[4]

convert_org_to_staff_org(org3['id'], OrgType.SBC_STAFF.value)
convert_org_to_staff_org(org5['id'], OrgType.STAFF.value)

staff_org_names = [org3['name'], org5['name']]
expected_org_count = create_org_count - len(staff_org_names)

# Create a system token
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.system_role)
rv = client.get('/api/v1/orgs/affiliation/{}'.format(TestAffliationInfo.affiliation3.get('businessIdentifier')),
headers=headers, content_type='application/json')

assert rv.status_code == http_status.HTTP_200_OK
assert schema_utils.validate(rv.json, 'orgs_response')[1]

response = json.loads(rv.data)

assert len(response.get('orgsDetails')) == expected_org_count # without org 3 and 5
orgs_details = response.get('orgsDetails')

for od in orgs_details:
assert od['name'] not in staff_org_names
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,31 @@
:disabled="accounts.length < 2"
class="business-identifier mb-n2"
:items="accounts"
item-text="name"
item-value="uuid"
@change="emitSelected"
/>
>
<template #selection="data">
<!-- HTML that describe how select should render selected items -->
<span
v-if="data.item.branchName"
data-test="account-authorization-request-selection"
>{{ data.item.name }} - {{ data.item.branchName }}</span>
<span
v-else
data-test="account-authorization-request-selection"
>{{ data.item.name }}</span>
</template>
<template #item="data">
<span
v-if="data.item.branchName"
data-test="account-authorization-request-option"
>{{ data.item.name }} - {{ data.item.branchName }}</span>
<span
v-else
data-test="account-authorization-request-option"
>{{ data.item.name }}</span>
</template>
</v-select>
<span>You can add a message that will be included as part of your authorization request. </span>
<v-textarea
id="account-authorization-request-additional-message-textarea"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,9 @@ describe('AccountAuthorizationRequest tests', () => {

// verify that account is selected and selector disabled
expect(wrapper.find('#account-authorization-request-request-account-select').attributes().disabled).toBeDefined()
expect(wrapper.find('.v-select__selection--comma').text())
.toBe(orgsDetailsByAffiliationSingleItemResponse.orgsDetails[0].name)
expect(wrapper.findAll('.v-list-item__title').length ===
orgsDetailsByAffiliationSingleItemResponse.orgsDetails.length)
expect(wrapper.find('[data-test="account-authorization-request-selection"]').text())
.toContain(orgsDetailsByAffiliationSingleItemResponse.orgsDetails[0].name)
expect(wrapper.findAll('[data-test="account-authorization-request-option"]').exists()).toBe(false)
})

it('renders enabled select with no preselected item, when multiple affiliated accounts found', async () => {
Expand All @@ -104,7 +103,7 @@ describe('AccountAuthorizationRequest tests', () => {

// verify that account is selected and selector disabled
expect(wrapper.find('#account-authorization-request-request-account-select').attributes().disabled).toBeUndefined()
expect(wrapper.findAll('.v-list-item__title').length ===
console.assert(wrapper.findAll('[data-test="account-authorization-request-option"]').length ===
orgsDetailsByAffiliationMultipleItemsResponse.orgsDetails.length)
})
})

0 comments on commit a93a8e5

Please sign in to comment.