ЕСОЗ - публічна документація
(GraphQL, NHS) Get Person Details
- Dmytro Omelchenko (SoE eHealth)
- Yuliia Mazur (UA SoE eHealth)
Purpose
This method allows to view Person's (MPI) details.
Specification
Link | - | Посилання на Apiary або Swagger |
Resource | /graphql | Посилання на ресурс, наприклад: /api/persons/create |
Scope | person:read | Scope для доступу |
Components | Patient registry | Зазначається перелік бізнес компонентів, які використовують цей метод, наприклад: ePrescription |
Microservices | mpi/api il/api (rpc) fe/admin-web | Перелік мікросервісів, які використовує метод API, наприклад: Auth, ABAC |
Protocol type | GraphQL | Тип протоколу, який використовується запитом, наприклад: SOAP | REST |
Request type | POST | Тип запиту API, наприклад: GET, POST, PATCH… |
Sync/Async | Sync | Метод є синхронним чи асинхронним? |
Public/Private/Internal | Private | Потрібно зазначити тип методу за ступенем доступності |
"""
This is Person (MPI) details. In order to obtain details user must have a scope **person:read**
"""
type Person implements Node {
"The ID of an object"
id: ID!
"Primary key identifier from the database"
databaseId: UUID!
"Patient first name."
firstName: String! @fake(locale: "uk", method: "name.firstName", args: [1])
"Patient last name."
lastName: String! @fake(locale: "uk", method: "name.lastName", args: [1])
"Patient second name, is exists."
secondName: String @fake(locale: "uk", method: "name.firstName", args: [0])
"Patient birth date."
birthDate: Date! @fake(locale: "uk", method: "date.past", args: [100])
"Patient birth country."
birthCountry: String! @fake(locale: "uk", format: "Україна")
"Person's death date."
deathDate: Date @fake(locale: "uk", method: "date.past", args: [100])
"Patient gender. The value should be present in the `GENDER` dictionary."
gender: String!
"Patient's email"
email: String
"Patient status in the system, is set automatically."
status: PersonStatus!
"Patient birth settlement."
birthSettlement: String! @fake(locale: "uk", method: "address.city")
"the unique number in Unified State Register"
unzr: String @fake(randexp: "^[0-9]{8}-[0-9]{5}$")
"National person identifier."
taxId: String
@fake(
locale: "uk"
method: "random.number"
args: [{ min: 1000000000, max: 9999999999 }]
)
"Flag to show whether person rejected to have taxId"
noTaxId: Boolean
"Preferred way to communicate with the patient."
preferredWayCommunication: PersonPreferredWayCommunication
"Technical information when the patient was inserted into the DB."
insertedAt: DateTime!
"Technical information when the patient was updated in the DB."
updatedAt: DateTime!
"The method to verify changes of patient by patient."
authenticationMethods(
"The method to use when ordering collection items."
filter: PersonAuthenticationMethodFilter
"The method to use when ordering collection items."
orderBy: PersonAuthenticationMethodOrderBy
"Read all values in the set after (below) this cursor."
after: String
"Read all values in the set before (above) this cursor."
before: String
"Only read the first _n_ values of the set."
first: Int
"Only read the last _n_ values of the set."
last: Int
): PersonAuthenticationMethodConnection!
"Patient identification documents."
documents: [PersonDocument]
"Patient addresses."
addresses: [Address]!
"The way how a patient wants to be reached."
phones: [Phone]
"Patient's contract person in case of emergency"
emergencyContact: EmergencyContact!
"The person(s) who is(are) responsible for the patient"
confidantPersons: [ConfidantPerson]
"Person's declarations."
declarations(
"The method to use when ordering collection items."
orderBy: DeclarationOrderBy
"Read all values in the set after (below) this cursor."
after: String
"Read all values in the set before (above) this cursor."
before: String
"Only read the first _n_ values of the set."
first: Int
"Only read the last _n_ values of the set."
last: Int
): DeclarationConnection!
"Person's secret word for voice authentification"
secret: String
"internal NHS number of Requests written by Person. 15-20 symbols"
nhsRequestNumber: String!
"Description of request - either title of paper Request or description of requested changes. Up to 300 symbols"
nhsRequestComment: String!
"Scan files of Person's documents, located in Bucket, where name = latest Person_request.ID identifier. person_documents:read scope is needed to get this parameter"
personRequestAttachedDocuments: PersonRequestAttachedDocuments
"Cummulative verification status of the person"
verificationStatus: PersonVerificationStatus!
"Details about cummulative verification status of the person"
verificationDetails: PersonVerificationDetail
}
"""
Scan files of documents, located in Bucket
"""
type PersonRequestAttachedDocuments {
"List of scan copies of documents, related to Confidant persons"
confidantPersons: [ConfidantPersonAttachedDocuments]
"List of scan copies of Person's documents"
documents: [PersonRequestAttachedDocument]
"Channel, which was used for creation of a latest Person Request"
lastPersonRequestChannel: String
"Date and time of creation of a latest Person Request"
lastPersonRequestInsertedAt: DateTime
}
"""
Scan file of document, located in Bucket
"""
type PersonRequestAttachedDocument {
"The type of document."
type: String!
"Person's scan files of documents, located in Bucket, in latest Person_request.id folder."
url: String!
}
"""
List of person statuses.
"""
enum PersonStatus {
"Status `ACTIVE` for a person"
ACTIVE
"Status `INACTIVE` for a person"
INACTIVE
}
"""
List of person preferred way communication.
"""
enum PersonPreferredWayCommunication {
"`EMAIL` as a preferred way of the communication."
EMAIL
"`PHONE` as a preferred way of the communication."
PHONE
}
"""
Person identifier documents.
"""
type PersonDocument {
"Dictionary DOCUMENT_TYPE"
type: String
"document issue number"
number: String
"authority which issued the document"
issuedBy: String @fake(locale: "uk", method: "company.companyName")
"the date when document was issued"
issuedAt: Date
"document expiration date"
expirationDate: Date
}
"""
Patient's contract person in case of emergency.
"""
type EmergencyContact {
"Person first name."
firstName: String! @fake(locale: "uk", method: "name.firstName", args: [1])
"Person last name."
lastName: String! @fake(locale: "uk", method: "name.lastName", args: [1])
"Person second name, if exists."
secondName: String @fake(locale: "uk", method: "name.firstName", args: [0])
"The way how a perons wants to be reached."
phones: [Phone]!
}
"""
The person who is responsible for the patient.
"""
type ConfidantPerson {
"Priority of persons. The value should be present in the `CONFIDANT_PERSON_TYPE` dictionary."
relationType: String!
"Person first name."
firstName: String! @fake(locale: "uk", method: "name.firstName", args: [1])
"Person last name."
lastName: String! @fake(locale: "uk", method: "name.lastName", args: [1])
"Person second name, is exists."
secondName: String @fake(locale: "uk", method: "name.firstName", args: [0])
"Person birth date."
birthDate: Date @fake(locale: "uk", method: "date.past", args: [100])
"Person birth country."
birthCountry: String! @fake(locale: "uk", format: "Україна")
"Person birth settlement."
birthSettlement: String! @fake(locale: "uk", method: "address.city")
"Patient gender. The value should be present in the `GENDER` dictionary."
gender: String!
"the unique number in Unified State Register"
unzr: String @fake(randexp: "^[0-9]{8}-[0-9]{5}$")
"National person identifier."
taxId: String
@fake(
locale: "uk"
method: "random.number"
args: [{ min: 1000000000, max: 9999999999 }]
)
"Person's email"
email: String
"ConfidantPerson identification documents."
documents: [PersonDocument]!
"Documents which prove relationship with the patient"
relationshipDocuments: [PersonDocument]!
"The way how a perons wants to be reached."
phones: [Phone]
"Preferred way to communicate with the confident person."
preferredWayCommunication: PersonPreferredWayCommunication
}
Logic
This is a graphQl query method used in Administration panel only to get person’s data . Only authenticated and authorized NHS employee with appropriate scope can get person’s data.
Request structure
Example:
{
"query": "query PersonQuery($id: ID!, $filter: PersonAuthenticationMethodFilter, $after: String, $before: String, $first: Int, $last: Int) { person(id: $id) { ...UserInfo ...AuthMethods ...PersonConfidantPersons ...EmergencyContact ...VerificationDetails __typename } } fragment AuthMethods on Person { authenticationMethods(filter: $filter, first: $first, last: $last, after: $after, before: $before) { nodes { id alias type startedAt endedAt phoneNumber isActive thirdPerson { databaseId firstName lastName secondName id authenticationMethods(filter: {type: [\"OTP\", \"OFFLINE\", \"NA\"]}, first: 50) { nodes { id type phoneNumber __typename } __typename } __typename } __typename } __typename } __typename } fragment VerificationDetails on Person { verificationDetails { dracsDeath { verificationComment verificationReason verificationStatus dracsDeathActId unverifiedAt __typename } drfo { result syncedAt unverifiedAt verificationReason verificationStatus __typename } manualRules { unverifiedAt verificationComment verificationReason verificationStatus __typename } __typename } __typename } fragment PersonConfidantPersons on Person { confidantPersons { relationType firstName lastName secondName gender birthDate birthCountry birthSettlement unzr taxId email secret preferredWayCommunication phones { type number __typename } documents { type number issuedAt issuedBy expirationDate __typename } relationshipDocuments { type number issuedAt issuedBy expirationDate __typename } __typename } __typename } fragment EmergencyContact on Person { emergencyContact { firstName lastName secondName phones { type number __typename } __typename } __typename } fragment UserInfo on Person { id databaseId status verificationStatus firstName secondName lastName gender birthDate birthCountry birthSettlement addresses { ...Addresses __typename } taxId noTaxId insertedAt updatedAt unzr email phones { type number __typename } preferredWayCommunication documents { type number issuedAt issuedBy expirationDate __typename } nhsRequestNumber nhsRequestComment deathDate secret __typename } fragment Addresses on Address { type country area region settlement settlementType settlementId streetType street building apartment zip __typename }",
"variables": {
"id": "UGVyc29uOjlkMWYzYzhlLWJlYzctNDUyYS04ZTlmLWNhYjdlOGRjYWU1OA==",
"first": 10,
"filter": {
"isEnded": false
}
}
}
Authorize
Request to process the request using a token in the headers.
Verify the validity of access token
Return
401
('Invalid access token') in case validation fails
Verify that token is not expired
in case of error - return (
401
, 'Invalid access token')
Check user scopes in order to perform this action (scope = 'person:read')
Return
403
in case invalid scope(s) "Your scope does not allow to access this resource. Missing allowances: person:read“
Request data validation
Validate legal entity
Extract client_id from token.
Check legal entity status (status = ACTIVE)
In case of error - return
409
('client_id refers to legal entity that is not active')
Validate request
Validate $.personId
Check $.personId
is ID from MPI.person.id
search person
$.personId
inMPI.person.(id = $.personId)
andMPI.person.(id = $.personId).is_active = true then ok
in case of error, return
404
, "Such person doesn't exist"
Processing
Get data from mpi DB:
persons by id
Render a response according to specification.
Response structure
Example:
{
"data": {
"person": {
"__typename": "Person",
"addresses": [
{
"__typename": "Address",
"apartment": "4",
"area": "Рівненська",
"building": "77",
"country": "UA",
"region": "Хмельницька",
"settlement": "Дніпро",
"settlementId": "429731cc-e019-44d6-a17e-3d8c16b78dba",
"settlementType": "CITY",
"street": "Абрикосова",
"streetType": "AVENUE",
"type": "RESIDENCE",
"zip": "82529"
}
],
"authenticationMethods": {
"__typename": "PersonAuthenticationMethodConnection",
"nodes": [
{
"__typename": "PersonAuthenticationMethod",
"alias": "OTP",
"endedAt": null,
"id": "UGVyc29uQXV0aGVudGljYXRpb25NZXRob2Q6YTZiZDczZDQtN2Q4NS00MzlkLTk1NjktZjEwYjVlY2QyNzAy",
"isActive": true,
"phoneNumber": "+380941455543",
"startedAt": null,
"thirdPerson": null,
"type": "OTP"
}
]
},
"birthCountry": "Україна",
"birthDate": "1975-06-15",
"birthSettlement": "Луцьк",
"confidantPersons": [
{
"__typename": "ConfidantPerson",
"birthCountry": "Україна",
"birthDate": "1996-12-12",
"birthSettlement": "Дніпро",
"documents": [
{
"__typename": "PersonDocument",
"expirationDate": null,
"issuedAt": "2013-03-15",
"issuedBy": "Херсон РОУ ВМУ МВС в Рівненська області",
"number": "ОП324030",
"type": "PASSPORT"
}
],
"email": null,
"firstName": "Юлія",
"gender": "FEMALE",
"lastName": "Басок",
"phones": [
{
"__typename": "Phone",
"number": "+380904035005",
"type": "LAND_LINE"
}
],
"preferredWayCommunication": null,
"relationType": "PRIMARY",
"relationshipDocuments": [
{
"__typename": "PersonDocument",
"expirationDate": null,
"issuedAt": "2011-03-16",
"issuedBy": "Луганьск РОУ ВМУ МВС в Хмельницька області",
"number": "ВБ361517",
"type": "DOCUMENT"
}
],
"secondName": "Корнелійовна",
"secret": "secret",
"taxId": "3541011501",
"unzr": null
}
],
"databaseId": "9d1f3c8e-bec7-452a-8e9f-cab7e8dcae58",
"deathDate": null,
"documents": [
{
"__typename": "PersonDocument",
"expirationDate": null,
"issuedAt": "2011-03-16",
"issuedBy": "Міколаїв РОУ ВМУ МВС в Чернівецька області",
"number": "ИЬ614450",
"type": "PASSPORT"
}
],
"email": null,
"emergencyContact": {
"__typename": "EmergencyContact",
"firstName": "Світлана",
"lastName": "Боровик",
"phones": [
{
"__typename": "Phone",
"number": "+380383752740",
"type": "LAND_LINE"
}
],
"secondName": "Йосиповна"
},
"firstName": "Павло",
"gender": "MALE",
"id": "UGVyc29uOjlkMWYzYzhlLWJlYzctNDUyYS04ZTlmLWNhYjdlOGRjYWU1OA==",
"insertedAt": "2021-03-13T19:44:28.426485Z",
"lastName": "Сироїд",
"nhsRequestComment": "",
"nhsRequestNumber": "",
"noTaxId": null,
"phones": [
{
"__typename": "Phone",
"number": "+380954591106",
"type": "LAND_LINE"
}
],
"preferredWayCommunication": null,
"secondName": "Пилипович",
"secret": "secret",
"status": "ACTIVE",
"taxId": null,
"unzr": null,
"updatedAt": "2023-04-26T16:12:01.740166Z",
"verificationDetails": {
"__typename": "PersonVerificationDetail",
"dracsDeath": {
"__typename": "PersonDracsDeathVerification",
"dracsDeathActId": null,
"unverifiedAt": null,
"verificationComment": " ",
"verificationReason": "MANUAL_NOT_CONFIRMED",
"verificationStatus": "VERIFIED"
},
"drfo": {
"__typename": "PersonDrfoVerification",
"result": null,
"syncedAt": null,
"unverifiedAt": null,
"verificationReason": "ONLINE_TRIGGERED",
"verificationStatus": "VERIFICATION_NEEDED"
},
"manualRules": {
"__typename": "PersonManualRulesVerification",
"unverifiedAt": null,
"verificationComment": null,
"verificationReason": "RULES_PASSED",
"verificationStatus": "VERIFIED"
}
},
"verificationStatus": "VERIFICATION_NEEDED"
}
},
"extensions": {
"requestId": "f211af74-f3f9-40dc-baef-793532d4023a#18633"
}
}
HTTP status codes
HTTP status code | Message | What caused the error |
---|
HTTP status code | Message | What caused the error |
---|---|---|
200 | Response |
|
401 | Invalid access token | Invalid token |
403 | Your scope does not allow to access this resource. Missing allowances: {{scope}} | Scope is missing |
409 | client_id refers to legal entity that is not active | Legal entity is not active |
ЕСОЗ - публічна документація