mms-client 1.4.0__tar.gz → 1.5.0__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.4.0 → mms_client-1.5.0}/PKG-INFO +5 -12
- {mms_client-1.4.0 → mms_client-1.5.0}/README.md +4 -11
- {mms_client-1.4.0 → mms_client-1.5.0}/pyproject.toml +1 -1
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/client.py +5 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/services/base.py +51 -63
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/services/market.py +7 -3
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/services/omi.py +5 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/services/registration.py +6 -1
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/services/report.py +5 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/utils/auditing.py +4 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/utils/errors.py +11 -2
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/utils/serialization.py +4 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/utils/web.py +46 -42
- {mms_client-1.4.0 → mms_client-1.5.0}/LICENSE +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/__init__.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/py.typed +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/schemas/wsdl/mi-web-service-jbms.wsdl +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/schemas/wsdl/omi-web-service.wsdl +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/schemas/xsd/mi-market.xsd +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/schemas/xsd/mi-outbnd-reports.xsd +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/schemas/xsd/mi-report.xsd +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/schemas/xsd/mpr.xsd +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/schemas/xsd/omi.xsd +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/security/__init__.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/security/certs.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/security/crypto.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/services/__init__.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/types/__init__.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/types/award.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/types/base.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/types/enums.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/types/fields.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/types/market.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/types/offer.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/types/registration.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/types/resource.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/types/transport.py +0 -0
- {mms_client-1.4.0 → mms_client-1.5.0}/src/mms_client/utils/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mms-client
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
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
|
|
@@ -33,7 +33,7 @@ Description-Content-Type: text/markdown
|
|
|
33
33
|
[](https://github.com/ElectroRoute-Japan/mms-client/actions)
|
|
34
34
|
|
|
35
35
|
# Overview
|
|
36
|
-
This repository contains a Python client that is capable of communication with the Market Management System (MMS), which handles requests related to Flex or Virtual Power Plant (VPP) trading. The underlying library
|
|
36
|
+
This repository contains a Python client that is capable of communication with the Market Management System (MMS), which handles requests related to Flex or Virtual Power Plant (VPP) trading. The underlying library relies on SOAP communication, which is a pain to work with at the best of times. This particular SOAP API adds its own special layer of obnoxiousness, however. As such, it was deemed useful to have a client which would obfuscate most, if not all of this away from the user.
|
|
37
37
|
|
|
38
38
|
# Communication
|
|
39
39
|
The underlying API sends and receives XML documents. Each of these request or responses, which we will hereafter refer to as *outer* requests and responses, contains metadata about the request/response as well as three fields which are extremely important to successful communication with the API:
|
|
@@ -47,7 +47,9 @@ After the data has been converted and added to the outer request object, it is s
|
|
|
47
47
|
This library relies on Pydantic 2 and the pydantic-xml library for serialization/deserialization. As such, any type in this library can be converted to not only XML, but to JSON as well. This is extremely useful if you're trying to build a pass-through API service or something similar.
|
|
48
48
|
|
|
49
49
|
## Client Types
|
|
50
|
-
Clients cannot call any and all endpoints in this API, willy-nilly. Some endpoints are restricted to particular clients. At the moment, there are
|
|
50
|
+
Clients cannot call any and all endpoints in this API, willy-nilly. Some endpoints are restricted to particular clients. At the moment, there are three clients: Balance Service Providers (BSPs), Market Operators (MOs), and Transmission Service Operators (TSOs). Most likely you're operating as a BSP or MO, in which case you'll have access to most or all endpoints. However, it makes little sense for a TSO to be able to submit bids on their own power, so they are restricted to a read-only role in most cases.
|
|
51
|
+
|
|
52
|
+
Note that MOs and BSPs access the MMS service using the same endpoints, but have distinct permissions. As such, both client types are supported explicitly here.
|
|
51
53
|
|
|
52
54
|
Should you request an endpoint which you are not authorized for, you will receive an `mms_client.utils.errors.AudienceError`.
|
|
53
55
|
|
|
@@ -184,15 +186,6 @@ If you're connecting as a market operator (MO), you can connect in admin mode:
|
|
|
184
186
|
client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert, is_admin=True)
|
|
185
187
|
```
|
|
186
188
|
|
|
187
|
-
## Log Settings
|
|
188
|
-
The client also supports injection of a custom logger. The default logger is named "MMS Client".
|
|
189
|
-
|
|
190
|
-
```python
|
|
191
|
-
client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert, logger=my_logger)
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
The client currently logs a number of informational, debug and error messages. You can freely change the logging level yourself.
|
|
195
|
-
|
|
196
189
|
## Auditing XML Requests & Responses
|
|
197
190
|
A common requirement for this sort of library is recording or saving the raw XML requests and responses for audit/logging purposes. This library supports this workflow through the `mms_client.utils.auditing.AuditPlugin` object. This object intercepts the XML request at the Zeep client level right before it is sent to the MMS and, similarly, intercepts the XML response immediately after it is received from the MMS. Before passing these objects on, without modifying them, it records the XML data as a byte string and passes it to two methods: `audit_request` and `audit_response`. These can be overridden by any object that inherits from this class, allowing the user to direct this data to whatever store they prefer to use for auditing or logging.
|
|
198
191
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[](https://github.com/ElectroRoute-Japan/mms-client/actions)
|
|
2
2
|
|
|
3
3
|
# Overview
|
|
4
|
-
This repository contains a Python client that is capable of communication with the Market Management System (MMS), which handles requests related to Flex or Virtual Power Plant (VPP) trading. The underlying library
|
|
4
|
+
This repository contains a Python client that is capable of communication with the Market Management System (MMS), which handles requests related to Flex or Virtual Power Plant (VPP) trading. The underlying library relies on SOAP communication, which is a pain to work with at the best of times. This particular SOAP API adds its own special layer of obnoxiousness, however. As such, it was deemed useful to have a client which would obfuscate most, if not all of this away from the user.
|
|
5
5
|
|
|
6
6
|
# Communication
|
|
7
7
|
The underlying API sends and receives XML documents. Each of these request or responses, which we will hereafter refer to as *outer* requests and responses, contains metadata about the request/response as well as three fields which are extremely important to successful communication with the API:
|
|
@@ -15,7 +15,9 @@ After the data has been converted and added to the outer request object, it is s
|
|
|
15
15
|
This library relies on Pydantic 2 and the pydantic-xml library for serialization/deserialization. As such, any type in this library can be converted to not only XML, but to JSON as well. This is extremely useful if you're trying to build a pass-through API service or something similar.
|
|
16
16
|
|
|
17
17
|
## Client Types
|
|
18
|
-
Clients cannot call any and all endpoints in this API, willy-nilly. Some endpoints are restricted to particular clients. At the moment, there are
|
|
18
|
+
Clients cannot call any and all endpoints in this API, willy-nilly. Some endpoints are restricted to particular clients. At the moment, there are three clients: Balance Service Providers (BSPs), Market Operators (MOs), and Transmission Service Operators (TSOs). Most likely you're operating as a BSP or MO, in which case you'll have access to most or all endpoints. However, it makes little sense for a TSO to be able to submit bids on their own power, so they are restricted to a read-only role in most cases.
|
|
19
|
+
|
|
20
|
+
Note that MOs and BSPs access the MMS service using the same endpoints, but have distinct permissions. As such, both client types are supported explicitly here.
|
|
19
21
|
|
|
20
22
|
Should you request an endpoint which you are not authorized for, you will receive an `mms_client.utils.errors.AudienceError`.
|
|
21
23
|
|
|
@@ -152,15 +154,6 @@ If you're connecting as a market operator (MO), you can connect in admin mode:
|
|
|
152
154
|
client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert, is_admin=True)
|
|
153
155
|
```
|
|
154
156
|
|
|
155
|
-
## Log Settings
|
|
156
|
-
The client also supports injection of a custom logger. The default logger is named "MMS Client".
|
|
157
|
-
|
|
158
|
-
```python
|
|
159
|
-
client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert, logger=my_logger)
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
The client currently logs a number of informational, debug and error messages. You can freely change the logging level yourself.
|
|
163
|
-
|
|
164
157
|
## Auditing XML Requests & Responses
|
|
165
158
|
A common requirement for this sort of library is recording or saving the raw XML requests and responses for audit/logging purposes. This library supports this workflow through the `mms_client.utils.auditing.AuditPlugin` object. This object intercepts the XML request at the Zeep client level right before it is sent to the MMS and, similarly, intercepts the XML response immediately after it is received from the MMS. Before passing these objects on, without modifying them, it records the XML data as a byte string and passes it to two methods: `audit_request` and `audit_response`. These can be overridden by any object that inherits from this class, allowing the user to direct this data to whatever store they prefer to use for auditing or logging.
|
|
166
159
|
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"""Contains the client layer for communicating with the MMS server."""
|
|
2
2
|
|
|
3
|
+
from logging import getLogger
|
|
4
|
+
|
|
3
5
|
from mms_client.services.base import BaseClient
|
|
4
6
|
from mms_client.services.market import MarketClientMixin
|
|
5
7
|
from mms_client.services.omi import OMIClientMixin
|
|
6
8
|
from mms_client.services.registration import RegistrationClientMixin
|
|
7
9
|
from mms_client.services.report import ReportClientMixin
|
|
8
10
|
|
|
11
|
+
# Set the default logger for the MMS client
|
|
12
|
+
logger = getLogger(__name__)
|
|
13
|
+
|
|
9
14
|
|
|
10
15
|
class MmsClient(BaseClient, MarketClientMixin, RegistrationClientMixin, ReportClientMixin, OMIClientMixin):
|
|
11
16
|
"""User client for the MMS server.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Contains the client layer for communicating with the MMS server."""
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from logging import Logger
|
|
5
4
|
from logging import getLogger
|
|
6
5
|
from typing import Dict
|
|
7
6
|
from typing import Generic
|
|
@@ -37,7 +36,7 @@ from mms_client.utils.web import Plugin
|
|
|
37
36
|
from mms_client.utils.web import ZWrapper
|
|
38
37
|
|
|
39
38
|
# Set the default logger for the MMS client
|
|
40
|
-
|
|
39
|
+
logger = getLogger(__name__)
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
@dataclass
|
|
@@ -59,7 +58,7 @@ class EndpointConfiguration(Generic[E, P]):
|
|
|
59
58
|
name: str
|
|
60
59
|
|
|
61
60
|
# The allowed client types for the endpoint
|
|
62
|
-
|
|
61
|
+
allowed_clients: Optional[List[ClientType]]
|
|
63
62
|
|
|
64
63
|
# The service for the endpoint
|
|
65
64
|
service: ServiceConfiguration
|
|
@@ -85,10 +84,6 @@ class ClientProto(Protocol):
|
|
|
85
84
|
def user(self) -> str:
|
|
86
85
|
"""Return the user name of the person making the request."""
|
|
87
86
|
|
|
88
|
-
@property
|
|
89
|
-
def logger(self) -> Logger:
|
|
90
|
-
"""Return the logger for the client."""
|
|
91
|
-
|
|
92
87
|
def verify_audience(self, config: EndpointConfiguration) -> None:
|
|
93
88
|
"""Verify that the client type is allowed.
|
|
94
89
|
|
|
@@ -139,7 +134,7 @@ def mms_endpoint(
|
|
|
139
134
|
name: str,
|
|
140
135
|
service: ServiceConfiguration,
|
|
141
136
|
request_type: RequestType,
|
|
142
|
-
|
|
137
|
+
allowed_clients: Optional[List[ClientType]] = None,
|
|
143
138
|
resp_envelope_type: Optional[Type[E]] = None,
|
|
144
139
|
resp_data_type: Optional[Type[P]] = None,
|
|
145
140
|
):
|
|
@@ -150,22 +145,23 @@ def mms_endpoint(
|
|
|
150
145
|
Decorated functions will only be responsible for creating the payload envelope to submit to the MMS server.
|
|
151
146
|
|
|
152
147
|
Arguments:
|
|
153
|
-
name (str):
|
|
154
|
-
service (ServiceConfiguration):
|
|
155
|
-
request_type (RequestType):
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
resp_envelope_type (Type[E]):
|
|
159
|
-
|
|
160
|
-
resp_data_type (Type[P]):
|
|
161
|
-
|
|
148
|
+
name (str): The name of the endpoint.
|
|
149
|
+
service (ServiceConfiguration): The configuration for the service.
|
|
150
|
+
request_type (RequestType): The type of request to submit to the MMS server.
|
|
151
|
+
allowed_clients (List[ClientType]): The types of clients that are allowed to access the endpoint. If this is not
|
|
152
|
+
provided, then any client will be allowed.
|
|
153
|
+
resp_envelope_type (Type[E]): The type of payload to expect in the response. If this is not provided, then the
|
|
154
|
+
response envelope will be assumed to have the same type as the request envelope.
|
|
155
|
+
resp_data_type (Type[P]): The type of data to expect in the response. If this is not provided, then the
|
|
156
|
+
response data will be assumed to have the same type as the request data.
|
|
162
157
|
"""
|
|
163
158
|
# First, create the endpoint configuration from the given parameters
|
|
164
|
-
config = EndpointConfiguration(name,
|
|
159
|
+
config = EndpointConfiguration(name, allowed_clients, service, request_type, resp_envelope_type, resp_data_type)
|
|
165
160
|
|
|
166
161
|
# Next, create a decorator that will add the endpoint configuration to the function
|
|
167
162
|
def decorator(func):
|
|
168
163
|
def wrapper(self: ClientProto, *args, **kwargs) -> Optional[P]:
|
|
164
|
+
logger.info(f"{config.name}: Called with args: {args[1:]}...")
|
|
169
165
|
|
|
170
166
|
# First, verify that the client type is allowed
|
|
171
167
|
self.verify_audience(config)
|
|
@@ -177,6 +173,7 @@ def mms_endpoint(
|
|
|
177
173
|
resp, _ = self.request_one(envelope, args[0], config)
|
|
178
174
|
|
|
179
175
|
# Finally, extract the data from the response and return it
|
|
176
|
+
logger.info(f"{config.name}: Returning {type(resp.data).__name__} data.")
|
|
180
177
|
return resp.data
|
|
181
178
|
|
|
182
179
|
return wrapper
|
|
@@ -189,7 +186,7 @@ def mms_multi_endpoint(
|
|
|
189
186
|
name: str,
|
|
190
187
|
service: ServiceConfiguration,
|
|
191
188
|
request_type: RequestType,
|
|
192
|
-
|
|
189
|
+
allowed_clients: Optional[List[ClientType]] = None,
|
|
193
190
|
resp_envelope_type: Optional[Type[E]] = None,
|
|
194
191
|
resp_data_type: Optional[Type[P]] = None,
|
|
195
192
|
):
|
|
@@ -201,26 +198,27 @@ def mms_multi_endpoint(
|
|
|
201
198
|
the MMS server.
|
|
202
199
|
|
|
203
200
|
Arguments:
|
|
204
|
-
name (str):
|
|
205
|
-
service (ServiceConfiguration):
|
|
206
|
-
request_type (RequestType):
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
resp_envelope_type (Type[E]):
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
201
|
+
name (str): The name of the endpoint.
|
|
202
|
+
service (ServiceConfiguration): The configuration for the service.
|
|
203
|
+
request_type (RequestType): The type of request to submit to the MMS server.
|
|
204
|
+
allowed_clients (List[ClientType]): The types of clients that are allowed to access the endpoint. If this is not
|
|
205
|
+
provided, then any client will be allowed.
|
|
206
|
+
resp_envelope_type (Type[E]): The type of payload to expect in the response. If this is not provided, then
|
|
207
|
+
the response envelope will be assumed to have the same type as the request
|
|
208
|
+
envelope.
|
|
209
|
+
resp_data_type (Type[P]): The type of data to expect in the response. If this is not provided, then the
|
|
210
|
+
response data will be assumed to have the same type as the request data. Note,
|
|
211
|
+
that this is not intended to account for the expected sequence type of the
|
|
212
|
+
response data. That is already handled in the wrapped function, so this should
|
|
213
|
+
only be set if the inner data type being returned differs from what was sent.
|
|
216
214
|
"""
|
|
217
215
|
# First, create the endpoint configuration from the given parameters
|
|
218
|
-
config = EndpointConfiguration(name,
|
|
216
|
+
config = EndpointConfiguration(name, allowed_clients, service, request_type, resp_envelope_type, resp_data_type)
|
|
219
217
|
|
|
220
218
|
# Next, create a decorator that will add the endpoint configuration to the function
|
|
221
219
|
def decorator(func):
|
|
222
220
|
def wrapper(self: ClientProto, *args, **kwargs) -> List[P]:
|
|
223
|
-
|
|
221
|
+
logger.info(f"{config.name}: Called with args: {args[1:]}...")
|
|
224
222
|
|
|
225
223
|
# First, verify that the client type is allowed
|
|
226
224
|
self.verify_audience(config)
|
|
@@ -232,7 +230,7 @@ def mms_multi_endpoint(
|
|
|
232
230
|
resp, _ = self.request_many(envelope, args[0], config)
|
|
233
231
|
|
|
234
232
|
# Finally, extract the data from the response and return it
|
|
235
|
-
|
|
233
|
+
logger.info(f"{config.name}: Returning {len(resp.data)} item(s).")
|
|
236
234
|
return resp.data
|
|
237
235
|
|
|
238
236
|
return wrapper
|
|
@@ -253,7 +251,6 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
253
251
|
user: str,
|
|
254
252
|
client_type: ClientType,
|
|
255
253
|
cert: Certificate,
|
|
256
|
-
logger: Optional[Logger] = None,
|
|
257
254
|
plugins: Optional[List[Plugin]] = None,
|
|
258
255
|
is_admin: bool = False,
|
|
259
256
|
test: bool = False,
|
|
@@ -265,8 +262,6 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
265
262
|
user (str): The user name of the person making the request.
|
|
266
263
|
client_type (ClientType): The type of client to use for making requests to the MMS server.
|
|
267
264
|
cert (Certificate): The certificate to use for signing requests.
|
|
268
|
-
logger (Logger): The logger to use for instrumentation. If this is not provided, then the default
|
|
269
|
-
logger will be used.
|
|
270
265
|
plugins (List[Plugin]): A list of plugins to add to the Zeep client. This can be useful for auditing or
|
|
271
266
|
other purposes.
|
|
272
267
|
is_admin (bool): Whether the user is an admin (i.e. is a market operator).
|
|
@@ -283,8 +278,7 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
283
278
|
self._cert = cert
|
|
284
279
|
self._signer = CryptoWrapper(cert)
|
|
285
280
|
|
|
286
|
-
# Now, set
|
|
287
|
-
self._logger = logger or default_logger
|
|
281
|
+
# Now, set the plugins we'll inject into the Zeep client
|
|
288
282
|
self._plugins = plugins or []
|
|
289
283
|
|
|
290
284
|
# Finally, create a list of wrappers for the different interfaces
|
|
@@ -300,11 +294,6 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
300
294
|
"""Return the user name of the person making the request."""
|
|
301
295
|
return self._user
|
|
302
296
|
|
|
303
|
-
@property
|
|
304
|
-
def logger(self) -> Logger:
|
|
305
|
-
"""Return the logger for the client."""
|
|
306
|
-
return self._logger
|
|
307
|
-
|
|
308
297
|
def verify_audience(self, config: EndpointConfiguration) -> None:
|
|
309
298
|
"""Verify that the client type is allowed.
|
|
310
299
|
|
|
@@ -317,12 +306,12 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
317
306
|
Raises:
|
|
318
307
|
ValueError: If the client type is not allowed.
|
|
319
308
|
"""
|
|
320
|
-
|
|
321
|
-
f"{config.name}: Verifying audience. Allowed
|
|
322
|
-
f"{config.
|
|
309
|
+
logger.debug(
|
|
310
|
+
f"{config.name}: Verifying audience. Allowed clients: "
|
|
311
|
+
f"{config.allowed_clients if config.allowed_clients else 'Any'}."
|
|
323
312
|
)
|
|
324
|
-
if config.
|
|
325
|
-
raise AudienceError(config.name, config.
|
|
313
|
+
if config.allowed_clients and (self._client_type not in config.allowed_clients):
|
|
314
|
+
raise AudienceError(config.name, config.allowed_clients, self._client_type)
|
|
326
315
|
|
|
327
316
|
def request_one(
|
|
328
317
|
self,
|
|
@@ -340,7 +329,7 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
340
329
|
Returns: The response from the MMS server.
|
|
341
330
|
"""
|
|
342
331
|
# First, create the MMS request from the payload and data.
|
|
343
|
-
|
|
332
|
+
logger.debug(
|
|
344
333
|
f"{config.name}: Starting request. Envelope: {type(envelope).__name__}, Data: {type(payload).__name__}",
|
|
345
334
|
)
|
|
346
335
|
request = self._to_mms_request(config.request_type, config.service.serializer.serialize(envelope, payload))
|
|
@@ -359,7 +348,7 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
359
348
|
self._verify_response(data, config)
|
|
360
349
|
|
|
361
350
|
# Return the response data and any attachments
|
|
362
|
-
|
|
351
|
+
logger.debug(
|
|
363
352
|
f"{config.name}: Returning response. Envelope: {envelope_type.__name__}, Data: {data_type.__name__}",
|
|
364
353
|
)
|
|
365
354
|
return data, attachments
|
|
@@ -382,7 +371,7 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
382
371
|
# First, create the MMS request from the payload and data.
|
|
383
372
|
is_list = isinstance(payload, list)
|
|
384
373
|
data_type = type(payload[0]) if is_list else type(payload) # type: ignore[index]
|
|
385
|
-
|
|
374
|
+
logger.debug(
|
|
386
375
|
(
|
|
387
376
|
f"{config.name}: Starting multi-request. Envelope: {type(envelope).__name__}, "
|
|
388
377
|
f"Data: {data_type.__name__}"
|
|
@@ -413,7 +402,7 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
413
402
|
self._verify_multi_response(data, config)
|
|
414
403
|
|
|
415
404
|
# Return the response data and any attachments
|
|
416
|
-
|
|
405
|
+
logger.debug(
|
|
417
406
|
f"{config.name}: Returning multi-response. Envelope: {envelope_type.__name__}, Data: {data_type.__name__}",
|
|
418
407
|
)
|
|
419
408
|
return data, attachments
|
|
@@ -446,7 +435,7 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
446
435
|
)
|
|
447
436
|
|
|
448
437
|
# Embed the data and the attachments in the MMS request and return it
|
|
449
|
-
|
|
438
|
+
logger.debug(
|
|
450
439
|
f"Creating MMS request of type {req_type.name} to send {len(data)} bytes of data and "
|
|
451
440
|
f"{len(attachment_data)} attachments."
|
|
452
441
|
)
|
|
@@ -480,9 +469,9 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
480
469
|
|
|
481
470
|
# Check the response status flags and log any warnings or errors
|
|
482
471
|
if resp.warnings:
|
|
483
|
-
|
|
472
|
+
logger.warning(f"{config.name}: MMS response contained warnings.")
|
|
484
473
|
if not resp.success:
|
|
485
|
-
|
|
474
|
+
logger.error(f"{config.name}: MMS response was unsuccessful.")
|
|
486
475
|
|
|
487
476
|
def _verify_response(self, resp: Response[E, P], config: EndpointConfiguration) -> None:
|
|
488
477
|
"""Verify that the given response is valid.
|
|
@@ -543,7 +532,7 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
543
532
|
Returns: True to indicate that the response is valid, False otherwise.
|
|
544
533
|
"""
|
|
545
534
|
# Log the request's processing statistics
|
|
546
|
-
|
|
535
|
+
logger.info(
|
|
547
536
|
f"{config.name} ({resp.statistics.timestamp_xml}): Recieved {resp.statistics.received}, "
|
|
548
537
|
f"Valid: {resp.statistics.valid}, Invalid: {resp.statistics.invalid}, "
|
|
549
538
|
f"Successful: {resp.statistics.successful}, Unsuccessful: {resp.statistics.unsuccessful} "
|
|
@@ -565,11 +554,11 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
565
554
|
"""
|
|
566
555
|
for path, messages in resp.messages.items():
|
|
567
556
|
for info in messages.information:
|
|
568
|
-
|
|
557
|
+
logger.info(f"{config.name} - {path}: {info.code}")
|
|
569
558
|
for warning in messages.warnings:
|
|
570
|
-
|
|
559
|
+
logger.warning(f"{config.name} - {path}: {warning.code}")
|
|
571
560
|
for error in messages.errors:
|
|
572
|
-
|
|
561
|
+
logger.error(f"{config.name} - {path}: {error.code}")
|
|
573
562
|
|
|
574
563
|
def _verify_response_common(
|
|
575
564
|
self, config: EndpointConfiguration, payload_type: type, resp: ResponseCommon, index: Optional[int] = None
|
|
@@ -586,7 +575,7 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
586
575
|
Returns: True to indicate that the response is valid, False otherwise.
|
|
587
576
|
"""
|
|
588
577
|
# Log the status of the response validation
|
|
589
|
-
|
|
578
|
+
logger.info(
|
|
590
579
|
f"{config.name}: {payload_type.__name__}{f'[{index}]' if index is not None else ''} was valid? "
|
|
591
580
|
f"{resp.success} ({resp.validation.value})",
|
|
592
581
|
)
|
|
@@ -601,12 +590,11 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
601
590
|
service (ServiceConfiguration): The service for which to get the wrapper.
|
|
602
591
|
"""
|
|
603
592
|
if service.interface not in self._wrappers:
|
|
604
|
-
|
|
593
|
+
logger.debug(f"Creating wrapper for {service.interface.name} interface.")
|
|
605
594
|
self._wrappers[service.interface] = ZWrapper(
|
|
606
595
|
self._client_type,
|
|
607
596
|
service.interface,
|
|
608
597
|
self._cert.to_adapter(),
|
|
609
|
-
self._logger,
|
|
610
598
|
self._plugins,
|
|
611
599
|
True,
|
|
612
600
|
self._test,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Contains the client layer for making market requests to the MMS server."""
|
|
2
2
|
|
|
3
3
|
from datetime import date as Date
|
|
4
|
+
from logging import getLogger
|
|
4
5
|
from typing import List
|
|
5
6
|
from typing import Optional
|
|
6
7
|
|
|
@@ -23,6 +24,9 @@ from mms_client.utils.serialization import Serializer
|
|
|
23
24
|
from mms_client.utils.web import ClientType
|
|
24
25
|
from mms_client.utils.web import Interface
|
|
25
26
|
|
|
27
|
+
# Set the default logger for the MMS client
|
|
28
|
+
logger = getLogger(__name__)
|
|
29
|
+
|
|
26
30
|
|
|
27
31
|
class MarketClientMixin: # pylint: disable=unused-argument
|
|
28
32
|
"""Market client for the MMS server."""
|
|
@@ -30,7 +34,7 @@ class MarketClientMixin: # pylint: disable=unused-argument
|
|
|
30
34
|
# The configuration for the market service
|
|
31
35
|
config = ServiceConfiguration(Interface.MI, Serializer(SchemaType.MARKET, "MarketData"))
|
|
32
36
|
|
|
33
|
-
@mms_endpoint("MarketSubmit_OfferData", config, RequestType.INFO, ClientType.BSP)
|
|
37
|
+
@mms_endpoint("MarketSubmit_OfferData", config, RequestType.INFO, [ClientType.BSP])
|
|
34
38
|
def put_offer(
|
|
35
39
|
self: ClientProto, request: OfferData, market_type: MarketType, days: int, date: Optional[Date] = None
|
|
36
40
|
) -> OfferData:
|
|
@@ -56,7 +60,7 @@ class MarketClientMixin: # pylint: disable=unused-argument
|
|
|
56
60
|
days=days,
|
|
57
61
|
)
|
|
58
62
|
|
|
59
|
-
@mms_multi_endpoint("MarketSubmit_OfferData", config, RequestType.INFO, ClientType.BSP)
|
|
63
|
+
@mms_multi_endpoint("MarketSubmit_OfferData", config, RequestType.INFO, [ClientType.BSP])
|
|
60
64
|
def put_offers(
|
|
61
65
|
self: ClientProto, requests: List[OfferData], market_type: MarketType, days: int, date: Optional[Date] = None
|
|
62
66
|
) -> List[OfferData]:
|
|
@@ -106,7 +110,7 @@ class MarketClientMixin: # pylint: disable=unused-argument
|
|
|
106
110
|
days=days,
|
|
107
111
|
)
|
|
108
112
|
|
|
109
|
-
@mms_endpoint("MarketCancel_OfferCancel", config, RequestType.INFO, ClientType.BSP)
|
|
113
|
+
@mms_endpoint("MarketCancel_OfferCancel", config, RequestType.INFO, [ClientType.BSP])
|
|
110
114
|
def cancel_offer(
|
|
111
115
|
self: ClientProto, request: OfferCancel, market_type: MarketType, days: int, date: Optional[Date] = None
|
|
112
116
|
) -> OfferCancel:
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"""Contains the client layer for making OMI requests to the MMS server."""
|
|
2
2
|
|
|
3
|
+
from logging import getLogger
|
|
4
|
+
|
|
3
5
|
from mms_client.services.base import ServiceConfiguration
|
|
4
6
|
from mms_client.utils.serialization import SchemaType
|
|
5
7
|
from mms_client.utils.serialization import Serializer
|
|
6
8
|
from mms_client.utils.web import Interface
|
|
7
9
|
|
|
10
|
+
# Set the default logger for the MMS client
|
|
11
|
+
logger = getLogger(__name__)
|
|
12
|
+
|
|
8
13
|
|
|
9
14
|
class OMIClientMixin: # pylint: disable=unused-argument
|
|
10
15
|
"""OMI client for the MMS server."""
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Contains the client layer for making registration requests to the MMS server."""
|
|
2
2
|
|
|
3
3
|
from datetime import date as Date
|
|
4
|
+
from logging import getLogger
|
|
4
5
|
from typing import List
|
|
5
6
|
from typing import Optional
|
|
6
7
|
|
|
@@ -20,6 +21,9 @@ from mms_client.utils.serialization import Serializer
|
|
|
20
21
|
from mms_client.utils.web import ClientType
|
|
21
22
|
from mms_client.utils.web import Interface
|
|
22
23
|
|
|
24
|
+
# Set the default logger for the MMS client
|
|
25
|
+
logger = getLogger(__name__)
|
|
26
|
+
|
|
23
27
|
|
|
24
28
|
class RegistrationClientMixin: # pylint: disable=unused-argument
|
|
25
29
|
"""Registration client for the MMS server."""
|
|
@@ -27,7 +31,7 @@ class RegistrationClientMixin: # pylint: disable=unused-argument
|
|
|
27
31
|
# The configuration for the registration service
|
|
28
32
|
config = ServiceConfiguration(Interface.MI, Serializer(SchemaType.REGISTRATION, "RegistrationData"))
|
|
29
33
|
|
|
30
|
-
@mms_endpoint("RegistrationSubmit_Resource", config, RequestType.REGISTRATION, ClientType.BSP)
|
|
34
|
+
@mms_endpoint("RegistrationSubmit_Resource", config, RequestType.REGISTRATION, [ClientType.BSP])
|
|
31
35
|
def put_resource(self: ClientProto, request: ResourceData) -> ResourceData:
|
|
32
36
|
"""Submit a new resource to the MMS server.
|
|
33
37
|
|
|
@@ -50,6 +54,7 @@ class RegistrationClientMixin: # pylint: disable=unused-argument
|
|
|
50
54
|
"RegistrationQuery_Resource",
|
|
51
55
|
config,
|
|
52
56
|
RequestType.REGISTRATION,
|
|
57
|
+
allowed_clients=[ClientType.BSP, ClientType.TSO],
|
|
53
58
|
resp_envelope_type=RegistrationSubmit,
|
|
54
59
|
resp_data_type=ResourceData,
|
|
55
60
|
)
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"""Contains the client layer for making report requests to the MMS server."""
|
|
2
2
|
|
|
3
|
+
from logging import getLogger
|
|
4
|
+
|
|
3
5
|
from mms_client.services.base import ServiceConfiguration
|
|
4
6
|
from mms_client.utils.serialization import SchemaType
|
|
5
7
|
from mms_client.utils.serialization import Serializer
|
|
6
8
|
from mms_client.utils.web import Interface
|
|
7
9
|
|
|
10
|
+
# Set the default logger for the MMS client
|
|
11
|
+
logger = getLogger(__name__)
|
|
12
|
+
|
|
8
13
|
|
|
9
14
|
class ReportClientMixin: # pylint: disable=unused-argument
|
|
10
15
|
"""Report client for the MMS server."""
|
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC
|
|
4
4
|
from abc import abstractmethod
|
|
5
|
+
from logging import getLogger
|
|
5
6
|
|
|
6
7
|
from lxml.etree import _Element as Element
|
|
7
8
|
from lxml.etree import tostring
|
|
8
9
|
from zeep import Plugin
|
|
9
10
|
|
|
11
|
+
# Set the default logger for the MMS client
|
|
12
|
+
logger = getLogger(__name__)
|
|
13
|
+
|
|
10
14
|
|
|
11
15
|
class AuditPlugin(ABC, Plugin):
|
|
12
16
|
"""Base class for audit plugins."""
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Contains error classes for the MMS client."""
|
|
2
2
|
|
|
3
|
+
from logging import getLogger
|
|
3
4
|
from typing import Dict
|
|
4
5
|
from typing import List
|
|
5
6
|
from typing import Optional
|
|
@@ -10,11 +11,14 @@ from mms_client.types.base import Messages
|
|
|
10
11
|
from mms_client.types.base import P
|
|
11
12
|
from mms_client.utils.web import ClientType
|
|
12
13
|
|
|
14
|
+
# Set the default logger for the MMS client
|
|
15
|
+
logger = getLogger(__name__)
|
|
16
|
+
|
|
13
17
|
|
|
14
18
|
class AudienceError(ValueError):
|
|
15
19
|
"""Error raised when an invalid audience is provided."""
|
|
16
20
|
|
|
17
|
-
def __init__(self, method: str, allowed: ClientType, audience: ClientType):
|
|
21
|
+
def __init__(self, method: str, allowed: List[ClientType], audience: ClientType):
|
|
18
22
|
"""Initialize the error.
|
|
19
23
|
|
|
20
24
|
Arguments:
|
|
@@ -22,8 +26,13 @@ class AudienceError(ValueError):
|
|
|
22
26
|
allowed (str): The allowed audience.
|
|
23
27
|
audience (str): The invalid audience.
|
|
24
28
|
"""
|
|
29
|
+
inner = (
|
|
30
|
+
f"'{allowed[0].name}' is"
|
|
31
|
+
if len(allowed) == 1
|
|
32
|
+
else f"""{" or ".join([f"'{a.name}'" for a in allowed])} are"""
|
|
33
|
+
)
|
|
25
34
|
self.method = method
|
|
26
|
-
self.message = f"{method}: Invalid client type, '{audience.name}' provided. Only
|
|
35
|
+
self.message = f"{method}: Invalid client type, '{audience.name}' provided. Only {inner} supported."
|
|
27
36
|
self.allowed = allowed
|
|
28
37
|
self.audience = audience
|
|
29
38
|
super().__init__(self.message)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from functools import lru_cache
|
|
4
4
|
from io import BytesIO
|
|
5
|
+
from logging import getLogger
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Dict
|
|
7
8
|
from typing import List
|
|
@@ -32,6 +33,9 @@ from mms_client.types.base import SchemaType
|
|
|
32
33
|
# Directory containing all our XML schemas
|
|
33
34
|
XSD_DIR = Path(__file__).parent.parent / "schemas" / "xsd"
|
|
34
35
|
|
|
36
|
+
# Set the default logger for the MMS client
|
|
37
|
+
logger = getLogger(__name__)
|
|
38
|
+
|
|
35
39
|
|
|
36
40
|
class Serializer:
|
|
37
41
|
"""Contains methods for serializing and deserializing MMS data."""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Contains the HTTP/web layer for communicating with the MMS server."""
|
|
2
2
|
|
|
3
|
-
from enum import
|
|
4
|
-
from logging import
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
from logging import getLogger
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import List
|
|
7
7
|
from typing import Optional
|
|
@@ -20,18 +20,23 @@ from zeep.xsd.valueobjects import CompoundValue
|
|
|
20
20
|
from mms_client.types.transport import MmsRequest
|
|
21
21
|
from mms_client.types.transport import MmsResponse
|
|
22
22
|
|
|
23
|
+
# Set the default logger for the MMS client
|
|
24
|
+
logger = getLogger(__name__)
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
|
|
27
|
+
class ClientType(StrEnum):
|
|
25
28
|
"""Identifies the type of client to use.
|
|
26
29
|
|
|
27
|
-
The client can be either "bsp" (Balancing Service Provider) or "tso" (Transmission System
|
|
30
|
+
The client can be either "bsp" (Balancing Service Provider), "mo" (Market Operator), or "tso" (Transmission System
|
|
31
|
+
Operator).
|
|
28
32
|
"""
|
|
29
33
|
|
|
30
34
|
BSP = "bsp"
|
|
35
|
+
MO = "mo"
|
|
31
36
|
TSO = "tso"
|
|
32
37
|
|
|
33
38
|
|
|
34
|
-
class Interface(
|
|
39
|
+
class Interface(StrEnum):
|
|
35
40
|
"""Identifies the type of interface to use.
|
|
36
41
|
|
|
37
42
|
The interface can be either "omi" (Other Market Initiator) or "mi" (Market Initiator).
|
|
@@ -76,32 +81,30 @@ class ServiceEndpoint:
|
|
|
76
81
|
self._selected = self.main
|
|
77
82
|
|
|
78
83
|
|
|
79
|
-
# Defines the service endpoints for the BSP and TSO clients for the OMI and MI web services, respectively.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
),
|
|
104
|
-
},
|
|
84
|
+
# Defines the service endpoints for the BSP, MO and TSO clients for the OMI and MI web services, respectively.
|
|
85
|
+
BSP_MO_URLS = {
|
|
86
|
+
Interface.OMI: ServiceEndpoint(
|
|
87
|
+
main="https://www5.tdgc.jp/axis2/services/OmiWebService",
|
|
88
|
+
backup="https://www6.tdgc.jp/axis2/services/OmiWebService",
|
|
89
|
+
test="https://www7.tdgc.jp/axis2/services/OmiWebService",
|
|
90
|
+
),
|
|
91
|
+
Interface.MI: ServiceEndpoint(
|
|
92
|
+
main="https://www2.tdgc.jp/axis2/services/MiWebService",
|
|
93
|
+
backup="https://www3.tdgc.jp/axis2/services/MiWebService",
|
|
94
|
+
test="https://www4.tdgc.jp/axis2/services/MiWebService",
|
|
95
|
+
),
|
|
96
|
+
}
|
|
97
|
+
TSO_URLS = {
|
|
98
|
+
Interface.OMI: ServiceEndpoint(
|
|
99
|
+
main="https://maiwlba103v07.tdgc.jp/axis2/services/OmiWebService",
|
|
100
|
+
backup="https://mbiwlba103v07.tdgc.jp/axis2/services/OmiWebService",
|
|
101
|
+
test="https://mbiwlba103v08.tdgc.jp/axis2/services/OmiWebService",
|
|
102
|
+
),
|
|
103
|
+
Interface.MI: ServiceEndpoint(
|
|
104
|
+
main="https://maiwlba103v03.tdgc.jp/axis2/services/MiWebService",
|
|
105
|
+
backup="https://mbiwlba103v03.tdgc.jp/axis2/services/MiWebService",
|
|
106
|
+
test="https://mbiwlba103v06.tdgc.jp/axis2/services/MiWebService",
|
|
107
|
+
),
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
|
|
@@ -134,7 +137,6 @@ class ZWrapper:
|
|
|
134
137
|
client: ClientType,
|
|
135
138
|
interface: Interface,
|
|
136
139
|
adapter: Pkcs12Adapter,
|
|
137
|
-
logger: Logger,
|
|
138
140
|
plugins: Optional[List[Plugin]] = None,
|
|
139
141
|
cache: bool = True,
|
|
140
142
|
test: bool = False,
|
|
@@ -150,7 +152,6 @@ class ZWrapper:
|
|
|
150
152
|
as the service and port to use.
|
|
151
153
|
adapter (Pkcs12Adapter): The PKCS12 adapter containing the certificate and private key to use for
|
|
152
154
|
authenticating with the MMS server.
|
|
153
|
-
logger (Logger): The logger to use for instrumentation.
|
|
154
155
|
plugins (List[Plugin]): A list of Zeep plugins to use with the client. This is useful for adding additional
|
|
155
156
|
functionality to the client, such as auditing or logging.
|
|
156
157
|
cache (bool): If True, use a cache for the Zeep client. This is useful for avoiding repeated
|
|
@@ -158,8 +159,12 @@ class ZWrapper:
|
|
|
158
159
|
test (bool): If True, use the test service endpoint. This is useful for testing the client.
|
|
159
160
|
"""
|
|
160
161
|
# First, we'll check that the client is valid. If it's not, we'll raise a ValueError.
|
|
161
|
-
if client
|
|
162
|
-
|
|
162
|
+
if client in [ClientType.BSP, ClientType.MO]:
|
|
163
|
+
urls = BSP_MO_URLS
|
|
164
|
+
elif client == ClientType.TSO:
|
|
165
|
+
urls = TSO_URLS
|
|
166
|
+
else:
|
|
167
|
+
raise ValueError(f"Invalid client, '{client}'. Only 'bsp', 'mo', and 'tso' are supported.")
|
|
163
168
|
|
|
164
169
|
# We need to determine the service port and location of the WSDL file based on the given interface. If the
|
|
165
170
|
# interface is neither "omi" nor "mi", we raise a ValueError.
|
|
@@ -172,7 +177,7 @@ class ZWrapper:
|
|
|
172
177
|
raise ValueError(f"Invalid interface, '{self._interface}'. Only 'mi' and 'omi' are supported.")
|
|
173
178
|
|
|
174
179
|
# Next, we need to select the correct service endpoint based on the given client and interface.
|
|
175
|
-
self._endpoint =
|
|
180
|
+
self._endpoint = urls[self._interface]
|
|
176
181
|
self._endpoint.select(test=test)
|
|
177
182
|
|
|
178
183
|
# Now, we need to create a new session and mount the PKCS12 adapter to it. This is necessary for
|
|
@@ -186,7 +191,6 @@ class ZWrapper:
|
|
|
186
191
|
|
|
187
192
|
# Finally, we create the Zeep client with the given WSDL file location, session, and cache settings and then,
|
|
188
193
|
# from that client, we create the SOAP service with the given service binding and selected endpoint.
|
|
189
|
-
self._logger = logger
|
|
190
194
|
self._client = Client(
|
|
191
195
|
wsdl=str(location.resolve()),
|
|
192
196
|
transport=Transport(cache=SqliteCache() if cache else None, session=sess),
|
|
@@ -194,11 +198,11 @@ class ZWrapper:
|
|
|
194
198
|
)
|
|
195
199
|
self._create_service()
|
|
196
200
|
|
|
197
|
-
@on_exception(expo, TransportError, max_tries=3, giveup=fatal_code) # type: ignore[arg-type]
|
|
201
|
+
@on_exception(expo, TransportError, max_tries=3, giveup=fatal_code, logger=logger) # type: ignore[arg-type]
|
|
198
202
|
def submit(self, req: MmsRequest) -> MmsResponse:
|
|
199
203
|
"""Submit the given request to the MMS server and return the response."""
|
|
200
204
|
try:
|
|
201
|
-
|
|
205
|
+
logger.debug(f"Submitting MMS request request to {self._interface.name} service")
|
|
202
206
|
|
|
203
207
|
# Submit the request to the MMS server and retrieve the response
|
|
204
208
|
resp: CompoundValue = self._service["submitAttachment"](**req.to_arguments())
|
|
@@ -208,12 +212,12 @@ class ZWrapper:
|
|
|
208
212
|
except TransportError as e:
|
|
209
213
|
# If we got a server fault error, then we'll switch to the backup endpoint. In any case, we'll raise the
|
|
210
214
|
# exception so that our back-off can handle it or pass the exception up the stack.
|
|
211
|
-
|
|
215
|
+
logger.error(
|
|
212
216
|
f"MMS request to {self._interface.name} service failed with status code: {e.status_code}",
|
|
213
217
|
exc_info=e,
|
|
214
218
|
)
|
|
215
219
|
if e.status_code >= 500:
|
|
216
|
-
|
|
220
|
+
logger.warning(f"MMS server error, switching to backup endpoint: {self._endpoint.backup}")
|
|
217
221
|
self._endpoint.select(error=True)
|
|
218
222
|
self._create_service()
|
|
219
223
|
raise
|
|
@@ -223,5 +227,5 @@ class ZWrapper:
|
|
|
223
227
|
|
|
224
228
|
This is useful for switching between the main and backup endpoints in case of an error.
|
|
225
229
|
"""
|
|
226
|
-
|
|
230
|
+
logger.debug(f"Creating new {self._interface.name} service with endpoint: {self._endpoint.selected}")
|
|
227
231
|
self._service = self._client.create_service(SERVICE_BINDINGS[self._interface], self._endpoint.selected)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|