This application is an adapter for Pääsuke written in Python Flask and Postgres functions.
This can be used (as a baseline) by a party who keeps mandates on their side and offers a standard web service for Pääsuke for it to add new mandates, query existing mandates end their validity.
The application uses a Postgres database for storing the data. Into that Postgres database, the scripts create some views and stored functions that the Python app calls. So anyone who keeps their data in a relation database could adapt a similar pattern and create their own database views and stored functions.
Every time this repository is tag-ed with a new version, GitHub Actions builds a public Docker image and uploads it to GitHub Container Registry (ghcr.io) See the details of recent actions and the list of tags.
For example, one can get the version 0.9.0 like this:
docker pull ghcr.io/e-gov/ph-sample-provider-python:v0.9.0
docker pull --platform linux/x86_64 ghcr.io/e-gov/ph-sample-provider-python:v0.9.0
(Mac)
Use Docker-compose to start up a Postgres database and the application. The scripts create a few tables to sore information about mandates and views and functions to interact with this data.
`python3 -m venv venv`
`source venv/bin/activate`
`pip install -r requirements.txt`
`export PYTHONPATH=$PWD`
`python -m venv venv`
`venv\Scripts\activate`
`pip install -r requirements.txt`
`set PYTHONPATH=%cd%`
`cp config/example.cfg config/dev.cfg`
`export APP_SETTINGS=../config/dev.cfg`
`python3 api/app.py`
`copy config\example.cfg config\dev.cfg`
`set APP_SETTINGS=..\config\dev.cfg`
`python api\app.py`
`docker-compose up -d`
Configure the list of roles in tests/pg_data/02b_view_paasuke_roles_view.sql
Tests are using the Postgres database running on a Docker container (so you need to have docker-compose up postgres
)
Database contains fixture data .
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
export PYTHONPATH=$PWD
export APP_SETTINGS=../config/test.cfg
pytest
`pip install gunicorn`
Point gunicorn to WSGI entrypoint wsgi.py
`gunicorn --bind 127.0.0.1:5001 wsgi:application`
When running locally then access API on http://localhost:8082
Accepts representee identifier as parameter in path
Raises 400
error if identifier is not valid
Returns the list of MandateTriplet
s with status code 200
if representee has valid mandates
Returns empty list if the representee has no valid mandates or the representee is unknown.
Selects data from Postgres view paasuke_mandates_view
Example:
curl --location 'http://localhost:8082/v1/representees/EE44444444/delegates/mandates' \
--header 'X-Road-UserId: test-header-xroad-userid' \
--header 'X-Road-Represented-Party: test-header-xroad-represented-party' \
--header 'X-Road-Id: test-header-xroad-id'
Accepts delegate identifier as parameter in path
Raises 400
error if identifier is not valid
Returns the list of MandateTriplet
s with status code 200
if delegate has valid mandates
Returns an empty list if delegate has no valid mandates or the delegate is unknown.
Selects data from Postgres view paasuke_mandates_view
Example:
curl --location 'http://127.0.0.1:8082/v1/delegates/EE22202222222/representees/mandates' \
--header 'X-Road-UserId: Test User Id'
Get a list of roles
Selects data from Postgres view paasuke_roles_view
Example:
curl --location 'http://localhost:8082/v1/roles'
Accepts repreentee and delegate identifiers as path parameters
Raises 400
error if payload data is not valid or identifiers are not valid
Raises 422
error if Postgres function paasuke_add_mandate
does not validate input data.
Return an empty list with status code 201
in case of success
Example of successful request:
curl --location 'http://127.0.0.1:8082/v1/representees/EE12345678/delegates/EE38302250123/mandates' \
--header 'Content-Type: application/json' \
--header 'X-Road-UserId: LT123456' \
--header 'X-Road-Represented-Party: LV1234566' \
--data ' {
"authorizations": [
{
"hasRole": "BR_REPRIGHT:JUHL_SOLEREP",
"userIdentifier": "EE49028099999"
}
],
"representee": {
"identifier": "EE12345678",
"legalName": "Väikefirma OÜ",
"type": "LEGAL_PERSON"
},
"delegate": {
"firstName": "Jüri",
"identifier": "EE38302250123",
"surname": "Juurikas",
"type": "NATURAL_PERSON"
},
"document": {
"singleDelegate": true,
"uuid": "5b72e01c-fa7f-479c-b014-cc19efe5b732"
},
"mandate": {
"canSubDelegate": true,
"role": "AGENCY_X:MANDATES_MANAGER",
"validityPeriod": {
"from": "2028-01-01",
"through": "2030-12-31"
}
}
}'
Accepts representeeId
, delegateId
, mandateId
as parameters in the path.
Raises 404
error if the mandate does not exist
Raises 422
error if Postgres function paasuke_add_mandate_subdelegate
does not validate input data.
Returns empty list with status code 200
in success case
Example of successful request:
curl --location 'http://127.0.0.1:8082/v1/representees/100004/delegates/100005/mandates/150003/subdelegates' \
--header 'Content-Type: application/json' \
--header 'X-Road-UserId: EE23232323' \
--header 'X-Road-Represented-Party: EE2323224444' \
--data '{
"authorizations": [
{
"hasRole": "BR_REPRIGHT:PROK_SOLEREP",
"userIdentifier": "EE39912310123"
}
],
"document": {
"singleDelegate": true,
"uuid": "5b72e01c-fa7f-479c-b014-cc19efe5b732"
},
"subDelegate": {
"firstName": "Jüri",
"identifier": "EE38302250123",
"surname": "Juurikas",
"type": "NATURAL_PERSON"
},
"validityPeriod": {
"from": "2028-01-01",
"through": "2030-12-31"
}
}'
Accepts representeeId
, delegateId
, mandateId
as parameters in the path.
Raises 404
error if the mandate does not exist
Raises 422
error if Postgres function paasuke_delete_mandate
does not validate input data.
Returns empty list with status code 200
if mandates has been marked deleted successfully
Example of successful request:
curl --location --request PUT 'http://127.0.0.1:8082/v1/representees/100001/delegates/100003/mandates/150002' \
--header 'Content-Type: application/json' \
--data '{
"action": "DELETE",
"authorizations": [
{
"userIdentifier": "EE39912310123",
"hasRole": "BR_REPRIGHT:SOLEREP"
}
],
"document": {
"uuid": "5b72e01c-fa7f-479c-b014-cc19efe5b732",
"singleDelegate": true
}
}'
GET /v1/delegates/<str:delegate>/representees/mandates
- Request parameter
subDelegatedBy
is not used
- Request parameter
GET /v1/representees/<str:representee>/delegates/mandates
- Request parameter
subDelegatedBy
is not used
- Request parameter
GET /roles
- Response RoleDefinition contains
canSubDelegate
property - Response RoleDefinition contains
modified
property - Response RoleDefinition does not contain
representeeIdentifierIn
property - Response RoleDefinition
representeeType
property containsGORVENRMENT_PERSON
enum - Response RoleDefinition does not contain
subDelegable
property - Response RoleDefinition does not contain
subDelegatingMustBeSigned
property - Response RoleDefinition contains
assignableBy
property
- Response RoleDefinition contains