mms-client 1.0.5__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.
@@ -0,0 +1,220 @@
1
+ """Contains the HTTP/web layer for communicating with the MMS server."""
2
+
3
+ from enum import Enum
4
+ from logging import Logger
5
+ from pathlib import Path
6
+
7
+ from backoff import expo
8
+ from backoff import on_exception
9
+ from requests import Session
10
+ from requests_pkcs12 import Pkcs12Adapter
11
+ from zeep import Client
12
+ from zeep import Transport
13
+ from zeep.cache import SqliteCache
14
+ from zeep.exceptions import TransportError
15
+ from zeep.xsd.valueobjects import CompoundValue
16
+
17
+ from mms_client.types.transport import MmsRequest
18
+ from mms_client.types.transport import MmsResponse
19
+
20
+
21
+ class ClientType(Enum):
22
+ """Identifies the type of client to use.
23
+
24
+ The client can be either "bsp" (Balancing Service Provider) or "tso" (Transmission System Operator).
25
+ """
26
+
27
+ BSP = "bsp"
28
+ TSO = "tso"
29
+
30
+
31
+ class Interface(Enum):
32
+ """Identifies the type of interface to use.
33
+
34
+ The interface can be either "omi" (Other Market Initiator) or "mi" (Market Initiator).
35
+ """
36
+
37
+ OMI = "omi"
38
+ MI = "mi"
39
+
40
+
41
+ class ServiceEndpoint:
42
+ """Represents the endpoints for the main, backup, and test servers for a given web service."""
43
+
44
+ def __init__(self, main: str, backup: str, test: str):
45
+ """Create a new service endpoint with the given main, backup, and test URLs.
46
+
47
+ This object is meant to obfuscate the service endpoint so that the web client can automatically switch between
48
+ the main and backup endpoints in case of an error. Alternatively, this allows the client to switch to the test
49
+ endpoint for testing purposes.
50
+ """
51
+ self.main = main
52
+ self.backup = backup
53
+ self.test = test
54
+ self._selected = self.main
55
+
56
+ @property
57
+ def selected(self) -> str:
58
+ """Return the currently selected URL."""
59
+ return self._selected
60
+
61
+ def select(self, error: bool = False, test: bool = False):
62
+ """Select the main, backup, or test URL based on the given parameters.
63
+
64
+ Arguments:
65
+ error (bool): If True, select the backup URL.
66
+ test (bool): If True, select the test URL.
67
+ """
68
+ if error:
69
+ self._selected = self.backup
70
+ elif test:
71
+ self._selected = self.test
72
+ else:
73
+ self._selected = self.main
74
+
75
+
76
+ # Defines the service endpoints for the BSP and TSO clients for the OMI and MI web services, respectively.
77
+ URLS = {
78
+ ClientType.BSP: {
79
+ Interface.OMI: ServiceEndpoint(
80
+ main="https://www5.tdgc.jp/axis2/services/OmiWebService",
81
+ backup="https://www6.tdgc.jp/axis2/services/OmiWebService",
82
+ test="https://www7.tdgc.jp/axis2/services/OmiWebService",
83
+ ),
84
+ Interface.MI: ServiceEndpoint(
85
+ main="https://www2.tdgc.jp/axis2/services/MiWebService",
86
+ backup="https://www3.tdgc.jp/axis2/services/MiWebService",
87
+ test="https://www4.tdgc.jp/axis2/services/MiWebService",
88
+ ),
89
+ },
90
+ ClientType.TSO: {
91
+ Interface.OMI: ServiceEndpoint(
92
+ main="https://maiwlba103v07.tdgc.jp/axis2/services/OmiWebService",
93
+ backup="https://mbiwlba103v07.tdgc.jp/axis2/services/OmiWebService",
94
+ test="https://mbiwlba103v08.tdgc.jp/axis2/services/OmiWebService",
95
+ ),
96
+ Interface.MI: ServiceEndpoint(
97
+ main="https://maiwlba103v03.tdgc.jp/axis2/services/MiWebService",
98
+ backup="https://mbiwlba103v03.tdgc.jp/axis2/services/MiWebService",
99
+ test="https://mbiwlba103v06.tdgc.jp/axis2/services/MiWebService",
100
+ ),
101
+ },
102
+ }
103
+
104
+
105
+ # The original service binding names, to be mapped to our actual endpoints
106
+ SERVICE_BINDINGS = {
107
+ Interface.MI: "{urn:abb.com:project/mms}MiWebServiceSOAP",
108
+ Interface.OMI: "{urn:ws.web.omi.co.jp}OmiWebServiceSOAP",
109
+ }
110
+
111
+
112
+ def fatal_code(e: TransportError) -> bool:
113
+ """Return True if the given exception is a fatal HTTP error code.
114
+
115
+ Arguments:
116
+ e (RequestException): The exception to check.
117
+ """
118
+ return 400 <= e.status_code < 500
119
+
120
+
121
+ class ZWrapper:
122
+ """Wrapper for the Zeep client that handles the HTTP/web layer for communicating with the MMS server.
123
+
124
+ Note that this is the lowest layer of the client, and is only responsible for handing off the final request to the
125
+ Zeep client. This class is meant to be used by the higher-level client classes that take in the actual request and
126
+ return the actual response.
127
+ """
128
+
129
+ def __init__(
130
+ self,
131
+ client: ClientType,
132
+ interface: Interface,
133
+ adapter: Pkcs12Adapter,
134
+ logger: Logger,
135
+ cache: bool = True,
136
+ test: bool = False,
137
+ ):
138
+ """Create a new Zeep wrapper object for a specific MMS service endpoint.
139
+
140
+ Arguments:
141
+ client (ClientType): The type of client to use. This can be either "bsp" (Balancing Service Provider) or
142
+ "tso" (Transmission System Operator). This will determine which service endpoint to
143
+ use.
144
+ interface (Interface): The type of interface to use. This can be either "omi" (Other Market Initiator) or
145
+ "mi" (Market Initiator). This will determine which service endpoint to use as well
146
+ as the service and port to use.
147
+ adapter (Pkcs12Adapter): The PKCS12 adapter containing the certificate and private key to use for
148
+ authenticating with the MMS server.
149
+ logger (Logger): The logger to use for instrumentation.
150
+ cache (bool): If True, use a cache for the Zeep client. This is useful for avoiding repeated
151
+ lookups of the WSDL file, which should result in lower latency.
152
+ test (bool): If True, use the test service endpoint. This is useful for testing the client.
153
+ """
154
+ # First, we'll check that the client is valid. If it's not, we'll raise a ValueError.
155
+ if client not in URLS:
156
+ raise ValueError(f"Invalid client, '{client}'. Only 'bsp' and 'tso' are supported.")
157
+
158
+ # We need to determine the service port and location of the WSDL file based on the given interface. If the
159
+ # interface is neither "omi" nor "mi", we raise a ValueError.
160
+ self._interface = interface
161
+ if self._interface == Interface.MI:
162
+ location = Path(__file__).parent.parent / "schemas" / "wsdl" / "mi-web-service-jbms.wsdl"
163
+ elif self._interface == Interface.OMI:
164
+ location = Path(__file__).parent.parent / "schemas" / "wsdl" / "omi-web-service.wsdl"
165
+ else:
166
+ raise ValueError(f"Invalid interface, '{self._interface}'. Only 'mi' and 'omi' are supported.")
167
+
168
+ # Next, we need to select the correct service endpoint based on the given client and interface.
169
+ self._endpoint = URLS[client][self._interface]
170
+ self._endpoint.select(test=test)
171
+
172
+ # Now, we need to create a new session and mount the PKCS12 adapter to it. This is necessary for
173
+ # authenticating with the MMS server. Note, that if we are not in test mode, we also need to mount the
174
+ # PKCS12 adapter to the backup endpoint to make sure we can switch between the main and backup endpoints in
175
+ # case of an error.
176
+ sess = Session()
177
+ sess.mount(self._endpoint.selected, adapter)
178
+ if not test:
179
+ sess.mount(self._endpoint.backup, adapter)
180
+
181
+ # Finally, we create the Zeep client with the given WSDL file location, session, and cache settings and then,
182
+ # from that client, we create the SOAP service with the given service binding and selected endpoint.
183
+ self._logger = logger
184
+ self._client = Client(
185
+ wsdl=str(location.resolve()),
186
+ transport=Transport(cache=SqliteCache() if cache else None, session=sess),
187
+ )
188
+ self._create_service()
189
+
190
+ @on_exception(expo, TransportError, max_tries=3, giveup=fatal_code) # type: ignore[arg-type]
191
+ def submit(self, req: MmsRequest) -> MmsResponse:
192
+ """Submit the given request to the MMS server and return the response."""
193
+ try:
194
+ self._logger.debug(f"Submitting MMS request request to {self._interface.name} service")
195
+
196
+ # Submit the request to the MMS server and retrieve the response
197
+ resp: CompoundValue = self._service["submitAttachment"](**req.to_arguments())
198
+
199
+ # Validate the response and return it
200
+ return MmsResponse.model_validate(resp.__values__)
201
+ except TransportError as e:
202
+ # If we got a server fault error, then we'll switch to the backup endpoint. In any case, we'll raise the
203
+ # exception so that our back-off can handle it or pass the exception up the stack.
204
+ self._logger.error(
205
+ f"MMS request to {self._interface.name} service failed with status code: {e.status_code}",
206
+ exc_info=e,
207
+ )
208
+ if e.status_code >= 500:
209
+ self._logger.warning(f"MMS server error, switching to backup endpoint: {self._endpoint.backup}")
210
+ self._endpoint.select(error=True)
211
+ self._create_service()
212
+ raise
213
+
214
+ def _create_service(self):
215
+ """Create a new SOAP service with the currently selected endpoint.
216
+
217
+ This is useful for switching between the main and backup endpoints in case of an error.
218
+ """
219
+ self._logger.debug(f"Creating new {self._interface.name} service with endpoint: {self._endpoint.selected}")
220
+ self._service = self._client.create_service(SERVICE_BINDINGS[self._interface], self._endpoint.selected)
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <https://unlicense.org>
@@ -0,0 +1,202 @@
1
+ Metadata-Version: 2.1
2
+ Name: mms-client
3
+ Version: 1.0.5
4
+ Summary: API client for accessing the MMS
5
+ Author: Ryan Wood
6
+ Author-email: ryan.wood@electroroute.co.jp
7
+ Requires-Python: >=3.11,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Requires-Dist: backoff (>=2.2.1,<3.0.0)
11
+ Requires-Dist: cryptography (>=42.0.5,<43.0.0)
12
+ Requires-Dist: lxml (>=5.1.0,<6.0.0)
13
+ Requires-Dist: pendulum (>=3.0.0,<4.0.0)
14
+ Requires-Dist: pydantic (>=2.6.3,<3.0.0)
15
+ Requires-Dist: pydantic-xml (>=2.9.0,<3.0.0)
16
+ Requires-Dist: requests (>=2.31.0,<3.0.0)
17
+ Requires-Dist: requests-pkcs12 (>=1.24,<2.0)
18
+ Requires-Dist: xmlschema (>=3.0.2,<4.0.0)
19
+ Requires-Dist: zeep (>=4.2.1,<5.0.0)
20
+ Description-Content-Type: text/markdown
21
+
22
+ [![Unit Tests Status](https://github.com/ElectroRoute-Japan/mms-client/actions/workflows/check.yml/badge.svg)](https://github.com/ElectroRoute-Japan/mms-client/actions)
23
+
24
+ # Overview
25
+ 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.
26
+
27
+ # Communication
28
+ 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:
29
+ - Attachments data, included as a list of elements with the binary data in a base-64 encoded format
30
+ - Payload data, encoded in a base-64 encoded format
31
+ - Payload signature, which is the SHA256 hash of the payload XML data, then signed with an RSA key, in a base-64 encoded format
32
+
33
+ After the data has been converted and added to the outer request object, it is sent to the appropriate server endpoint via a Zeep client. The client certificate is also injected into the request using a PCKS12 adaptor.
34
+
35
+ ## Client Types
36
+ 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.
37
+
38
+ Should you request an endpoint which you are not authorized for, you will receive an `mms_client.utils.errors.AudienceError`.
39
+
40
+ ## Interface Type
41
+ The MMS has two separate endpoints, depending on whether you want to access market information (MI), or other market information (OMI). Really informative, I know. Fortunately, we have designed this API in such a way that the distinction around which interface is which is unnecessary for you to know. However, note that separate Zeep clients will be maintained for each interface. These will be created as needed, which endpoints requiring the associated interface are first called. These are both setup to cache schemas to improve performance, as well. The memory required for these clients is still light, but you may want to reduce that further based on your application.
42
+
43
+ # Object Hierarchy
44
+ The object hierarchy contained in this project is not trivial, and perhaps that reflects a bit of overengineering on our part. However, we chose the paradigm we did to reduce the number of types which had to be exposed to the user. The diagram below indicates how this hierarchy works:
45
+
46
+ ![MMS Client Object Hierarchy](/docs/mmsclient_hierarchy.drawio.png)
47
+
48
+ Note that there are some types here that are shared between the request and response: mainly, anything inheriting from `mms_client.types.base.Envelop` or `mms_client.types.base.Payload`. For users of the client, only the Payload types need ever be used. Everything else has been obfuscated away, so it is unecessary for the user to have access to these. However, we will explain our reasoning here to provide additional context.
49
+
50
+ ## Requests
51
+ The primary request DTO is the `mms_client.types.transport.MmsRequest` object. The majority of the fields here are metadata specifying how the client should communicate with the MMS server. However, `requestData`, `requestSignature` and `attachmentData` do bear considering as these represent the actual request, a signature used to verify that the request was received, and any attachments to the request, respectively. As mentioned before, `requestData` and `attachementData` can each represent more complex objects, which should be serialized and then encoded as base-64 strings.
52
+
53
+ ## Responses
54
+ The primary response DTO is the `mms_client.types.transport.MmsResponse` object. Similar to the request DTO, this object includes a number of fields containing metadata describing how the response should be interpretted. Currently, only uncompressed XML is supported and any other response type will result in an `mms_client.utils.errors.MmsClientError`. Again, the `requestData` and `attachmentData` fields contain the payload data, encoded as base-64 strings.
55
+
56
+ ### BaseResponse
57
+ This object represents the top-level XML element contained within the `MmsResponse`. It contains all other response data. Note that the structure of this object will not mimic the structure of the XML response, as that structure is incompatible with the philosophy of this library. However, all the information has been preserved. This type contains a number of validation elements as well as the envelope.
58
+
59
+ ### Response & MultiResponse
60
+ These objects contain the actual payload data and inherit from `BaseResponse`. These are what will actually be returned from the deserialization process. They also contain validation data for the top-level paylaod item(s). The difference between `Response` and `MultiResponse` is that the former contains a single item and the latter contains a list.
61
+
62
+ ## Envelopes
63
+ Not to be confused with the SOAP envelope, this envelope contains the method parameters used to send requests to the MMS server. For example, if you wanted to send a market-related request, this would take on the form of a `MarketQuery`, `MarketSubmit` or `MarketCancel` object. This is combined with the payload during the serialization process to produce the final XML payload before injecting it into the `MmsRequest`. During the deserialization process, this is extracted from the XML paylod on the `MmsResponse` object. Each of these should inherit from `mms_client.types.base.Envelope`.
64
+
65
+ ## Payloads
66
+ This is the actual data that is sent to and returned from client methods and represent the actual "data", as contained on the MMS server. As such, these represent the first-class objects within this library. During the serialization process, this data is inserted into the envelope before being serialized to XML. During the deserialization process, these objects are extracted from the envelope XML and converted to the object therein. Each of these should inherit from `mms_client.types.base.Payload`. Note, however, that not everything that inherits from this class will represent a top-level object.
67
+
68
+ ## Validation
69
+ There are a number of validation objects returned with the response, each with its own purpose. Any case resulting in a validation failure will cause an `mms_client.utils.errors.MMSValidationError` to be raised.
70
+
71
+ ### ProcessingStatistics
72
+ This object describes the number of items in the request that were received, the number of valid items, the number of invalid items, the number of items successfully and unsuccessfully returned, respectively. This object also features some information on the latency and receipt time of the request. Given how the MMS determines the values of these fields, this client will check that there are no invalid files and raise an exception otherwise.
73
+
74
+ ### ResponseCommon
75
+ This object can be found on the envelope and top-level payload object(s) within the response. It contains a success flag and a value describing whether or not validation passed. If `success` is True, then `validation` should be either "PASSED" or "NOT_DONE". Any other status indicates a failure and will result in an exception.
76
+
77
+ ### Messages
78
+ Each Payload object will have a collection of messages. These are organized into three categories: information, warnings and errors. Information and warnings will be logged, but will not cause validation failures. Errors will, however, result in an exception being raised.
79
+
80
+ # Authentication
81
+ The authentication scheme used by the MMS has been forced down to this client, but we have tried to make it nicer for our users to ingest. Essentially, to authenticate requests with the MMS server, three pieces of information are required.
82
+
83
+ ## Certificate
84
+ The MMS should have made a certificate available to you when you signed up to access the system. This certificate should be a PCKS#12 file and should have a passphrase associated with it. We have created a convenience class to handle this objects, called `mms_client.security.certs.Certificate`. There are a number of ways the certificate can be created, as demonstrated below:
85
+
86
+ ```python
87
+
88
+ from pathlib import Path
89
+ from mms_client.security.certs import Certificate
90
+
91
+ # Create a cert from a path to a certificate
92
+ cert = Certificate("/path/to/my/cert.p12", "my_passphrase")
93
+
94
+ # Create a cert using a Path object
95
+ cert = Certificate(Path("/path/to/my") / "cert.p12", "my_passphrase")
96
+
97
+ # Create a cert using the raw certificate data
98
+ with open("/path/to/my/cert.p12", "rb") as f:
99
+ cert = Certificate(f.read(), "my_passphrase")
100
+ ```
101
+
102
+ This object can then be used to generate signatures for requests, and also to create a `Pkcs12Adapter`, which is mounted to the HTTP sessions used to send those same requests. As such, this one object represents the single source of authentication for the entire package.
103
+
104
+ ## Participant Name
105
+ This is a 4-digit alphanumerical code that can be used to identify the market participant to which a user belongs, and on behalf of whom a request is being made. This value must be provided when an MMS client is created, and it must match the value encoded in the certificate and also the participant to whom the certificate is registered on the MMS server.
106
+
107
+ ## User Name
108
+ This is a 1-12 digit alphanumerical code that can be used to identify the user making the request. This value must be provided when an MMS client is created, and it must match the value encoded in the certificate and also the user to whom the certificate is registered on the MMS server.
109
+
110
+ # Examples
111
+ This section includes some examples of creating a client and requesting data.
112
+
113
+ ## Basic Example
114
+ Create the base client and then add an offer.
115
+
116
+ ```python
117
+ from pendulum import DateTime
118
+
119
+ from mms_client.client import MmsClient
120
+ from mms_client.security.certs import Certificate
121
+ from mms_client.types.market import MarketType
122
+ from mms_client.types.offer import Direction, OfferData, OfferStack
123
+ from mms_client.utils.web import ClientType
124
+
125
+ # Create a certificate
126
+ cert = Certificate("/path/to/my/cert.p12", "fake_passphrase")
127
+
128
+ # Create a new MMS client
129
+ client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert)
130
+
131
+ # Create our request offer
132
+ request_offer = OfferData(
133
+ stack=[
134
+ OfferStack(
135
+ number=1,
136
+ unit_price=100,
137
+ minimum_quantity_kw=100,
138
+ ),
139
+ ],
140
+ resource="FAKE_RESO",
141
+ start=DateTime(2024, 3, 15, 12),
142
+ end=DateTime(2024, 3, 15, 21),
143
+ direction=Direction.SELL,
144
+ )
145
+
146
+ # Submit our offer to the client, returning the completed offer
147
+ response_offer = client.put_offer(
148
+ request=request_offer,
149
+ market_type=MarketType.DAY_AHEAD,
150
+ days=1,
151
+ )
152
+
153
+ # Print out the response offer
154
+ print(response_offer)
155
+ ```
156
+
157
+ There's a lot of code here but it's not terribly difficult to understand. All this does is pull in the authentication data, create the client, create the payload, submit it to the server and retrieve the response.
158
+
159
+ ## Connecting to a Test Server
160
+ If you want to test your MMS connection, you can try using the test server:
161
+
162
+ ```python
163
+ client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert, is_admin=True)
164
+ ```
165
+
166
+ ## Connecting as a Market Admin
167
+ If you're connecting as a market operator (MO), you can connect in admin mode:
168
+
169
+ ```python
170
+ client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert, test=True)
171
+ ```
172
+
173
+ ## Log Settings
174
+ The client also supports injection of a custom logger. The default logger is named "MMS Client".
175
+
176
+ ```python
177
+ client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert, logger=my_logger)
178
+ ```
179
+
180
+ The client currently logs a number of informational, debug and error messages. You can freely change the logging level yourself.
181
+
182
+ # Completeness
183
+ This client is not complete. Currently, it supports the following endpoints:
184
+ - MarketSubmit_OfferData
185
+ - MarketQuery_OfferQuery
186
+ - MarketCancel_OfferCancel
187
+
188
+ 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.
189
+
190
+ # Installation
191
+ We have a package on PyPI so installation is as easy as doing:
192
+
193
+ ```
194
+ pip install mms-client
195
+ ```
196
+
197
+ or
198
+
199
+ ```
200
+ poetry add mms-client
201
+ ```
202
+
@@ -0,0 +1,34 @@
1
+ mms_client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ mms_client/client.py,sha256=e04kuyY2byONvLcc-etsdaV7mA6mBQLAhd4-wfDitlk,571
3
+ mms_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ mms_client/schemas/wsdl/mi-web-service-jbms.wsdl,sha256=4qSn52d-DF6COeI-53P85sSODQy-C4_yp30yYCjeI84,10702
5
+ mms_client/schemas/wsdl/omi-web-service.wsdl,sha256=pIEKcUWzyChzPJnQ6IpnZYyJJ0_0j3yt8M-1l_su5Q8,9894
6
+ mms_client/schemas/xsd/mi-market.xsd,sha256=YyA-84Ha95BfvbACLaALs7m5U-idV-5yQ8GZfmZS_M8,109679
7
+ mms_client/schemas/xsd/mi-outbnd-reports.xsd,sha256=xdQ_9jekOnqs9YPx0Dg8wsZjoxEp-HLO_ZXhzZl2Qk0,76166
8
+ mms_client/schemas/xsd/mi-report.xsd,sha256=XEHhHCGgK4aeYsmObIrlzkvV8UhbirgoysAEOglvUW8,13276
9
+ mms_client/schemas/xsd/mpr.xsd,sha256=QcnuKFm1WkyZfT_56cINHldZkR-3pU4nHSbFKHkIQS0,69149
10
+ mms_client/schemas/xsd/omi.xsd,sha256=benkYeno_HF6BOIv7gAT79dWz00JccS3XilPLBCKVIU,31524
11
+ mms_client/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ mms_client/security/certs.py,sha256=kNCUFmy18YIxkWKu3mdMmlxmHdft4a6BvtyJ46rA9I4,1489
13
+ mms_client/security/crypto.py,sha256=M7aIllM3_ZwZgm9nH6QQ6Ig14XCAd6e6WGwqqUbbI1Q,2149
14
+ mms_client/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ mms_client/services/base.py,sha256=8yWBeDoBimY9BZeMOkMDV-ggvPTzZHnlqXiv9eo8fIQ,25363
16
+ mms_client/services/market.py,sha256=kwcbXz1Op4hwXbOn_Xsx_qBNuHfvWAxBH6G2f4AWzPI,4892
17
+ mms_client/services/omi.py,sha256=h6cM5U3-iSm0YiIaJwqYTZeI5uhLbA7FPxh_qy_3qww,521
18
+ mms_client/services/registration.py,sha256=MUUp0SDE7F0d_lV5pXNqPhrU87CAZc6AA72AdmRAu6Q,571
19
+ mms_client/services/report.py,sha256=HYVJNwEHo6ZC6497UqO5y1IqZ2ga3kVH5AepdxhYfug,537
20
+ mms_client/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ mms_client/types/base.py,sha256=EMGCDvf_B_8q1YMH-l31c_lV0OdxxAReFNU3zPs0K0Q,9681
22
+ mms_client/types/enums.py,sha256=WzFD9fG9jKFsA91oKyXCAW0oKzUTH3NyUkV4A_N0sXg,329
23
+ mms_client/types/fields.py,sha256=QaNnfU0goAld8VADD_DXZ6qEuUrxISY7rFg5gB6k948,6250
24
+ mms_client/types/market.py,sha256=OKjBIx9aIgaLUci6b5WoB4NZbFncRbbd2FxeAvW1iWw,2588
25
+ mms_client/types/offer.py,sha256=EdxgHlU9FWPuMq3tLYcbILBWnL5SWMsH5Qe7NRhGz9Y,6300
26
+ mms_client/types/transport.py,sha256=SGzDGoTpUvhGpkKxoyJR22z-Ao_Dj5idz97vd6u7fqk,4419
27
+ mms_client/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ mms_client/utils/errors.py,sha256=jYdlG4OPI82s0fJcXCRNlKeEixDUSSAxjs_7C16qVL4,2306
29
+ mms_client/utils/serialization.py,sha256=Zp1pZvrWcbV4p_PERS5e9zNKPSeHQFijYzJiby3y0Pk,25206
30
+ mms_client/utils/web.py,sha256=fcdCtdDrHBPyhIlTcyiuAk3D3TlW8HmUw-wGfpG4KTA,9653
31
+ mms_client-1.0.5.dist-info/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
32
+ mms_client-1.0.5.dist-info/METADATA,sha256=ipQzCCXjjlPxB3CgR_xRVWBp-t22GnEq_lmyC0mlXuU,13782
33
+ mms_client-1.0.5.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
34
+ mms_client-1.0.5.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.6.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any