Skip to content

feat(search): FTRS-719 Role based field access#1112

Open
DomBaker wants to merge 15 commits intomainfrom
task/FTRS-719-Role-based-responses
Open

feat(search): FTRS-719 Role based field access#1112
DomBaker wants to merge 15 commits intomainfrom
task/FTRS-719-Role-based-responses

Conversation

@DomBaker
Copy link
Copy Markdown
Contributor

@DomBaker DomBaker commented Apr 2, 2026

I have added role-based filtering to the healthcare service search endpoint. When a request comes in, I read the caller's role (clinician or healthcareAdvisor) and their system (e.g. 111, 999) from the request headers. I then filter services so that only callers with a matching role and system see restricted ones — for example, a clinician-only service won't be returned to a health advisor. On top of that, I strip non-public phone numbers from the response for any caller without a recognised role (i.e. public-facing requests). I also fixed a failing test in the CRUD API where the response now correctly includes an empty referralRoles field following a recent domain model update.

Description

As a Clinician
USER STORY
I want to be able to get the non-public contact details and referral instructions for a service

So that I can direct a patient efficiently

As a non-clinician

I want to get only relevant information in the response

So that I can take a clear course of action

Context


Sensitive Information Declaration

To ensure the utmost confidentiality and protect your and others privacy, we kindly ask you to NOT including PII (Personal Identifiable Information) / PID (Personal Identifiable Data) or any other sensitive data in this PR (Pull Request) and the codebase changes. We will remove any PR that do contain any sensitive information. We really appreciate your cooperation in this matter.

  • I confirm that neither PII/PID nor sensitive data are included in this PR and the codebase changes.

class ReferralOrgEntry(BaseModel):
org_id: UUID
org_name: str
assigned_org_roles: list[ReferralRoleEntry]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the names for the fields taken from? I notice its a bit different in the data dictionary? https://nhsd-confluence.digital.nhs.uk/spaces/FRS/pages/917480828/Data+Dictionary#DataDictionary-HealthcareServicetable%3AServicetype

[ { "system_name": "111", "assigned_system_roles": [ {"role_id":UUID4, "role_name": "clinician"} ], "system_id": UUID4 }, { "system_name": "999", "assigned_system_roles": [ {"role_id": UUID4, "role_name": "clinician"}, {"role_id": UUID4, "role_name": "healthcareAdvisor"} ], "system_id": UUID4 } ]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field names come from the internal domain model in ftrs_data_layer/domain/healthcare_service.py rather than the data dictionary directly. The data dictionary uses "system" terminology (e.g. system_id, system_name, assigned_system_roles) because 111/999 are calling systems, but the domain model uses "org" to reflect that these entries represent organisations. There's no alias mapping between the two — they're genuinely different names for the same concept. If we want to align the code with the data dictionary, we'd need to rename org_id → system_id, org_name → system_name, and assigned_org_roles → assigned_system_roles in the domain model and all usages. should this be aligned?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i also assumed that we were trying to match the data dictionary as close as possible

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've aligned this to be closely mapped to the data dictionary 👍🏻

Comment thread services/dos-search/src/common/model/organization_headers.py Outdated
resources = self._create_resources(healthcare_services, include_locations)
eligible_services = self._filter_by_referral_roles(
healthcare_services, role, caller_system
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to add log to say number of eligible services after filtering?



class ReferralRoleEntry(BaseModel):
referral_function: str
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't remember what this are called in the db will double check


class ReferralRoleEntry(BaseModel):
referral_function: str
return_level: str
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not yet added to db

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants