Skip to content

Commit 770fd1b

Browse files
HamedAkhavanViCt0r99suda
authored
Version 0.3.1 (#49)
* feat: add basic structure * feat: add few ocpi data types * feat: add data types * feat: add version module enums and schemas * feat: add basic versions module * feat: add versions details endpoint * refactor: modify VersionNumber enum * feat: add locations module, crud and adaptor * chore: update prospector config * refactor: adaptor * refactor: adaptor * chore: update readme * feat: make crud and adapter dependency, add example * refactor: import core enums and datatypes to source module * refactor: make routers and CRUD methods asycn * fix: linter * fix: update schema definitions * fix: update schema definitions * chore: add pypi publish workflow * refactor: change project name to py_ocpi * Update README.md * refactor: get prefix from env * refactor: change HOST to OCPI_HOST * feat: add pagination for list endpoints * fix: add id param to update method of crud * Update README.md * Rename README.md to README.rst * Update README.rst * Update pyproject.toml * OCPI-55 (#7) * feat: add sessions module schemas and enums * feat: add sessions module schemas and enums * feat: add sessions endpoints * OCPI-48 (#3) * feat: add schemas and enums for credentials module * fix: pylint fix * feat: add credentials module * fix: PR review changes * feat: make crud funtions in credentials endpoint async * fix: change verification params * feat: add cdrs schemas and enums (#10) * feat: add cdrs endpoints, refactor code enums, move universal files to core (#11) * OCPI-48 (#15) * feat: update authorization process * fix: pylint fix * feat: add exception handlder * fix: pylint, add middleware fix * feat: add all test modules (#17) * feat: change post credentials url * feat: modify commands module cpo (#18) * OCPI-66 (#22) * feat: improve crud and adapter, improve OCPI version management * refactor: add routers and modules directories * Fix the router URLs (#36) * Fix the router URLs * Fix versions and details URLs * fix: return version details based on role (#39) * Use dict type instead of list for details endpoint (#40) * Fix the initial authentication (#43) * Base64 encode the bearer token * Decode the base64 encoded authentication token * Require authorization when listing versions * Revert "Fix the initial authentication (#43)" (#44) This reverts commit a40dc0d. * Update push.yaml * Fix initial credentials exchange (#46) * Fix the initial authentication (#43) * Base64 encode the bearer token * Decode the base64 encoded authentication token * Require authorization when listing versions * Remove the list wrapping the credentials object * Fix tests * Skip false positive Bandit test B105 * Ignore false positive * Fix linter error * feat: make new version 0.3.1 --------- Co-authored-by: Hamed Akhavan <[email protected]> Co-authored-by: ViCt0r99 <[email protected]> Co-authored-by: ViCt0r99 <[email protected]> Co-authored-by: Wojtek Siudzinski <[email protected]>
1 parent 6dcd0c7 commit 770fd1b

File tree

20 files changed

+230
-169
lines changed

20 files changed

+230
-169
lines changed

.github/workflows/push.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
branches:
66
- 'develop'
77
- 'feature/**'
8+
- 'fix/**'
89

910
jobs:
1011
lint:

py_ocpi/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Python Implementation of OCPI"""
22

3-
__version__ = "0.3.0"
3+
__version__ = "0.3.1"
44

55
from .core import enums, data_types
66
from .main import get_application

py_ocpi/core/dependencies.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@ def get_versions():
2222
return [
2323
Version(
2424
version=VersionNumber.v_2_2_1,
25-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/{VersionNumber.v_2_2_1}/details')
25+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/{VersionNumber.v_2_2_1.value}/details')
2626
).dict(),
2727
]
2828

2929

30+
def get_endpoints():
31+
return {}
32+
33+
3034
def pagination_filters(
3135
date_from: datetime = Query(default=None),
3236
date_to: datetime = Query(default=datetime.now()),

py_ocpi/core/endpoints.py

Lines changed: 98 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,108 @@
1-
from py_ocpi.core.enums import ModuleID
1+
from py_ocpi.core.enums import ModuleID, RoleEnum
22
from py_ocpi.core.data_types import URL
33
from py_ocpi.core.config import settings
44
from py_ocpi.modules.versions.schemas import Endpoint
55
from py_ocpi.modules.versions.enums import VersionNumber, InterfaceRole
66

77
ENDPOINTS = {
8-
VersionNumber.v_2_2_1: [
8+
VersionNumber.v_2_2_1: {
99
# ###############--CPO--###############
10-
11-
# locations
12-
Endpoint(
13-
identifier=ModuleID.locations,
14-
role=InterfaceRole.sender,
15-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
16-
f'/{VersionNumber.v_2_2_1}/{ModuleID.locations}')
17-
),
18-
# sessions
19-
Endpoint(
20-
identifier=ModuleID.sessions,
21-
role=InterfaceRole.sender,
22-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
23-
f'/{VersionNumber.v_2_2_1}/{ModuleID.sessions}')
24-
),
25-
# credentials
26-
Endpoint(
27-
identifier=ModuleID.credentials_and_registration,
28-
role=InterfaceRole.receiver,
29-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
30-
f'/{VersionNumber.v_2_2_1}/{ModuleID.credentials_and_registration}')
31-
),
32-
# tariffs
33-
Endpoint(
34-
identifier=ModuleID.tariffs,
35-
role=InterfaceRole.sender,
36-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
37-
f'/{VersionNumber.v_2_2_1}/{ModuleID.tariffs}')
38-
),
39-
# cdrs
40-
Endpoint(
41-
identifier=ModuleID.cdrs,
42-
role=InterfaceRole.sender,
43-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
44-
f'/{VersionNumber.v_2_2_1}/{ModuleID.cdrs}')
45-
),
46-
# tokens
47-
Endpoint(
48-
identifier=ModuleID.tokens,
49-
role=InterfaceRole.receiver,
50-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
51-
f'/{VersionNumber.v_2_2_1}/{ModuleID.tokens}')
52-
),
10+
RoleEnum.cpo: [
11+
# locations
12+
Endpoint(
13+
identifier=ModuleID.locations,
14+
role=InterfaceRole.sender,
15+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
16+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.locations.value}')
17+
),
18+
# sessions
19+
Endpoint(
20+
identifier=ModuleID.sessions,
21+
role=InterfaceRole.sender,
22+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
23+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.sessions.value}')
24+
),
25+
# credentials
26+
Endpoint(
27+
identifier=ModuleID.credentials_and_registration,
28+
role=InterfaceRole.receiver,
29+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
30+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.credentials_and_registration.value}')
31+
),
32+
# tariffs
33+
Endpoint(
34+
identifier=ModuleID.tariffs,
35+
role=InterfaceRole.sender,
36+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
37+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.tariffs.value}')
38+
),
39+
# cdrs
40+
Endpoint(
41+
identifier=ModuleID.cdrs,
42+
role=InterfaceRole.sender,
43+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
44+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.cdrs.value}')
45+
),
46+
# tokens
47+
Endpoint(
48+
identifier=ModuleID.tokens,
49+
role=InterfaceRole.receiver,
50+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo'
51+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.tokens.value}')
52+
),
53+
],
5354

5455
# ###############--EMSP--###############
55-
56-
# credentials
57-
Endpoint(
58-
identifier=ModuleID.credentials_and_registration,
59-
role=InterfaceRole.receiver,
60-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
61-
f'/{VersionNumber.v_2_2_1}/{ModuleID.credentials_and_registration}')
62-
),
63-
# locations
64-
Endpoint(
65-
identifier=ModuleID.locations,
66-
role=InterfaceRole.receiver,
67-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
68-
f'/{VersionNumber.v_2_2_1}/{ModuleID.locations}')
69-
),
70-
# sessions
71-
Endpoint(
72-
identifier=ModuleID.sessions,
73-
role=InterfaceRole.receiver,
74-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
75-
f'/{VersionNumber.v_2_2_1}/{ModuleID.sessions}')
76-
),
77-
# cdrs
78-
Endpoint(
79-
identifier=ModuleID.cdrs,
80-
role=InterfaceRole.receiver,
81-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
82-
f'/{VersionNumber.v_2_2_1}/{ModuleID.cdrs}')
83-
),
84-
# tariffs
85-
Endpoint(
86-
identifier=ModuleID.tariffs,
87-
role=InterfaceRole.receiver,
88-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
89-
f'/{VersionNumber.v_2_2_1}/{ModuleID.tariffs}')
90-
),
91-
# commands
92-
Endpoint(
93-
identifier=ModuleID.commands,
94-
role=InterfaceRole.sender,
95-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
96-
f'/{VersionNumber.v_2_2_1}/{ModuleID.commands}')
97-
),
98-
# tokens
99-
Endpoint(
100-
identifier=ModuleID.tokens,
101-
role=InterfaceRole.sender,
102-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
103-
f'/{VersionNumber.v_2_2_1}/{ModuleID.tokens}')
104-
),
105-
]
106-
56+
RoleEnum.emsp: [
57+
# credentials
58+
Endpoint(
59+
identifier=ModuleID.credentials_and_registration,
60+
role=InterfaceRole.receiver,
61+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
62+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.credentials_and_registration.value}')
63+
),
64+
# locations
65+
Endpoint(
66+
identifier=ModuleID.locations,
67+
role=InterfaceRole.receiver,
68+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
69+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.locations.value}')
70+
),
71+
# sessions
72+
Endpoint(
73+
identifier=ModuleID.sessions,
74+
role=InterfaceRole.receiver,
75+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
76+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.sessions.value}')
77+
),
78+
# cdrs
79+
Endpoint(
80+
identifier=ModuleID.cdrs,
81+
role=InterfaceRole.receiver,
82+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
83+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.cdrs.value}')
84+
),
85+
# tariffs
86+
Endpoint(
87+
identifier=ModuleID.tariffs,
88+
role=InterfaceRole.receiver,
89+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
90+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.tariffs.value}')
91+
),
92+
# commands
93+
Endpoint(
94+
identifier=ModuleID.commands,
95+
role=InterfaceRole.sender,
96+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
97+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.commands.value}')
98+
),
99+
# tokens
100+
Endpoint(
101+
identifier=ModuleID.tokens,
102+
role=InterfaceRole.sender,
103+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp'
104+
f'/{VersionNumber.v_2_2_1.value}/{ModuleID.tokens.value}')
105+
),
106+
]
107+
}
107108
}

py_ocpi/core/push.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from py_ocpi.core.adapter import Adapter
55
from py_ocpi.core.crud import Crud
66
from py_ocpi.core.schemas import Push, PushResponse, ReceiverResponse
7-
from py_ocpi.core.utils import get_auth_token
7+
from py_ocpi.core.utils import encode_string_base64, get_auth_token
88
from py_ocpi.core.dependencies import get_crud, get_adapter
99
from py_ocpi.core.enums import ModuleID, RoleEnum
1010
from py_ocpi.core.config import settings
@@ -66,7 +66,7 @@ async def push_object(version: VersionNumber, push: Push, crud: Crud, adapter: A
6666
receiver_responses = []
6767
for receiver in push.receivers:
6868
# get client endpoints
69-
client_auth_token = f'Token {receiver.auth_token}'
69+
client_auth_token = f'Token {encode_string_base64(receiver.auth_token)}'
7070
async with httpx.AsyncClient() as client:
7171
response = await client.get(receiver.endpoints_url,
7272
headers={'authorization': client_auth_token})

py_ocpi/core/schemas.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from datetime import datetime, timezone
2-
from typing import List
2+
from typing import List, Union
33

44
from pydantic import BaseModel
55

@@ -11,7 +11,7 @@ class OCPIResponse(BaseModel):
1111
"""
1212
https://github.com/ocpi/ocpi/blob/2.2.1/transport_and_format.asciidoc#117-response-format
1313
"""
14-
data: list
14+
data: Union[list, dict]
1515
status_code: int
1616
status_message: String(255)
1717
timestamp: DateTime = str(datetime.now(timezone.utc))

py_ocpi/core/utils.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import urllib
2+
import base64
23

34
from fastapi import Response, Request
45
from pydantic import BaseModel
@@ -18,7 +19,10 @@ def set_pagination_headers(response: Response, link: str, total: int, limit: int
1819
def get_auth_token(request: Request) -> str:
1920
headers = request.headers
2021
headers_token = headers.get('authorization', 'Token Null')
21-
return headers_token.split()[1]
22+
token = headers_token.split()[1]
23+
if token == 'Null': # nosec
24+
return None
25+
return decode_string_base64(token)
2226

2327

2428
async def get_list(response: Response, filters: dict, module: ModuleID, role: RoleEnum,
@@ -40,3 +44,13 @@ async def get_list(response: Response, filters: dict, module: ModuleID, role: Ro
4044
def partially_update_attributes(instance: BaseModel, attributes: dict):
4145
for key, value in attributes.items():
4246
setattr(instance, key, value)
47+
48+
49+
def encode_string_base64(input: str) -> str:
50+
input_bytes = base64.b64encode(bytes(input, 'utf-8'))
51+
return input_bytes.decode('utf-8')
52+
53+
54+
def decode_string_base64(input: str) -> str:
55+
input_bytes = base64.b64decode(bytes(input, 'utf-8'))
56+
return input_bytes.decode('utf-8')

py_ocpi/main.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
from fastapi.middleware.cors import CORSMiddleware
66
from pydantic import ValidationError
77
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
8+
from py_ocpi.core.endpoints import ENDPOINTS
89

910
from py_ocpi.modules.versions.api import router as versions_router, versions_v_2_2_1_router
1011
from py_ocpi.modules.versions.enums import VersionNumber
1112
from py_ocpi.modules.versions.schemas import Version
12-
from py_ocpi.core.dependencies import get_crud, get_adapter, get_versions
13+
from py_ocpi.core.dependencies import get_crud, get_adapter, get_versions, get_endpoints
1314
from py_ocpi.core import status
1415
from py_ocpi.core.enums import RoleEnum
1516
from py_ocpi.core.config import settings
@@ -82,40 +83,38 @@ def get_application(
8283
)
8384

8485
versions = []
86+
version_endpoints = {}
8587

8688
if VersionNumber.v_2_2_1 in version_numbers:
8789
_app.include_router(
8890
versions_v_2_2_1_router,
8991
prefix=f'/{settings.OCPI_PREFIX}',
9092
)
9193

94+
versions.append(
95+
Version(
96+
version=VersionNumber.v_2_2_1,
97+
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/{VersionNumber.v_2_2_1.value}/details')
98+
).dict(),
99+
)
100+
101+
version_endpoints[VersionNumber.v_2_2_1] = []
102+
92103
if RoleEnum.cpo in roles:
93104
_app.include_router(
94105
v_2_2_1_cpo_router,
95-
prefix=f'/{settings.OCPI_PREFIX}/cpo/{VersionNumber.v_2_2_1}',
106+
prefix=f'/{settings.OCPI_PREFIX}/cpo/{VersionNumber.v_2_2_1.value}',
96107
tags=['CPO']
97108
)
98-
99-
versions.append(
100-
Version(
101-
version=VersionNumber.v_2_2_1,
102-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo/{VersionNumber.v_2_2_1}')
103-
).dict(),
104-
)
109+
version_endpoints[VersionNumber.v_2_2_1] += ENDPOINTS[VersionNumber.v_2_2_1][RoleEnum.cpo]
105110

106111
if RoleEnum.emsp in roles:
107112
_app.include_router(
108113
v_2_2_1_emsp_router,
109-
prefix=f'/{settings.OCPI_PREFIX}/emsp/{VersionNumber.v_2_2_1}',
114+
prefix=f'/{settings.OCPI_PREFIX}/emsp/{VersionNumber.v_2_2_1.value}',
110115
tags=['EMSP']
111116
)
112-
113-
versions.append(
114-
Version(
115-
version=VersionNumber.v_2_2_1,
116-
url=URL(f'https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp/{VersionNumber.v_2_2_1}')
117-
).dict(),
118-
)
117+
version_endpoints[VersionNumber.v_2_2_1] += ENDPOINTS[VersionNumber.v_2_2_1][RoleEnum.emsp]
119118

120119
def override_get_crud():
121120
return crud
@@ -132,4 +131,9 @@ def override_get_versions():
132131

133132
_app.dependency_overrides[get_versions] = override_get_versions
134133

134+
def override_get_endpoints():
135+
return version_endpoints
136+
137+
_app.dependency_overrides[get_endpoints] = override_get_endpoints
138+
135139
return _app

py_ocpi/modules/cdrs/v_2_2_1/schemas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class ChargingPeriod(BaseModel):
4343
https://github.com/ocpi/ocpi/blob/2.2.1/mod_cdrs.asciidoc#146-chargingperiod-class
4444
"""
4545
start_date_time: DateTime
46-
diemnsions: List[CdrDimension]
46+
dimensions: List[CdrDimension]
4747
tariff_id: Optional[CiString(36)]
4848

4949

0 commit comments

Comments
 (0)