mms-client 1.4.0__py3-none-any.whl → 1.4.1__py3-none-any.whl

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.
@@ -59,7 +59,7 @@ class EndpointConfiguration(Generic[E, P]):
59
59
  name: str
60
60
 
61
61
  # The allowed client types for the endpoint
62
- allowed_client: Optional[ClientType]
62
+ allowed_clients: Optional[List[ClientType]]
63
63
 
64
64
  # The service for the endpoint
65
65
  service: ServiceConfiguration
@@ -139,7 +139,7 @@ def mms_endpoint(
139
139
  name: str,
140
140
  service: ServiceConfiguration,
141
141
  request_type: RequestType,
142
- allowed_client: Optional[ClientType] = None,
142
+ allowed_clients: Optional[List[ClientType]] = None,
143
143
  resp_envelope_type: Optional[Type[E]] = None,
144
144
  resp_data_type: Optional[Type[P]] = None,
145
145
  ):
@@ -150,18 +150,18 @@ def mms_endpoint(
150
150
  Decorated functions will only be responsible for creating the payload envelope to submit to the MMS server.
151
151
 
152
152
  Arguments:
153
- name (str): The name of the endpoint.
154
- service (ServiceConfiguration): The configuration for the service.
155
- request_type (RequestType): The type of request to submit to the MMS server.
156
- allowed_client (ClientType): The type of client that is allowed to access the endpoint. If this is not provided,
157
- then any client type is allowed.
158
- resp_envelope_type (Type[E]): The type of payload to expect in the response. If this is not provided, then the
159
- the response envelope will be assumed to have the same type as the request envelope.
160
- resp_data_type (Type[P]): The type of data to expect in the response. If this is not provided, then the
161
- response data will be assumed to have the same type as the request data.
153
+ name (str): The name of the endpoint.
154
+ service (ServiceConfiguration): The configuration for the service.
155
+ request_type (RequestType): The type of request to submit to the MMS server.
156
+ allowed_clients (List[ClientType]): The types of clients that are allowed to access the endpoint. If this is not
157
+ provided, then any client will be allowed.
158
+ resp_envelope_type (Type[E]): The type of payload to expect in the response. If this is not provided, then the
159
+ response envelope will be assumed to have the same type as the request envelope.
160
+ resp_data_type (Type[P]): The type of data to expect in the response. If this is not provided, then the
161
+ response data will be assumed to have the same type as the request data.
162
162
  """
163
163
  # First, create the endpoint configuration from the given parameters
164
- config = EndpointConfiguration(name, allowed_client, service, request_type, resp_envelope_type, resp_data_type)
164
+ config = EndpointConfiguration(name, allowed_clients, service, request_type, resp_envelope_type, resp_data_type)
165
165
 
166
166
  # Next, create a decorator that will add the endpoint configuration to the function
167
167
  def decorator(func):
@@ -189,7 +189,7 @@ def mms_multi_endpoint(
189
189
  name: str,
190
190
  service: ServiceConfiguration,
191
191
  request_type: RequestType,
192
- allowed_client: Optional[ClientType] = None,
192
+ allowed_clients: Optional[List[ClientType]] = None,
193
193
  resp_envelope_type: Optional[Type[E]] = None,
194
194
  resp_data_type: Optional[Type[P]] = None,
195
195
  ):
@@ -201,21 +201,22 @@ def mms_multi_endpoint(
201
201
  the MMS server.
202
202
 
203
203
  Arguments:
204
- name (str): The name of the endpoint.
205
- service (ServiceConfiguration): The configuration for the service.
206
- request_type (RequestType): The type of request to submit to the MMS server.
207
- allowed_client (ClientType): The type of client that is allowed to access the endpoint. If this is not provided,
208
- then any client type is allowed.
209
- resp_envelope_type (Type[E]): The type of payload to expect in the response. If this is not provided, then the
210
- the response envelope will be assumed to have the same type as the request envelope.
211
- resp_data_type (Type[P]): The type of data to expect in the response. If this is not provided, then the
212
- response data will be assumed to have the same type as the request data. Note, that
213
- this is not intended to account for the expected sequence type of the response data.
214
- That is already handled in the wrapped function, so this should only be set if the
215
- inner data type being returned differs from what was sent.
204
+ name (str): The name of the endpoint.
205
+ service (ServiceConfiguration): The configuration for the service.
206
+ request_type (RequestType): The type of request to submit to the MMS server.
207
+ allowed_clients (List[ClientType]): The types of clients that are allowed to access the endpoint. If this is not
208
+ provided, then any client will be allowed.
209
+ resp_envelope_type (Type[E]): The type of payload to expect in the response. If this is not provided, then
210
+ the response envelope will be assumed to have the same type as the request
211
+ envelope.
212
+ resp_data_type (Type[P]): The type of data to expect in the response. If this is not provided, then the
213
+ response data will be assumed to have the same type as the request data. Note,
214
+ that this is not intended to account for the expected sequence type of the
215
+ response data. That is already handled in the wrapped function, so this should
216
+ only be set if the inner data type being returned differs from what was sent.
216
217
  """
217
218
  # First, create the endpoint configuration from the given parameters
218
- config = EndpointConfiguration(name, allowed_client, service, request_type, resp_envelope_type, resp_data_type)
219
+ config = EndpointConfiguration(name, allowed_clients, service, request_type, resp_envelope_type, resp_data_type)
219
220
 
220
221
  # Next, create a decorator that will add the endpoint configuration to the function
221
222
  def decorator(func):
@@ -318,11 +319,11 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
318
319
  ValueError: If the client type is not allowed.
319
320
  """
320
321
  self._logger.debug(
321
- f"{config.name}: Verifying audience. Allowed client: "
322
- f"{config.allowed_client.name if config.allowed_client else 'Any'}."
322
+ f"{config.name}: Verifying audience. Allowed clients: "
323
+ f"{config.allowed_clients if config.allowed_clients else 'Any'}."
323
324
  )
324
- if config.allowed_client and self._client_type != config.allowed_client:
325
- raise AudienceError(config.name, config.allowed_client, self._client_type)
325
+ if config.allowed_clients and (self._client_type not in config.allowed_clients):
326
+ raise AudienceError(config.name, config.allowed_clients, self._client_type)
326
327
 
327
328
  def request_one(
328
329
  self,
@@ -30,7 +30,7 @@ class MarketClientMixin: # pylint: disable=unused-argument
30
30
  # The configuration for the market service
31
31
  config = ServiceConfiguration(Interface.MI, Serializer(SchemaType.MARKET, "MarketData"))
32
32
 
33
- @mms_endpoint("MarketSubmit_OfferData", config, RequestType.INFO, ClientType.BSP)
33
+ @mms_endpoint("MarketSubmit_OfferData", config, RequestType.INFO, [ClientType.BSP])
34
34
  def put_offer(
35
35
  self: ClientProto, request: OfferData, market_type: MarketType, days: int, date: Optional[Date] = None
36
36
  ) -> OfferData:
@@ -56,7 +56,7 @@ class MarketClientMixin: # pylint: disable=unused-argument
56
56
  days=days,
57
57
  )
58
58
 
59
- @mms_multi_endpoint("MarketSubmit_OfferData", config, RequestType.INFO, ClientType.BSP)
59
+ @mms_multi_endpoint("MarketSubmit_OfferData", config, RequestType.INFO, [ClientType.BSP])
60
60
  def put_offers(
61
61
  self: ClientProto, requests: List[OfferData], market_type: MarketType, days: int, date: Optional[Date] = None
62
62
  ) -> List[OfferData]:
@@ -106,7 +106,7 @@ class MarketClientMixin: # pylint: disable=unused-argument
106
106
  days=days,
107
107
  )
108
108
 
109
- @mms_endpoint("MarketCancel_OfferCancel", config, RequestType.INFO, ClientType.BSP)
109
+ @mms_endpoint("MarketCancel_OfferCancel", config, RequestType.INFO, [ClientType.BSP])
110
110
  def cancel_offer(
111
111
  self: ClientProto, request: OfferCancel, market_type: MarketType, days: int, date: Optional[Date] = None
112
112
  ) -> OfferCancel:
@@ -27,7 +27,7 @@ class RegistrationClientMixin: # pylint: disable=unused-argument
27
27
  # The configuration for the registration service
28
28
  config = ServiceConfiguration(Interface.MI, Serializer(SchemaType.REGISTRATION, "RegistrationData"))
29
29
 
30
- @mms_endpoint("RegistrationSubmit_Resource", config, RequestType.REGISTRATION, ClientType.BSP)
30
+ @mms_endpoint("RegistrationSubmit_Resource", config, RequestType.REGISTRATION, [ClientType.BSP])
31
31
  def put_resource(self: ClientProto, request: ResourceData) -> ResourceData:
32
32
  """Submit a new resource to the MMS server.
33
33
 
@@ -50,6 +50,7 @@ class RegistrationClientMixin: # pylint: disable=unused-argument
50
50
  "RegistrationQuery_Resource",
51
51
  config,
52
52
  RequestType.REGISTRATION,
53
+ allowed_clients=[ClientType.BSP, ClientType.TSO],
53
54
  resp_envelope_type=RegistrationSubmit,
54
55
  resp_data_type=ResourceData,
55
56
  )
@@ -14,7 +14,7 @@ from mms_client.utils.web import ClientType
14
14
  class AudienceError(ValueError):
15
15
  """Error raised when an invalid audience is provided."""
16
16
 
17
- def __init__(self, method: str, allowed: ClientType, audience: ClientType):
17
+ def __init__(self, method: str, allowed: List[ClientType], audience: ClientType):
18
18
  """Initialize the error.
19
19
 
20
20
  Arguments:
@@ -22,8 +22,13 @@ class AudienceError(ValueError):
22
22
  allowed (str): The allowed audience.
23
23
  audience (str): The invalid audience.
24
24
  """
25
+ inner = (
26
+ f"'{allowed[0].name}' is"
27
+ if len(allowed) == 1
28
+ else f"""{" or ".join([f"'{a.name}'" for a in allowed])} are"""
29
+ )
25
30
  self.method = method
26
- self.message = f"{method}: Invalid client type, '{audience.name}' provided. Only '{allowed.name}' is supported."
31
+ self.message = f"{method}: Invalid client type, '{audience.name}' provided. Only {inner} supported."
27
32
  self.allowed = allowed
28
33
  self.audience = audience
29
34
  super().__init__(self.message)
mms_client/utils/web.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Contains the HTTP/web layer for communicating with the MMS server."""
2
2
 
3
- from enum import Enum
3
+ from enum import StrEnum
4
4
  from logging import Logger
5
5
  from pathlib import Path
6
6
  from typing import List
@@ -21,17 +21,19 @@ from mms_client.types.transport import MmsRequest
21
21
  from mms_client.types.transport import MmsResponse
22
22
 
23
23
 
24
- class ClientType(Enum):
24
+ class ClientType(StrEnum):
25
25
  """Identifies the type of client to use.
26
26
 
27
- The client can be either "bsp" (Balancing Service Provider) or "tso" (Transmission System Operator).
27
+ The client can be either "bsp" (Balancing Service Provider), "mo" (Market Operator), or "tso" (Transmission System
28
+ Operator).
28
29
  """
29
30
 
30
31
  BSP = "bsp"
32
+ MO = "mo"
31
33
  TSO = "tso"
32
34
 
33
35
 
34
- class Interface(Enum):
36
+ class Interface(StrEnum):
35
37
  """Identifies the type of interface to use.
36
38
 
37
39
  The interface can be either "omi" (Other Market Initiator) or "mi" (Market Initiator).
@@ -76,32 +78,30 @@ class ServiceEndpoint:
76
78
  self._selected = self.main
77
79
 
78
80
 
79
- # Defines the service endpoints for the BSP and TSO clients for the OMI and MI web services, respectively.
80
- URLS = {
81
- ClientType.BSP: {
82
- Interface.OMI: ServiceEndpoint(
83
- main="https://www5.tdgc.jp/axis2/services/OmiWebService",
84
- backup="https://www6.tdgc.jp/axis2/services/OmiWebService",
85
- test="https://www7.tdgc.jp/axis2/services/OmiWebService",
86
- ),
87
- Interface.MI: ServiceEndpoint(
88
- main="https://www2.tdgc.jp/axis2/services/MiWebService",
89
- backup="https://www3.tdgc.jp/axis2/services/MiWebService",
90
- test="https://www4.tdgc.jp/axis2/services/MiWebService",
91
- ),
92
- },
93
- ClientType.TSO: {
94
- Interface.OMI: ServiceEndpoint(
95
- main="https://maiwlba103v07.tdgc.jp/axis2/services/OmiWebService",
96
- backup="https://mbiwlba103v07.tdgc.jp/axis2/services/OmiWebService",
97
- test="https://mbiwlba103v08.tdgc.jp/axis2/services/OmiWebService",
98
- ),
99
- Interface.MI: ServiceEndpoint(
100
- main="https://maiwlba103v03.tdgc.jp/axis2/services/MiWebService",
101
- backup="https://mbiwlba103v03.tdgc.jp/axis2/services/MiWebService",
102
- test="https://mbiwlba103v06.tdgc.jp/axis2/services/MiWebService",
103
- ),
104
- },
81
+ # Defines the service endpoints for the BSP, MO and TSO clients for the OMI and MI web services, respectively.
82
+ BSP_MO_URLS = {
83
+ Interface.OMI: ServiceEndpoint(
84
+ main="https://www5.tdgc.jp/axis2/services/OmiWebService",
85
+ backup="https://www6.tdgc.jp/axis2/services/OmiWebService",
86
+ test="https://www7.tdgc.jp/axis2/services/OmiWebService",
87
+ ),
88
+ Interface.MI: ServiceEndpoint(
89
+ main="https://www2.tdgc.jp/axis2/services/MiWebService",
90
+ backup="https://www3.tdgc.jp/axis2/services/MiWebService",
91
+ test="https://www4.tdgc.jp/axis2/services/MiWebService",
92
+ ),
93
+ }
94
+ TSO_URLS = {
95
+ Interface.OMI: ServiceEndpoint(
96
+ main="https://maiwlba103v07.tdgc.jp/axis2/services/OmiWebService",
97
+ backup="https://mbiwlba103v07.tdgc.jp/axis2/services/OmiWebService",
98
+ test="https://mbiwlba103v08.tdgc.jp/axis2/services/OmiWebService",
99
+ ),
100
+ Interface.MI: ServiceEndpoint(
101
+ main="https://maiwlba103v03.tdgc.jp/axis2/services/MiWebService",
102
+ backup="https://mbiwlba103v03.tdgc.jp/axis2/services/MiWebService",
103
+ test="https://mbiwlba103v06.tdgc.jp/axis2/services/MiWebService",
104
+ ),
105
105
  }
106
106
 
107
107
 
@@ -158,8 +158,12 @@ class ZWrapper:
158
158
  test (bool): If True, use the test service endpoint. This is useful for testing the client.
159
159
  """
160
160
  # First, we'll check that the client is valid. If it's not, we'll raise a ValueError.
161
- if client not in URLS:
162
- raise ValueError(f"Invalid client, '{client}'. Only 'bsp' and 'tso' are supported.")
161
+ if client in [ClientType.BSP, ClientType.MO]:
162
+ urls = BSP_MO_URLS
163
+ elif client == ClientType.TSO:
164
+ urls = TSO_URLS
165
+ else:
166
+ raise ValueError(f"Invalid client, '{client}'. Only 'bsp', 'mo', and 'tso' are supported.")
163
167
 
164
168
  # We need to determine the service port and location of the WSDL file based on the given interface. If the
165
169
  # interface is neither "omi" nor "mi", we raise a ValueError.
@@ -172,7 +176,7 @@ class ZWrapper:
172
176
  raise ValueError(f"Invalid interface, '{self._interface}'. Only 'mi' and 'omi' are supported.")
173
177
 
174
178
  # Next, we need to select the correct service endpoint based on the given client and interface.
175
- self._endpoint = URLS[client][self._interface]
179
+ self._endpoint = urls[self._interface]
176
180
  self._endpoint.select(test=test)
177
181
 
178
182
  # Now, we need to create a new session and mount the PKCS12 adapter to it. This is necessary for
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mms-client
3
- Version: 1.4.0
3
+ Version: 1.4.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
@@ -33,7 +33,7 @@ Description-Content-Type: text/markdown
33
33
  [![Unit Tests Status](https://github.com/ElectroRoute-Japan/mms-client/actions/workflows/check.yml/badge.svg)](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 is 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.
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 two clients: Balance Service Providers (BSPs) and Transmission Service Operators (TSOs). Most likely you're operating as a BSP, in which case you'll have access to 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.
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
 
@@ -12,10 +12,10 @@ mms_client/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
12
12
  mms_client/security/certs.py,sha256=kNCUFmy18YIxkWKu3mdMmlxmHdft4a6BvtyJ46rA9I4,1489
13
13
  mms_client/security/crypto.py,sha256=M7aIllM3_ZwZgm9nH6QQ6Ig14XCAd6e6WGwqqUbbI1Q,2149
14
14
  mms_client/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- mms_client/services/base.py,sha256=LVAZSIBunvTPoC8BquC1TEUauIcSA7ePZhIrcOFg2aE,26245
16
- mms_client/services/market.py,sha256=aIWi52BFvDmtuipKnSZNprwbFYGYBcV6sZCQqMR_D2g,7574
15
+ mms_client/services/base.py,sha256=dKyMoulyyh5p7L7eBtiY10dLx5HAqn5icUjrheYgv4I,26397
16
+ mms_client/services/market.py,sha256=cxfBrqHeHLHdXWFsS4UFJYMsUBW-m66QYlM0WQcdwEc,7580
17
17
  mms_client/services/omi.py,sha256=h6cM5U3-iSm0YiIaJwqYTZeI5uhLbA7FPxh_qy_3qww,521
18
- mms_client/services/registration.py,sha256=46Scntwlc9CtCO-tV6uEnr4NrVGJiUqhvksII13CGAE,3651
18
+ mms_client/services/registration.py,sha256=lq5eC3M5BX74WD8oAPGOu4ExxwDSled4iGC9aJzzFPI,3711
19
19
  mms_client/services/report.py,sha256=HYVJNwEHo6ZC6497UqO5y1IqZ2ga3kVH5AepdxhYfug,537
20
20
  mms_client/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  mms_client/types/award.py,sha256=BWE9V_KHXpg_cW1LZsetVrPs2hZDOklvRpNnoZtmR3k,14139
@@ -29,10 +29,10 @@ mms_client/types/resource.py,sha256=TQnY3JLHRgQhQrG6ISquw-BQgKSr8TGuqn9ItWxWz_w,
29
29
  mms_client/types/transport.py,sha256=DPjWs34UW915GkUCJWKuDZmsjS6mRdRXgcGISduN_Bc,4399
30
30
  mms_client/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  mms_client/utils/auditing.py,sha256=nRa2SI21B3N3ZhbGhgooHAPZ81J1NZ9e4JU1ucEKM-g,1693
32
- mms_client/utils/errors.py,sha256=jYdlG4OPI82s0fJcXCRNlKeEixDUSSAxjs_7C16qVL4,2306
32
+ mms_client/utils/errors.py,sha256=ZiAmCCp8ntUyPzaBST4pAItAHYW7vYcNeSWftLf2uII,2475
33
33
  mms_client/utils/serialization.py,sha256=vTJZSjhfTph2tDAYbLAhnz89i0qXJ8FINGdve67cJOU,29534
34
- mms_client/utils/web.py,sha256=KtG2ffQL14eOyNfiipGHB5dSotS32ecTLmS-7gnyX1g,10020
35
- mms_client-1.4.0.dist-info/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
36
- mms_client-1.4.0.dist-info/METADATA,sha256=mkJwPYQwunR0QRLqRZmCybkiCiM_FirlqrvnBx7AQXg,16154
37
- mms_client-1.4.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
38
- mms_client-1.4.0.dist-info/RECORD,,
34
+ mms_client/utils/web.py,sha256=sS7CriPh-5lAEKJh4YkAjXKhRr9SzwfosUOWXFFhq-w,10094
35
+ mms_client-1.4.1.dist-info/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
36
+ mms_client-1.4.1.dist-info/METADATA,sha256=eXxAN45UXZTb1bX5_YJetGscOPOtF2nLwUsYuUtnrn0,16354
37
+ mms_client-1.4.1.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
38
+ mms_client-1.4.1.dist-info/RECORD,,