mms-client 1.0.5__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.
Files changed (34) hide show
  1. mms_client-1.0.5/LICENSE +24 -0
  2. mms_client-1.0.5/PKG-INFO +202 -0
  3. mms_client-1.0.5/README.md +180 -0
  4. mms_client-1.0.5/pyproject.toml +104 -0
  5. mms_client-1.0.5/src/mms_client/__init__.py +0 -0
  6. mms_client-1.0.5/src/mms_client/client.py +14 -0
  7. mms_client-1.0.5/src/mms_client/py.typed +0 -0
  8. mms_client-1.0.5/src/mms_client/schemas/wsdl/mi-web-service-jbms.wsdl +276 -0
  9. mms_client-1.0.5/src/mms_client/schemas/wsdl/omi-web-service.wsdl +262 -0
  10. mms_client-1.0.5/src/mms_client/schemas/xsd/mi-market.xsd +2395 -0
  11. mms_client-1.0.5/src/mms_client/schemas/xsd/mi-outbnd-reports.xsd +1489 -0
  12. mms_client-1.0.5/src/mms_client/schemas/xsd/mi-report.xsd +379 -0
  13. mms_client-1.0.5/src/mms_client/schemas/xsd/mpr.xsd +1817 -0
  14. mms_client-1.0.5/src/mms_client/schemas/xsd/omi.xsd +793 -0
  15. mms_client-1.0.5/src/mms_client/security/__init__.py +0 -0
  16. mms_client-1.0.5/src/mms_client/security/certs.py +44 -0
  17. mms_client-1.0.5/src/mms_client/security/crypto.py +57 -0
  18. mms_client-1.0.5/src/mms_client/services/__init__.py +0 -0
  19. mms_client-1.0.5/src/mms_client/services/base.py +591 -0
  20. mms_client-1.0.5/src/mms_client/services/market.py +107 -0
  21. mms_client-1.0.5/src/mms_client/services/omi.py +13 -0
  22. mms_client-1.0.5/src/mms_client/services/registration.py +13 -0
  23. mms_client-1.0.5/src/mms_client/services/report.py +13 -0
  24. mms_client-1.0.5/src/mms_client/types/__init__.py +0 -0
  25. mms_client-1.0.5/src/mms_client/types/base.py +272 -0
  26. mms_client-1.0.5/src/mms_client/types/enums.py +18 -0
  27. mms_client-1.0.5/src/mms_client/types/fields.py +153 -0
  28. mms_client-1.0.5/src/mms_client/types/market.py +61 -0
  29. mms_client-1.0.5/src/mms_client/types/offer.py +163 -0
  30. mms_client-1.0.5/src/mms_client/types/transport.py +130 -0
  31. mms_client-1.0.5/src/mms_client/utils/__init__.py +0 -0
  32. mms_client-1.0.5/src/mms_client/utils/errors.py +66 -0
  33. mms_client-1.0.5/src/mms_client/utils/serialization.py +513 -0
  34. mms_client-1.0.5/src/mms_client/utils/web.py +220 -0
@@ -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,180 @@
1
+ [![Unit Tests Status](https://github.com/ElectroRoute-Japan/mms-client/actions/workflows/check.yml/badge.svg)](https://github.com/ElectroRoute-Japan/mms-client/actions)
2
+
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 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.
5
+
6
+ # Communication
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:
8
+ - Attachments data, included as a list of elements with the binary data in a base-64 encoded format
9
+ - Payload data, encoded in a base-64 encoded format
10
+ - Payload signature, which is the SHA256 hash of the payload XML data, then signed with an RSA key, in a base-64 encoded format
11
+
12
+ 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.
13
+
14
+ ## Client Types
15
+ 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.
16
+
17
+ Should you request an endpoint which you are not authorized for, you will receive an `mms_client.utils.errors.AudienceError`.
18
+
19
+ ## Interface Type
20
+ 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.
21
+
22
+ # Object Hierarchy
23
+ 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:
24
+
25
+ ![MMS Client Object Hierarchy](/docs/mmsclient_hierarchy.drawio.png)
26
+
27
+ 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.
28
+
29
+ ## Requests
30
+ 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.
31
+
32
+ ## Responses
33
+ 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.
34
+
35
+ ### BaseResponse
36
+ 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.
37
+
38
+ ### Response & MultiResponse
39
+ 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.
40
+
41
+ ## Envelopes
42
+ 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`.
43
+
44
+ ## Payloads
45
+ 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.
46
+
47
+ ## Validation
48
+ 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.
49
+
50
+ ### ProcessingStatistics
51
+ 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.
52
+
53
+ ### ResponseCommon
54
+ 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.
55
+
56
+ ### Messages
57
+ 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.
58
+
59
+ # Authentication
60
+ 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.
61
+
62
+ ## Certificate
63
+ 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:
64
+
65
+ ```python
66
+
67
+ from pathlib import Path
68
+ from mms_client.security.certs import Certificate
69
+
70
+ # Create a cert from a path to a certificate
71
+ cert = Certificate("/path/to/my/cert.p12", "my_passphrase")
72
+
73
+ # Create a cert using a Path object
74
+ cert = Certificate(Path("/path/to/my") / "cert.p12", "my_passphrase")
75
+
76
+ # Create a cert using the raw certificate data
77
+ with open("/path/to/my/cert.p12", "rb") as f:
78
+ cert = Certificate(f.read(), "my_passphrase")
79
+ ```
80
+
81
+ 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.
82
+
83
+ ## Participant Name
84
+ 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.
85
+
86
+ ## User Name
87
+ 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.
88
+
89
+ # Examples
90
+ This section includes some examples of creating a client and requesting data.
91
+
92
+ ## Basic Example
93
+ Create the base client and then add an offer.
94
+
95
+ ```python
96
+ from pendulum import DateTime
97
+
98
+ from mms_client.client import MmsClient
99
+ from mms_client.security.certs import Certificate
100
+ from mms_client.types.market import MarketType
101
+ from mms_client.types.offer import Direction, OfferData, OfferStack
102
+ from mms_client.utils.web import ClientType
103
+
104
+ # Create a certificate
105
+ cert = Certificate("/path/to/my/cert.p12", "fake_passphrase")
106
+
107
+ # Create a new MMS client
108
+ client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert)
109
+
110
+ # Create our request offer
111
+ request_offer = OfferData(
112
+ stack=[
113
+ OfferStack(
114
+ number=1,
115
+ unit_price=100,
116
+ minimum_quantity_kw=100,
117
+ ),
118
+ ],
119
+ resource="FAKE_RESO",
120
+ start=DateTime(2024, 3, 15, 12),
121
+ end=DateTime(2024, 3, 15, 21),
122
+ direction=Direction.SELL,
123
+ )
124
+
125
+ # Submit our offer to the client, returning the completed offer
126
+ response_offer = client.put_offer(
127
+ request=request_offer,
128
+ market_type=MarketType.DAY_AHEAD,
129
+ days=1,
130
+ )
131
+
132
+ # Print out the response offer
133
+ print(response_offer)
134
+ ```
135
+
136
+ 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.
137
+
138
+ ## Connecting to a Test Server
139
+ If you want to test your MMS connection, you can try using the test server:
140
+
141
+ ```python
142
+ client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert, is_admin=True)
143
+ ```
144
+
145
+ ## Connecting as a Market Admin
146
+ If you're connecting as a market operator (MO), you can connect in admin mode:
147
+
148
+ ```python
149
+ client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert, test=True)
150
+ ```
151
+
152
+ ## Log Settings
153
+ The client also supports injection of a custom logger. The default logger is named "MMS Client".
154
+
155
+ ```python
156
+ client = MmsClient(participant="F100", user="FAKEUSER", client_type=ClientType.BSP, cert, logger=my_logger)
157
+ ```
158
+
159
+ The client currently logs a number of informational, debug and error messages. You can freely change the logging level yourself.
160
+
161
+ # Completeness
162
+ This client is not complete. Currently, it supports the following endpoints:
163
+ - MarketSubmit_OfferData
164
+ - MarketQuery_OfferQuery
165
+ - MarketCancel_OfferCancel
166
+
167
+ 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.
168
+
169
+ # Installation
170
+ We have a package on PyPI so installation is as easy as doing:
171
+
172
+ ```
173
+ pip install mms-client
174
+ ```
175
+
176
+ or
177
+
178
+ ```
179
+ poetry add mms-client
180
+ ```
@@ -0,0 +1,104 @@
1
+ [tool.poetry]
2
+ name = "mms_client"
3
+ version = "v1.0.5"
4
+ description = "API client for accessing the MMS"
5
+ authors = ["Ryan Wood <ryan.wood@electroroute.co.jp>"]
6
+ readme = "README.md"
7
+ packages = [{ include = "mms_client", from = "src" }]
8
+
9
+ [tool.poetry.dependencies]
10
+ python = "^3.11"
11
+ requests = "^2.31.0"
12
+ zeep = "^4.2.1"
13
+ requests-pkcs12 = "^1.24"
14
+ pydantic = "^2.6.3"
15
+ pendulum = "^3.0.0"
16
+ xmlschema = "^3.0.2"
17
+ cryptography = "^42.0.5"
18
+ pydantic-xml = "^2.9.0"
19
+ lxml = "^5.1.0"
20
+ backoff = "^2.2.1"
21
+
22
+ [tool.poetry.group.dev.dependencies]
23
+ black = "^24.2.0"
24
+ isort = "^5.13.2"
25
+ mypy = "^1.8.0"
26
+ pylint = "^3.1.0"
27
+ xenon = "^0.9.1"
28
+ pydocstyle = "^6.3.0"
29
+ pre-commit = "^3.6.2"
30
+ pytest = "~8.0"
31
+ pytest-xdist = "^3.5.0"
32
+ pytest-cov = "^4.1.0"
33
+ pytest-mock = "^3.12.0"
34
+ mock = "^5.1.0"
35
+ lxml-stubs = "^0.5.1"
36
+ pyfakefs = "^5.3.5"
37
+ pydantic-extra-types = "^2.6.0"
38
+ responses = "^0.25.0"
39
+ types-urllib3 = "^1.26.25.14"
40
+
41
+ [tool.black]
42
+ line-length = 120
43
+
44
+ [tool.isort]
45
+ force_single_line = true
46
+ line_length = 120
47
+
48
+ [tool.mypy]
49
+ exclude = [
50
+ '^tests/'
51
+ ]
52
+ scripts_are_modules = true
53
+ plugins = [
54
+ "pydantic_xml.mypy"
55
+ ]
56
+
57
+ [[tool.mypy.overrides]]
58
+ module = [
59
+ 'requests_pkcs12',
60
+ 'dicttoxml.*'
61
+ ]
62
+ ignore_missing_imports = true
63
+
64
+ [tool.pydocstyle]
65
+ match = '((?!test).)*\.py'
66
+ inherit = false
67
+ ignore = ["D200", "D203", "D213", "D406", "D407", "D413", "D104"]
68
+
69
+ [tool.pylint.main]
70
+ # See https://pylint.readthedocs.io/en/latest/user_guide/configuration/all-options.html for supported configuration
71
+ disable = [
72
+ # This is already covered by Pydocstyle
73
+ "missing-function-docstring",
74
+ # Disable warning for using f string in the logging
75
+ "logging-fstring-interpolation",
76
+ # We plan to extend classes in the future
77
+ "too-few-public-methods",
78
+ # This check was too aggressive, complaining about similarities in docstrings
79
+ "duplicate-code",
80
+ ]
81
+ max-line-length = 120
82
+ max-args = 10
83
+ ignore = ["docs", "tests"]
84
+ source-roots = ["src/mms_client"]
85
+ recursive = "true"
86
+ extension-pkg-whitelist = ["lxml"]
87
+
88
+ [tool.pylint.MASTER]
89
+ ignore-paths = '^tests/.*$'
90
+
91
+ [tool.pytest.ini_options]
92
+ addopts = "-n auto --cov=src/mms_client"
93
+ testpaths = ["tests"]
94
+ filterwarnings = "ignore::DeprecationWarning"
95
+
96
+ [tool.coverage.run]
97
+ omit = ["*/__init__.py"]
98
+
99
+ [tool.coverage.report]
100
+ fail_under = 100
101
+
102
+ [build-system]
103
+ requires = ["poetry-core"]
104
+ build-backend = "poetry.core.masonry.api"
File without changes
@@ -0,0 +1,14 @@
1
+ """Contains the client layer for communicating with the MMS server."""
2
+
3
+ from mms_client.services.base import BaseClient
4
+ from mms_client.services.market import MarketClientMixin
5
+ from mms_client.services.omi import OMIClientMixin
6
+ from mms_client.services.registration import RegistrationClientMixin
7
+ from mms_client.services.report import ReportClientMixin
8
+
9
+
10
+ class MmsClient(BaseClient, MarketClientMixin, RegistrationClientMixin, ReportClientMixin, OMIClientMixin):
11
+ """User client for the MMS server.
12
+
13
+ This class is used to communicate with the MMS server.
14
+ """
File without changes