Skip to content

Commit 7e1faef

Browse files
authored
Merge pull request #6 from itk-dev-rpa/release/1.1.0
Release/1.1.0
2 parents 8db6584 + 33c912c commit 7e1faef

26 files changed

+2517
-22
lines changed

.github/workflows/Linting.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
runs-on: ubuntu-latest
88
strategy:
99
matrix:
10-
python-version: ["3.11"]
10+
python-version: ["3.12"]
1111
fail-fast: false
1212
steps:
1313
- uses: actions/checkout@v4

README.md

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# python_serviceplatformen
22

3-
This project is made to hopefully make it easier to use Kombit's Serviceplatformen API.
3+
This project is made to make it easier to use Kombit's Serviceplatformen API.
44

55
## Certificates and authentication
66

@@ -27,9 +27,97 @@ class and it will handle the rest for you.
2727

2828
```python
2929
from python_serviceplatformen.authentication import KombitAccess
30-
ka = KombitAccess(cvr=cvr, cert_path=cert_path)
30+
kombit_access = KombitAccess(cvr="12345678", cert_path="C:\something\Certificate.pem", test=False)
3131
```
3232

33+
If you want to use the test system instead pass `test=True` to KombitAccess.
34+
35+
## Digital Post
36+
37+
This library supports the full functionality of Digital Post including NemSMS.
38+
39+
### Check registration
40+
41+
You can easily check if someone is registered for Digital Post or NemSMS:
42+
43+
```python
44+
digital_post.is_registered(cpr="1234567890", service="digitalpost", kombit_access=kombit_access)
45+
digital_post.is_registered(cpr="1234567890", service="nemsms", kombit_access=kombit_access)
46+
```
47+
48+
### MeMo model
49+
50+
A detailed data class model has been defined to help define MeMo objects which are used
51+
in the api.
52+
53+
The entire model is located in the message module:
54+
55+
```python
56+
from python_serviceplatformen.models.message import Message
57+
```
58+
59+
A detailed description of the model and all attributes can be found in the official documentation:
60+
[MeMo - Digitaliser.dk](https://digitaliser.dk/digital-post/vejledninger/memo)
61+
62+
**Note:** The model doesn't follow the normal python naming conventions to follow the source names as close as possible.
63+
64+
### Send Digital Post
65+
66+
To send a message construct a message object and then send it off to the send_message function:
67+
68+
```python
69+
import uuid
70+
from datetime import datetime
71+
import base64
72+
73+
from python_serviceplatformen.authentication import KombitAccess
74+
from python_serviceplatformen import digital_post
75+
from python_serviceplatformen.models.message import (
76+
Message, MessageHeader, Sender, Recipient, MessageBody, MainDocument, File
77+
)
78+
79+
kombit_access = KombitAccess(cvr="55133018", cert_path=r"C:\Users\az68933\Desktop\SF1601\Certificate.pem")
80+
81+
m = Message(
82+
messageHeader=MessageHeader(
83+
messageType="DIGITALPOST",
84+
messageUUID=str(uuid.uuid4()),
85+
label="Digital Post test message",
86+
mandatory=False,
87+
legalNotification=False,
88+
sender=Sender(
89+
senderID="12345678",
90+
idType="CVR",
91+
label="Python Serviceplatformen"
92+
),
93+
recipient=Recipient(
94+
recipientID="1234567890",
95+
idType="CPR"
96+
)
97+
),
98+
messageBody=MessageBody(
99+
createdDateTime=datetime.now(),
100+
mainDocument=MainDocument(
101+
files=[
102+
File(
103+
encodingFormat="text/plain",
104+
filename="Besked.txt",
105+
language="da",
106+
content=base64.b64encode(b"Hello World").decode()
107+
)
108+
]
109+
)
110+
)
111+
)
112+
113+
digital_post.send_message("Digital Post", m, kombit_access)
114+
```
115+
116+
### Recipes
117+
118+
The message module also contains a few static helper functions to construct simple messages. These are not meant to
119+
be all encompassing but to help as a starting point.
120+
33121
## Tests
34122

35123
This project contains automated tests in the "tests" folder.
@@ -46,4 +134,13 @@ Create a .env file in the project folder and fill out these variables:
46134
```yaml
47135
KOMBIT_TEST_CVR = "XXXXXXXX" # The cvr of the organization who owns the certificate
48136
KOMBIT_TEST_CERT_PATH = "C:\something\Certificate.pem" # The path to the certificate file
137+
DIGITAL_POST_TEST_CPR = "xxxxxxxxxx" # The receiver of the test messages
138+
```
139+
140+
### Running the tests
141+
142+
To run all tests open a command line in the project folder and run:
143+
144+
```bash
145+
python -m unittest
49146
```

changelog.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
nop
11+
12+
## [1.1.0] - 2024-11-01
13+
14+
### Added
15+
16+
- Complete data class model of MeMo objects.
17+
- Tools to verify and convert MeMo objects to xml.
18+
- Function for sending a MeMo message.
19+
- Helper functions to create simple MeMo messages.
20+
1021
## [1.0.0] - 19-09-2024
1122

1223
### Added
1324

1425
- Module for authenticating towards Kombit's Serviceplatform API.
1526
- Function for checking if someone is registered for Digital Post or NemSMS.
1627

17-
[Unreleased]: https://github.com/itk-dev-rpa/python-serviceplatformen/compare/1.0.0...HEAD
28+
[Unreleased]: https://github.com/itk-dev-rpa/python-serviceplatformen/compare/1.1.0...HEAD
29+
[1.1.0]: https://github.com/itk-dev-rpa/python-serviceplatformen/releases/tag/1.1.0
1830
[1.0.0]: https://github.com/itk-dev-rpa/python-serviceplatformen/releases/tag/1.0.0

pyproject.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "python_serviceplatformen"
7-
version = "1.0.0"
7+
version = "1.1.0"
88
authors = [
99
{ name="ITK Development", email="[email protected]" },
1010
]
1111
description = "Python modules for easier use of Kombit's serviceplatformen."
1212
readme = "README.md"
13-
requires-python = ">=3.7"
13+
requires-python = ">=3.10, <3.13"
1414
classifiers = [
1515
"Programming Language :: Python :: 3",
1616
"License :: OSI Approved :: MIT License",
1717
]
1818
dependencies = [
1919
"requests == 2.*",
20-
"cryptography"
20+
"cryptography",
21+
"xmlschema"
2122
]
2223

2324
[project.urls]
@@ -30,3 +31,6 @@ dev = [
3031
"flake8",
3132
"pylint"
3233
]
34+
35+
[tool.setuptools.package-data]
36+
"*" = ["*.xsd"]

python_serviceplatformen/authentication.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def get_access_token(self, entity_id: str) -> str:
6161
self._access_tokens[entity_id] = access_token
6262
return access_token[0]
6363
except HTTPError as exc:
64-
raise ValueError(f"Couldn't obtain access token for {entity_id}") from exc
64+
raise ValueError(f"Couldn't obtain access token for {entity_id}: {exc.response.text}") from exc
6565

6666

6767
def _get_saml_token(cvr: str, cert_path: str, entity_id: str, test: bool) -> str:
Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
"""This module contains function to help with datetimes in the Kombit API."""
1+
"""This module contains functions to help with dates and datetimes in the Kombit API."""
22

3-
from datetime import datetime
3+
from datetime import datetime, date
44

55

6-
def format_datetime(_datetime: datetime) -> str:
6+
def format_datetime(datetime_: datetime) -> str:
77
"""Convert a datetime object to a string with the format:
88
%Y-%m-%dT%H:%M:%SZ
99
"""
10-
return _datetime.strftime('%Y-%m-%dT%H:%M:%SZ')
10+
return datetime_.strftime('%Y-%m-%dT%H:%M:%SZ')
11+
12+
13+
def format_date(date_: date) -> str:
14+
"""Convert a date object to a string with the format:
15+
%Y-%m-%d
16+
"""
17+
return date_.strftime('%Y-%m-%d')

python_serviceplatformen/digital_post.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
"""This moduel contains helper functions to use the SF1601 Kombit API.
1+
"""This module contains helper functions to use the SF1601 Kombit API.
22
https://digitaliseringskataloget.dk/integration/sf1601
33
"""
44

55
import urllib.parse
66
import uuid
77
from datetime import datetime
88
from typing import Literal
9+
from xml.etree import ElementTree
910

1011
import requests
1112

1213
from python_serviceplatformen.authentication import KombitAccess
1314
from python_serviceplatformen.date_helper import format_datetime
15+
from python_serviceplatformen.models import xml_util
16+
from python_serviceplatformen.models.message import Message
1417

1518

1619
def is_registered(cpr: str, service: Literal['digitalpost', 'nemsms'], kombit_access: KombitAccess) -> bool:
@@ -41,3 +44,41 @@ def is_registered(cpr: str, service: Literal['digitalpost', 'nemsms'], kombit_ac
4144
response = requests.get(url, params=parameters, headers=headers, timeout=10)
4245
response.raise_for_status()
4346
return response.json()['result']
47+
48+
49+
def send_message(message_type: Literal['Digital Post', 'NemSMS'],
50+
message: Message, kombit_access: KombitAccess) -> str:
51+
"""Send a Message object as Digital Post or NemSMS.
52+
53+
Args:
54+
message_type: The type of message to send.
55+
message: The Message object to send.
56+
kombit_access: The KombitAccess objet used to authenticate.
57+
58+
Returns:
59+
The uuid of the transaction to trace the message later.
60+
"""
61+
url = urllib.parse.urljoin(kombit_access.environment, "service/KombiPostAfsend_1/memos")
62+
63+
transaction_id = str(uuid.uuid4())
64+
65+
headers = {
66+
"X-TransaktionsId": transaction_id,
67+
"X-TransaktionsTid": format_datetime(datetime.now()),
68+
"authorization": kombit_access.get_access_token("http://entityid.kombit.dk/service/kombipostafsend/1"),
69+
"Content-Type": "application/xml"
70+
}
71+
72+
message_xml = xml_util.dataclass_to_xml(message)
73+
xml_util.validate_memo(message_xml)
74+
75+
element = ElementTree.Element("kombi_request")
76+
ElementTree.SubElement(element, "KombiValgKode").text = message_type
77+
element.append(message_xml)
78+
79+
xml_body = ElementTree.tostring(element, encoding="utf8").decode()
80+
81+
response = requests.post(url=url, headers=headers, data=xml_body, timeout=10)
82+
response.raise_for_status()
83+
84+
return transaction_id

python_serviceplatformen/models/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)