mms-client 1.7.0__tar.gz → 1.8.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mms_client-1.7.0 → mms_client-1.8.1}/PKG-INFO +6 -1
- {mms_client-1.7.0 → mms_client-1.8.1}/README.md +4 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/pyproject.toml +2 -2
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/services/base.py +81 -52
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/services/market.py +28 -10
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/services/registration.py +12 -9
- mms_client-1.8.1/src/mms_client/services/report.py +161 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/base.py +52 -9
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/enums.py +34 -0
- mms_client-1.8.1/src/mms_client/types/report.py +474 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/resource.py +4 -34
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/transport.py +1 -1
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/utils/serialization.py +111 -42
- mms_client-1.7.0/src/mms_client/services/report.py +0 -18
- {mms_client-1.7.0 → mms_client-1.8.1}/LICENSE +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/__init__.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/client.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/py.typed +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/schemas/wsdl/mi-web-service-jbms.wsdl +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/schemas/wsdl/omi-web-service.wsdl +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/schemas/xsd/mi-market.xsd +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/schemas/xsd/mi-outbnd-reports.xsd +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/schemas/xsd/mi-report.xsd +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/schemas/xsd/mpr.xsd +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/schemas/xsd/omi.xsd +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/security/__init__.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/security/certs.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/security/crypto.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/services/__init__.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/services/omi.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/__init__.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/award.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/fields.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/market.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/offer.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/registration.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/types/reserve.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/utils/__init__.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/utils/auditing.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/utils/errors.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/utils/multipart_transport.py +0 -0
- {mms_client-1.7.0 → mms_client-1.8.1}/src/mms_client/utils/web.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mms-client
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.1
|
|
4
4
|
Summary: API client for accessing the MMS
|
|
5
5
|
Home-page: https://github.com/ElectroRoute-Japan/mms-client
|
|
6
6
|
Author: Ryan Wood
|
|
@@ -24,6 +24,7 @@ Requires-Dist: lxml (>=5.1.0,<6.0.0)
|
|
|
24
24
|
Requires-Dist: pendulum (>=3.0.0,<4.0.0)
|
|
25
25
|
Requires-Dist: pycryptodomex (>=3.20.0,<4.0.0)
|
|
26
26
|
Requires-Dist: pydantic (>=2.6.3,<3.0.0)
|
|
27
|
+
Requires-Dist: pydantic-extra-types (>=2.7.0,<3.0.0)
|
|
27
28
|
Requires-Dist: pydantic-xml (>=2.9.0,<3.0.0)
|
|
28
29
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
29
30
|
Requires-Dist: requests-pkcs12 (>=1.24,<2.0)
|
|
@@ -220,6 +221,10 @@ This client is not complete. Currently, it supports the following endpoints:
|
|
|
220
221
|
- MarketQuery_AwardResultsQuery
|
|
221
222
|
- RegistrationSubmit_Resource
|
|
222
223
|
- RegistrationQuery_Resource
|
|
224
|
+
- ReportCreateRequest
|
|
225
|
+
- ReportListRequest
|
|
226
|
+
- ReportDownloadRequestTrnID
|
|
227
|
+
- BSP_ResourceList
|
|
223
228
|
|
|
224
229
|
We can add support for additional endpoints as time goes on, and independent contribution is, of course, welcome. However, support for attachments is currently limited because none of the endpoints we support currently require them. We have implemented attachment support up to the client level, but we haven't developed an architecture for submitting them through an endpoint yet.
|
|
225
230
|
|
|
@@ -187,6 +187,10 @@ This client is not complete. Currently, it supports the following endpoints:
|
|
|
187
187
|
- MarketQuery_AwardResultsQuery
|
|
188
188
|
- RegistrationSubmit_Resource
|
|
189
189
|
- RegistrationQuery_Resource
|
|
190
|
+
- ReportCreateRequest
|
|
191
|
+
- ReportListRequest
|
|
192
|
+
- ReportDownloadRequestTrnID
|
|
193
|
+
- BSP_ResourceList
|
|
190
194
|
|
|
191
195
|
We can add support for additional endpoints as time goes on, and independent contribution is, of course, welcome. However, support for attachments is currently limited because none of the endpoints we support currently require them. We have implemented attachment support up to the client level, but we haven't developed an architecture for submitting them through an endpoint yet.
|
|
192
196
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "mms_client"
|
|
3
|
-
version = "v1.
|
|
3
|
+
version = "v1.8.1"
|
|
4
4
|
description = "API client for accessing the MMS"
|
|
5
5
|
authors = ["Ryan Wood <ryan.wood@electroroute.co.jp>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -33,6 +33,7 @@ pydantic-xml = "^2.9.0"
|
|
|
33
33
|
lxml = "^5.1.0"
|
|
34
34
|
backoff = "^2.2.1"
|
|
35
35
|
pycryptodomex = "^3.20.0"
|
|
36
|
+
pydantic-extra-types = "^2.7.0"
|
|
36
37
|
|
|
37
38
|
[tool.poetry.group.dev.dependencies]
|
|
38
39
|
black = "^24.2.0"
|
|
@@ -49,7 +50,6 @@ pytest-mock = "^3.12.0"
|
|
|
49
50
|
mock = "^5.1.0"
|
|
50
51
|
lxml-stubs = "^0.5.1"
|
|
51
52
|
pyfakefs = "^5.3.5"
|
|
52
|
-
pydantic-extra-types = "^2.6.0"
|
|
53
53
|
responses = "^0.25.0"
|
|
54
54
|
types-urllib3 = "^1.26.25.14"
|
|
55
55
|
|
|
@@ -53,26 +53,33 @@ class ServiceConfiguration:
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
@dataclass
|
|
56
|
-
class EndpointConfiguration(Generic[E, P]):
|
|
56
|
+
class EndpointConfiguration(Generic[E, P]): # pylint: disable=too-many-instance-attributes
|
|
57
57
|
"""Configuration for an endpoint on the MMS server."""
|
|
58
58
|
|
|
59
59
|
# The name of the endpoint
|
|
60
60
|
name: str
|
|
61
61
|
|
|
62
|
-
# The allowed client types for the endpoint
|
|
63
|
-
allowed_clients: Optional[List[ClientType]]
|
|
64
|
-
|
|
65
62
|
# The service for the endpoint
|
|
66
63
|
service: ServiceConfiguration
|
|
67
64
|
|
|
68
65
|
# The type of request to submit to the MMS server
|
|
69
66
|
request_type: RequestType
|
|
70
67
|
|
|
68
|
+
# The allowed client types for the endpoint
|
|
69
|
+
allowed_clients: Optional[List[ClientType]] = None
|
|
70
|
+
|
|
71
71
|
# The type of payload to expect in the response
|
|
72
|
-
response_envelope_type: Optional[Type[E]]
|
|
72
|
+
response_envelope_type: Optional[Type[E]] = None
|
|
73
73
|
|
|
74
74
|
# The type of data to expect in the response
|
|
75
|
-
response_data_type: Optional[Type[P]]
|
|
75
|
+
response_data_type: Optional[Type[P]] = None
|
|
76
|
+
|
|
77
|
+
# Whether the endpoint is for a report request
|
|
78
|
+
for_report: bool = False
|
|
79
|
+
|
|
80
|
+
# An optional serializer used to deserialize the response data for the service. This is only used for report
|
|
81
|
+
# requests because they have a separate XSD file for responses.
|
|
82
|
+
serializer: Optional[Serializer] = None
|
|
76
83
|
|
|
77
84
|
|
|
78
85
|
class ClientProto(Protocol):
|
|
@@ -86,6 +93,10 @@ class ClientProto(Protocol):
|
|
|
86
93
|
def user(self) -> str:
|
|
87
94
|
"""Return the user name of the person making the request."""
|
|
88
95
|
|
|
96
|
+
@property
|
|
97
|
+
def client_type(self) -> ClientType:
|
|
98
|
+
"""Return the type of client to use for making requests to the MMS server."""
|
|
99
|
+
|
|
89
100
|
def verify_audience(self, config: EndpointConfiguration) -> None:
|
|
90
101
|
"""Verify that the client type is allowed.
|
|
91
102
|
|
|
@@ -132,14 +143,7 @@ class ClientProto(Protocol):
|
|
|
132
143
|
"""
|
|
133
144
|
|
|
134
145
|
|
|
135
|
-
def mms_endpoint(
|
|
136
|
-
name: str,
|
|
137
|
-
service: ServiceConfiguration,
|
|
138
|
-
request_type: RequestType,
|
|
139
|
-
allowed_clients: Optional[List[ClientType]] = None,
|
|
140
|
-
resp_envelope_type: Optional[Type[E]] = None,
|
|
141
|
-
resp_data_type: Optional[Type[P]] = None,
|
|
142
|
-
):
|
|
146
|
+
def mms_endpoint(**kwargs):
|
|
143
147
|
"""Create a decorator for an MMS endpoint.
|
|
144
148
|
|
|
145
149
|
This decorator is used to mark a method as an MMS endpoint. It will add the endpoint configuration to the function
|
|
@@ -152,13 +156,16 @@ def mms_endpoint(
|
|
|
152
156
|
request_type (RequestType): The type of request to submit to the MMS server.
|
|
153
157
|
allowed_clients (List[ClientType]): The types of clients that are allowed to access the endpoint. If this is not
|
|
154
158
|
provided, then any client will be allowed.
|
|
155
|
-
|
|
159
|
+
response_envelope_type (Type[E]): The type of payload to expect in the response. If this is not provided, then the
|
|
156
160
|
response envelope will be assumed to have the same type as the request envelope.
|
|
157
|
-
|
|
161
|
+
response_data_type (Type[P]): The type of data to expect in the response. If this is not provided, then the
|
|
158
162
|
response data will be assumed to have the same type as the request data.
|
|
163
|
+
for_report (bool): If True, the endpoint is for a report request.
|
|
164
|
+
serializer (Serializer): The serializer to use for responses from the endpoint. Overrides the default
|
|
165
|
+
serializer for the service.
|
|
159
166
|
"""
|
|
160
167
|
# First, create the endpoint configuration from the given parameters
|
|
161
|
-
config = EndpointConfiguration(
|
|
168
|
+
config = EndpointConfiguration(**kwargs)
|
|
162
169
|
|
|
163
170
|
# Next, create a decorator that will add the endpoint configuration to the function
|
|
164
171
|
def decorator(func):
|
|
@@ -169,10 +176,18 @@ def mms_endpoint(
|
|
|
169
176
|
self.verify_audience(config)
|
|
170
177
|
|
|
171
178
|
# Next, call the wrapped function to get the envelope
|
|
172
|
-
|
|
179
|
+
result = func(self, *args, **kwargs)
|
|
180
|
+
if isinstance(result, tuple):
|
|
181
|
+
envelope, callback = result
|
|
182
|
+
else:
|
|
183
|
+
envelope, callback = result, None
|
|
173
184
|
|
|
174
185
|
# Now, submit the request to the MMS server and get the response
|
|
175
|
-
resp,
|
|
186
|
+
resp, attachments = self.request_one(envelope, args[0], config)
|
|
187
|
+
|
|
188
|
+
# Call the callback function if it was provided
|
|
189
|
+
if callback:
|
|
190
|
+
callback(resp, attachments)
|
|
176
191
|
|
|
177
192
|
# Finally, extract the data from the response and return it
|
|
178
193
|
logger.info(f"{config.name}: Returning {type(resp.data).__name__} data.")
|
|
@@ -184,14 +199,7 @@ def mms_endpoint(
|
|
|
184
199
|
return decorator
|
|
185
200
|
|
|
186
201
|
|
|
187
|
-
def mms_multi_endpoint(
|
|
188
|
-
name: str,
|
|
189
|
-
service: ServiceConfiguration,
|
|
190
|
-
request_type: RequestType,
|
|
191
|
-
allowed_clients: Optional[List[ClientType]] = None,
|
|
192
|
-
resp_envelope_type: Optional[Type[E]] = None,
|
|
193
|
-
resp_data_type: Optional[Type[P]] = None,
|
|
194
|
-
):
|
|
202
|
+
def mms_multi_endpoint(**kwargs):
|
|
195
203
|
"""Create a decorator for an MMS multi-response endpoint.
|
|
196
204
|
|
|
197
205
|
This decorator is used to mark a method as an MMS multi-response endpoint. It will add the endpoint configuration to
|
|
@@ -205,17 +213,20 @@ def mms_multi_endpoint(
|
|
|
205
213
|
request_type (RequestType): The type of request to submit to the MMS server.
|
|
206
214
|
allowed_clients (List[ClientType]): The types of clients that are allowed to access the endpoint. If this is not
|
|
207
215
|
provided, then any client will be allowed.
|
|
208
|
-
|
|
216
|
+
response_envelope_type (Type[E]): The type of payload to expect in the response. If this is not provided, then
|
|
209
217
|
the response envelope will be assumed to have the same type as the request
|
|
210
218
|
envelope.
|
|
211
|
-
|
|
219
|
+
response_data_type (Type[P]): The type of data to expect in the response. If this is not provided, then the
|
|
212
220
|
response data will be assumed to have the same type as the request data. Note,
|
|
213
221
|
that this is not intended to account for the expected sequence type of the
|
|
214
222
|
response data. That is already handled in the wrapped function, so this should
|
|
215
223
|
only be set if the inner data type being returned differs from what was sent.
|
|
224
|
+
for_report (bool): If True, the endpoint is for a report request.
|
|
225
|
+
serializer (Serializer): The serializer to use for responses from the endpoint. Overrides the default
|
|
226
|
+
serializer for the service.
|
|
216
227
|
"""
|
|
217
228
|
# First, create the endpoint configuration from the given parameters
|
|
218
|
-
config = EndpointConfiguration(
|
|
229
|
+
config = EndpointConfiguration(**kwargs)
|
|
219
230
|
|
|
220
231
|
# Next, create a decorator that will add the endpoint configuration to the function
|
|
221
232
|
def decorator(func):
|
|
@@ -226,10 +237,18 @@ def mms_multi_endpoint(
|
|
|
226
237
|
self.verify_audience(config)
|
|
227
238
|
|
|
228
239
|
# Next, call the wrapped function to get the envelope
|
|
229
|
-
|
|
240
|
+
result = func(self, *args, **kwargs)
|
|
241
|
+
if isinstance(result, tuple):
|
|
242
|
+
envelope, callback = result # pragma: no cover
|
|
243
|
+
else:
|
|
244
|
+
envelope, callback = result, None
|
|
230
245
|
|
|
231
246
|
# Now, submit the request to the MMS server and get the response
|
|
232
|
-
resp,
|
|
247
|
+
resp, attachments = self.request_many(envelope, args[0], config)
|
|
248
|
+
|
|
249
|
+
# Call the callback function if it was provided
|
|
250
|
+
if callback:
|
|
251
|
+
callback(resp, attachments) # pragma: no cover
|
|
233
252
|
|
|
234
253
|
# Finally, extract the data from the response and return it
|
|
235
254
|
logger.info(f"{config.name}: Returning {len(resp.data)} item(s).")
|
|
@@ -299,6 +318,11 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
299
318
|
"""Return the user name of the person making the request."""
|
|
300
319
|
return self._user
|
|
301
320
|
|
|
321
|
+
@property
|
|
322
|
+
def client_type(self) -> ClientType:
|
|
323
|
+
"""Return the type of client to use for making requests to the MMS server."""
|
|
324
|
+
return self._client_type
|
|
325
|
+
|
|
302
326
|
def verify_audience(self, config: EndpointConfiguration) -> None:
|
|
303
327
|
"""Verify that the client type is allowed.
|
|
304
328
|
|
|
@@ -341,7 +365,7 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
341
365
|
f"{config.name}: Starting request. Envelope: {type(envelope).__name__}, Data: {type(payload).__name__}",
|
|
342
366
|
)
|
|
343
367
|
request = self._to_mms_request(
|
|
344
|
-
wrapper, config.request_type, config.service.serializer.serialize(envelope, payload)
|
|
368
|
+
wrapper, config.request_type, config.service.serializer.serialize(envelope, payload, config.for_report)
|
|
345
369
|
)
|
|
346
370
|
|
|
347
371
|
# Next, submit the request to the MMS server and get and verify the response.
|
|
@@ -354,7 +378,8 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
354
378
|
# Finally, deserialize and verify the response
|
|
355
379
|
envelope_type = config.response_envelope_type or type(envelope)
|
|
356
380
|
data_type = config.response_data_type or type(payload)
|
|
357
|
-
|
|
381
|
+
deserializer = config.serializer or config.service.serializer
|
|
382
|
+
data: Response[E, P] = deserializer.deserialize(resp.payload, envelope_type, data_type, config.for_report)
|
|
358
383
|
self._verify_response(data, config)
|
|
359
384
|
|
|
360
385
|
# Return the response data and any attachments
|
|
@@ -391,9 +416,11 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
391
416
|
),
|
|
392
417
|
)
|
|
393
418
|
serialized = (
|
|
394
|
-
config.service.serializer.serialize_multi(
|
|
419
|
+
config.service.serializer.serialize_multi(
|
|
420
|
+
envelope, payload, data_type, config.for_report # type: ignore[arg-type]
|
|
421
|
+
)
|
|
395
422
|
if is_list
|
|
396
|
-
else config.service.serializer.serialize(envelope, payload) # type: ignore[type-var]
|
|
423
|
+
else config.service.serializer.serialize(envelope, payload, config.for_report) # type: ignore[type-var]
|
|
397
424
|
)
|
|
398
425
|
request = self._to_mms_request(wrapper, config.request_type, serialized)
|
|
399
426
|
|
|
@@ -407,10 +434,12 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
407
434
|
# Finally, deserialize and verify the response
|
|
408
435
|
envelope_type = config.response_envelope_type or type(envelope)
|
|
409
436
|
data_type = config.response_data_type or data_type
|
|
410
|
-
|
|
437
|
+
deserializer = config.serializer or config.service.serializer
|
|
438
|
+
data: MultiResponse[E, P] = deserializer.deserialize_multi(
|
|
411
439
|
resp.payload,
|
|
412
440
|
envelope_type,
|
|
413
441
|
data_type, # type: ignore[arg-type]
|
|
442
|
+
config.for_report,
|
|
414
443
|
)
|
|
415
444
|
self._verify_multi_response(data, config)
|
|
416
445
|
|
|
@@ -574,18 +603,18 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
574
603
|
Returns: True to indicate that the response is valid, False otherwise.
|
|
575
604
|
"""
|
|
576
605
|
# Log the request's processing statistics
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
606
|
+
if resp.statistics is not None:
|
|
607
|
+
logger.info(
|
|
608
|
+
f"{config.name} ({resp.statistics.timestamp_xml}): Recieved {resp.statistics.received}, "
|
|
609
|
+
f"Valid: {resp.statistics.valid}, Invalid: {resp.statistics.invalid}, "
|
|
610
|
+
f"Successful: {resp.statistics.successful}, Unsuccessful: {resp.statistics.unsuccessful} "
|
|
611
|
+
f"in {resp.statistics.time_ms}ms"
|
|
612
|
+
)
|
|
583
613
|
|
|
584
614
|
# Check if the response is invalid and if the envelope had any validation issues. If not, then we have a
|
|
585
615
|
# valid base response so return True. Otherwise, return False.
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
)
|
|
616
|
+
is_invalid = resp.statistics is not None and resp.statistics.invalid
|
|
617
|
+
return not is_invalid and self._verify_response_common(config, type(resp.envelope), resp.envelope_validation)
|
|
589
618
|
|
|
590
619
|
def _verify_messages(self, config: EndpointConfiguration, resp: BaseResponse[E]) -> None:
|
|
591
620
|
"""Verify the messages in the given response.
|
|
@@ -595,12 +624,12 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
595
624
|
resp (BaseResponse): The response to verify.
|
|
596
625
|
"""
|
|
597
626
|
for path, messages in resp.messages.items():
|
|
598
|
-
for info in messages.information:
|
|
599
|
-
logger.info(f"{config.name} - {path}: {info
|
|
600
|
-
for warning in messages.warnings:
|
|
601
|
-
logger.warning(f"{config.name} - {path}: {warning
|
|
602
|
-
for error in messages.errors:
|
|
603
|
-
logger.error(f"{config.name} - {path}: {error
|
|
627
|
+
for info in messages.information: # type: ignore[union-attr]
|
|
628
|
+
logger.info(f"{config.name} - {path}: {info}")
|
|
629
|
+
for warning in messages.warnings: # type: ignore[union-attr]
|
|
630
|
+
logger.warning(f"{config.name} - {path}: {warning}")
|
|
631
|
+
for error in messages.errors: # type: ignore[union-attr]
|
|
632
|
+
logger.error(f"{config.name} - {path}: {error}")
|
|
604
633
|
|
|
605
634
|
def _verify_response_common(
|
|
606
635
|
self, config: EndpointConfiguration, payload_type: type, resp: ResponseCommon, index: Optional[int] = None
|
|
@@ -37,11 +37,11 @@ class MarketClientMixin: # pylint: disable=unused-argument
|
|
|
37
37
|
config = ServiceConfiguration(Interface.MI, Serializer(SchemaType.MARKET, "MarketData"))
|
|
38
38
|
|
|
39
39
|
@mms_endpoint(
|
|
40
|
-
"MarketQuery_ReserveRequirementQuery",
|
|
41
|
-
config,
|
|
42
|
-
RequestType.INFO,
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
name="MarketQuery_ReserveRequirementQuery",
|
|
41
|
+
service=config,
|
|
42
|
+
request_type=RequestType.INFO,
|
|
43
|
+
response_envelope_type=MarketSubmit,
|
|
44
|
+
response_data_type=ReserveRequirement,
|
|
45
45
|
)
|
|
46
46
|
def query_reserve_requirements(
|
|
47
47
|
self: ClientProto, request: ReserveRequirementQuery, days: int, date: Optional[Date] = None
|
|
@@ -66,7 +66,9 @@ class MarketClientMixin: # pylint: disable=unused-argument
|
|
|
66
66
|
days=days,
|
|
67
67
|
)
|
|
68
68
|
|
|
69
|
-
@mms_endpoint(
|
|
69
|
+
@mms_endpoint(
|
|
70
|
+
name="MarketSubmit_OfferData", service=config, request_type=RequestType.MARKET, allowed_clients=[ClientType.BSP]
|
|
71
|
+
)
|
|
70
72
|
def put_offer(
|
|
71
73
|
self: ClientProto, request: OfferData, market_type: MarketType, days: int, date: Optional[Date] = None
|
|
72
74
|
) -> OfferData:
|
|
@@ -92,7 +94,9 @@ class MarketClientMixin: # pylint: disable=unused-argument
|
|
|
92
94
|
days=days,
|
|
93
95
|
)
|
|
94
96
|
|
|
95
|
-
@mms_multi_endpoint(
|
|
97
|
+
@mms_multi_endpoint(
|
|
98
|
+
name="MarketSubmit_OfferData", service=config, request_type=RequestType.MARKET, allowed_clients=[ClientType.BSP]
|
|
99
|
+
)
|
|
96
100
|
def put_offers(
|
|
97
101
|
self: ClientProto, requests: List[OfferData], market_type: MarketType, days: int, date: Optional[Date] = None
|
|
98
102
|
) -> List[OfferData]:
|
|
@@ -119,7 +123,11 @@ class MarketClientMixin: # pylint: disable=unused-argument
|
|
|
119
123
|
)
|
|
120
124
|
|
|
121
125
|
@mms_multi_endpoint(
|
|
122
|
-
"MarketQuery_OfferQuery",
|
|
126
|
+
name="MarketQuery_OfferQuery",
|
|
127
|
+
service=config,
|
|
128
|
+
request_type=RequestType.MARKET,
|
|
129
|
+
response_envelope_type=MarketSubmit,
|
|
130
|
+
response_data_type=OfferData,
|
|
123
131
|
)
|
|
124
132
|
def query_offers(self: ClientProto, request: OfferQuery, days: int, date: Optional[Date] = None) -> List[OfferData]:
|
|
125
133
|
"""Query the MMS server for offers.
|
|
@@ -142,7 +150,12 @@ class MarketClientMixin: # pylint: disable=unused-argument
|
|
|
142
150
|
days=days,
|
|
143
151
|
)
|
|
144
152
|
|
|
145
|
-
@mms_endpoint(
|
|
153
|
+
@mms_endpoint(
|
|
154
|
+
name="MarketCancel_OfferCancel",
|
|
155
|
+
service=config,
|
|
156
|
+
request_type=RequestType.MARKET,
|
|
157
|
+
allowed_clients=[ClientType.BSP],
|
|
158
|
+
)
|
|
146
159
|
def cancel_offer(
|
|
147
160
|
self: ClientProto, request: OfferCancel, market_type: MarketType, days: int, date: Optional[Date] = None
|
|
148
161
|
) -> OfferCancel:
|
|
@@ -168,7 +181,12 @@ class MarketClientMixin: # pylint: disable=unused-argument
|
|
|
168
181
|
days=days,
|
|
169
182
|
)
|
|
170
183
|
|
|
171
|
-
@mms_endpoint(
|
|
184
|
+
@mms_endpoint(
|
|
185
|
+
name="MarketQuery_AwardResultsQuery",
|
|
186
|
+
service=config,
|
|
187
|
+
request_type=RequestType.MARKET,
|
|
188
|
+
response_data_type=AwardResponse,
|
|
189
|
+
)
|
|
172
190
|
def query_awards(self: ClientProto, request: AwardQuery, days: int, date: Optional[Date] = None) -> AwardResponse:
|
|
173
191
|
"""Query the MMS server for award results.
|
|
174
192
|
|
|
@@ -10,7 +10,6 @@ from mms_client.services.base import ServiceConfiguration
|
|
|
10
10
|
from mms_client.services.base import mms_endpoint
|
|
11
11
|
from mms_client.services.base import mms_multi_endpoint
|
|
12
12
|
from mms_client.types.registration import QueryAction
|
|
13
|
-
from mms_client.types.registration import QueryType
|
|
14
13
|
from mms_client.types.registration import RegistrationQuery
|
|
15
14
|
from mms_client.types.registration import RegistrationSubmit
|
|
16
15
|
from mms_client.types.resource import ResourceData
|
|
@@ -31,7 +30,12 @@ class RegistrationClientMixin: # pylint: disable=unused-argument
|
|
|
31
30
|
# The configuration for the registration service
|
|
32
31
|
config = ServiceConfiguration(Interface.MI, Serializer(SchemaType.REGISTRATION, "RegistrationData"))
|
|
33
32
|
|
|
34
|
-
@mms_endpoint(
|
|
33
|
+
@mms_endpoint(
|
|
34
|
+
name="RegistrationSubmit_Resource",
|
|
35
|
+
service=config,
|
|
36
|
+
request_type=RequestType.REGISTRATION,
|
|
37
|
+
allowed_clients=[ClientType.BSP],
|
|
38
|
+
)
|
|
35
39
|
def put_resource(self: ClientProto, request: ResourceData) -> ResourceData:
|
|
36
40
|
"""Submit a new resource to the MMS server.
|
|
37
41
|
|
|
@@ -51,12 +55,12 @@ class RegistrationClientMixin: # pylint: disable=unused-argument
|
|
|
51
55
|
return RegistrationSubmit() # type: ignore[return-value]
|
|
52
56
|
|
|
53
57
|
@mms_multi_endpoint(
|
|
54
|
-
"RegistrationQuery_Resource",
|
|
55
|
-
config,
|
|
56
|
-
RequestType.REGISTRATION,
|
|
58
|
+
name="RegistrationQuery_Resource",
|
|
59
|
+
service=config,
|
|
60
|
+
request_type=RequestType.REGISTRATION,
|
|
57
61
|
allowed_clients=[ClientType.BSP, ClientType.TSO],
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
response_envelope_type=RegistrationSubmit,
|
|
63
|
+
response_data_type=ResourceData,
|
|
60
64
|
)
|
|
61
65
|
def query_resources(
|
|
62
66
|
self: ClientProto, request: ResourceQuery, action: QueryAction, date: Optional[Date] = None
|
|
@@ -79,6 +83,5 @@ class RegistrationClientMixin: # pylint: disable=unused-argument
|
|
|
79
83
|
# Inject our parameters into the query and return it.
|
|
80
84
|
return RegistrationQuery( # type: ignore[return-value]
|
|
81
85
|
action=action,
|
|
82
|
-
|
|
83
|
-
date=date or Date.today(),
|
|
86
|
+
date=date,
|
|
84
87
|
)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Contains the client layer for making report requests to the MMS server."""
|
|
2
|
+
|
|
3
|
+
from logging import getLogger
|
|
4
|
+
from typing import Dict
|
|
5
|
+
from typing import List
|
|
6
|
+
from typing import Type
|
|
7
|
+
from typing import TypeVar
|
|
8
|
+
|
|
9
|
+
from mms_client.services.base import ClientProto
|
|
10
|
+
from mms_client.services.base import ServiceConfiguration
|
|
11
|
+
from mms_client.services.base import mms_endpoint
|
|
12
|
+
from mms_client.services.base import mms_multi_endpoint
|
|
13
|
+
from mms_client.types.base import Response
|
|
14
|
+
from mms_client.types.report import ApplicationType
|
|
15
|
+
from mms_client.types.report import BSPResourceListItem
|
|
16
|
+
from mms_client.types.report import ListReportRequest
|
|
17
|
+
from mms_client.types.report import ListReportResponse
|
|
18
|
+
from mms_client.types.report import NewReportRequest
|
|
19
|
+
from mms_client.types.report import NewReportResponse
|
|
20
|
+
from mms_client.types.report import OutboundData
|
|
21
|
+
from mms_client.types.report import ReportBase
|
|
22
|
+
from mms_client.types.report import ReportDownloadRequestTrnID
|
|
23
|
+
from mms_client.types.report import ReportLineBase
|
|
24
|
+
from mms_client.types.transport import RequestType
|
|
25
|
+
from mms_client.utils.serialization import SchemaType
|
|
26
|
+
from mms_client.utils.serialization import Serializer
|
|
27
|
+
from mms_client.utils.web import ClientType
|
|
28
|
+
from mms_client.utils.web import Interface
|
|
29
|
+
|
|
30
|
+
# Set the default logger for the MMS client
|
|
31
|
+
logger = getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# The type variable we'll use to represent report line items
|
|
35
|
+
R = TypeVar("R", bound=ReportLineBase)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def attach_transaction_id(
|
|
39
|
+
resp: Response[ReportBase, NewReportResponse],
|
|
40
|
+
attachments: Dict[str, bytes], # pylint: disable=unused-argument
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Attach the transaction ID to the response.
|
|
43
|
+
|
|
44
|
+
Arguments:
|
|
45
|
+
resp (Response[ReportBase, NewReportResponse]): The MMS response.
|
|
46
|
+
attachments (Dict[str, bytes]): Attachements to the response.
|
|
47
|
+
"""
|
|
48
|
+
if resp.data and resp.statistics is not None:
|
|
49
|
+
resp.data.transaction_id = resp.statistics.transaction_id or ""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def report_getter_factory(config: ServiceConfiguration, resp_data_type: Type[R], allowed_clients: List[ClientType]):
|
|
53
|
+
"""Create a function for getting a report with a transaction ID.
|
|
54
|
+
|
|
55
|
+
Arguments:
|
|
56
|
+
config (ServiceConfiguration): The configuration for the report service.
|
|
57
|
+
resp_data_type (Type[R]): The type of report data to return.
|
|
58
|
+
allowed_clients (List[ClientType]): The allowed client types for the report service.
|
|
59
|
+
|
|
60
|
+
Returns: A function for getting a report with a transaction ID.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
@mms_multi_endpoint(
|
|
64
|
+
name="ReportDownloadRequestTrnID",
|
|
65
|
+
service=config,
|
|
66
|
+
request_type=RequestType.REPORT,
|
|
67
|
+
allowed_clients=allowed_clients,
|
|
68
|
+
response_envelope_type=OutboundData,
|
|
69
|
+
response_data_type=resp_data_type,
|
|
70
|
+
serializer=Serializer(SchemaType.REPORT_RESPONSE, "OutboundData"),
|
|
71
|
+
for_report=True,
|
|
72
|
+
)
|
|
73
|
+
def get_report_with_transaction_id(
|
|
74
|
+
self: ClientProto,
|
|
75
|
+
request: ReportDownloadRequestTrnID, # pylint: disable=unused-argument
|
|
76
|
+
) -> List[resp_data_type]: # type: ignore[valid-type]
|
|
77
|
+
"""Request download of a report with a transaction ID.
|
|
78
|
+
|
|
79
|
+
This endpoint is accessible to any client, but may be rejected depending on the type of report being requested.
|
|
80
|
+
|
|
81
|
+
Arguments:
|
|
82
|
+
self (ClientProto): The client to use for making the request.
|
|
83
|
+
request (ReportDownloadRequestTrnID): The request to download the report.
|
|
84
|
+
|
|
85
|
+
Returns: The request to download the report.
|
|
86
|
+
"""
|
|
87
|
+
# NOTE: The return type does not match the method definition but the decorator will return the correct type
|
|
88
|
+
return ReportBase( # type: ignore[return-value]
|
|
89
|
+
application_type=ApplicationType.MARKET_REPORT,
|
|
90
|
+
participant=self.participant,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return get_report_with_transaction_id
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ReportClientMixin: # pylint: disable=unused-argument
|
|
97
|
+
"""Report client for the MMS server."""
|
|
98
|
+
|
|
99
|
+
# The configuration for the report service
|
|
100
|
+
config = ServiceConfiguration(
|
|
101
|
+
Interface.MI,
|
|
102
|
+
Serializer(SchemaType.REPORT, "MarketReport"),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
@mms_endpoint(
|
|
106
|
+
name="ReportCreateRequest",
|
|
107
|
+
service=config,
|
|
108
|
+
request_type=RequestType.REPORT,
|
|
109
|
+
response_envelope_type=ReportBase,
|
|
110
|
+
response_data_type=NewReportResponse,
|
|
111
|
+
for_report=True,
|
|
112
|
+
)
|
|
113
|
+
def create_report(self: ClientProto, request: NewReportRequest) -> NewReportResponse:
|
|
114
|
+
"""Request creation of a new report.
|
|
115
|
+
|
|
116
|
+
This endpoint is only accessible to BSPs.
|
|
117
|
+
|
|
118
|
+
Arguments:
|
|
119
|
+
request (NewReportRequest): The request to create the report.
|
|
120
|
+
|
|
121
|
+
Returns: The request to create the report.
|
|
122
|
+
"""
|
|
123
|
+
# If the client type is BSP, then we can't set the BSP name
|
|
124
|
+
if self.client_type == ClientType.BSP:
|
|
125
|
+
request.bsp_name = None
|
|
126
|
+
|
|
127
|
+
# NOTE: The return type does not match the method definition but the decorator will return the correct type
|
|
128
|
+
# Return the envelope and the callback function
|
|
129
|
+
return (
|
|
130
|
+
ReportBase( # type: ignore[return-value]
|
|
131
|
+
application_type=ApplicationType.MARKET_REPORT,
|
|
132
|
+
participant=self.participant,
|
|
133
|
+
),
|
|
134
|
+
attach_transaction_id,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
@mms_endpoint(
|
|
138
|
+
name="ReportListRequest",
|
|
139
|
+
service=config,
|
|
140
|
+
request_type=RequestType.REPORT,
|
|
141
|
+
response_envelope_type=ReportBase,
|
|
142
|
+
response_data_type=ListReportResponse,
|
|
143
|
+
for_report=True,
|
|
144
|
+
)
|
|
145
|
+
def list_reports(self: ClientProto, request: ListReportRequest) -> ListReportResponse:
|
|
146
|
+
"""Query the existing reports.
|
|
147
|
+
|
|
148
|
+
Arguments:
|
|
149
|
+
request (ListReportRequest): The request to query the reports.
|
|
150
|
+
|
|
151
|
+
Returns: A list of reports that match the query.
|
|
152
|
+
"""
|
|
153
|
+
# NOTE: The return type does not match the method definition but the decorator will return the correct type
|
|
154
|
+
# Return the envelope and the callback function
|
|
155
|
+
return ReportBase( # type: ignore[return-value]
|
|
156
|
+
application_type=ApplicationType.MARKET_REPORT,
|
|
157
|
+
participant=self.participant,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Define the individual report getter functions here
|
|
161
|
+
list_bsp_resources = report_getter_factory(config, BSPResourceListItem, [ClientType.BSP])
|