1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
Initialization
==============
.. _Requests: https://requests.readthedocs.io/en/latest/
.. _Session: https://requests.readthedocs.io/en/latest/user/advanced/#session-objects
**PyOData** requires an external HTTP library which has API compatible with
Session_ from Requests_.
Get the service
---------------
Basic initialization which is going to work for everybody:
.. code-block:: python
import pyodata
import requests
SERVICE_URL = 'http://services.odata.org/V2/Northwind/Northwind.svc/'
northwind = pyodata.Client(SERVICE_URL, requests.Session())
Get the service for async libraries
-----------------------------------
Initialization of the session instance is dependent on particular library,
but also must have API compatible Session_ from Requests_.
.. code-block:: python
import httpx
import aiohttp
SERVICE_URL = 'http://services.odata.org/V2/Northwind/Northwind.svc/'
service_httpx = await pyodata.Client.build_async_client(SERVICE_URL, httpx)
service_aiohttp = await pyodata.Client.build_async_client(SERVICE_URL, aiohttp.ClientSession())
Get the service proxy client for an OData service requiring sap-client parameter
--------------------------------------------------------------------------------
This is a sample when it is necessary to specify sap-client:
.. code-block:: python
import pyodata
import requests
SERVICE_URL = 'http://services.odata.org/V2/Northwind/Northwind.svc/'
session = requests.Session()
param = {'sap-client': '510'}
session.params = param
northwind = pyodata.Client(SERVICE_URL, session)
Get the service proxy client for an OData service requiring authentication
--------------------------------------------------------------------------
Let's assume you need to work with a service at
the URL *https://odata.example.com/Secret.svc* and User ID is 'MyUser' with
the password 'MyPassword'.
.. code-block:: python
import pyodata
import requests
SERVICE_URL = 'https://odata.example.com/Secret.svc'
session = requests.Session()
session.auth = ('MyUser', 'MyPassword')
theservice = pyodata.Client(SERVICE_URL, session)
Get the service proxy client for an OData service requiring Certificate authentication
--------------------------------------------------------------------------------------
Let's assume your service requires certificate based authentication and you are
able to export the certificate into the file *mycert.p12*. You need to split
the certificate into public key, private key and certificate authority key.
The following steps has been verified on Fedora Linux and Mac OS.
.. code-block:: bash
openssl pkcs12 -in mycert.p12 -out ca.pem -cacerts -nokeys
openssl pkcs12 -in mycert.p12 -out client.pem -clcerts -nokeys
openssl pkcs12 -in mycert.p12 -out key.pem -nocerts
openssl rsa -in key.pem -out key_decrypt.pem
You can verify your steps by curl:
.. code-block:: bash
curl --key key_decrypt.pem --cacert ca.pem --cert client.pem -k 'SERVICE_URL/$metadata'
Python client initialization:
.. code-block:: python
import pyodata
import requests
SERVICE_URL = 'https://odata.example.com/Secret.svc'
session = requests.Session()
session.verify = 'ca.pem'
session.cert = ('client.pem', 'key_decrypt.pem')
client = pyodata.Client(SERVICE_URL, session)
For more information on client side SSL cerificationcas, please refer to this [gist](https://gist.github.com/mtigas/952344).
Get the service with local metadata
-----------------------------------
It may happen that you will have metadata document for your service downloaded
in a local file and you will want to initialize the service proxy from this
file. In such a case you can provide content of the file as the named argument
`metadata`. Please, make sure you provide `bytes` and not `str` (required by
the xml parser lxml).
.. code-block:: python
import pyodata
import requests
SERVICE_URL = 'http://services.odata.org/V2/Northwind/Northwind.svc/'
with open('/the/file/path.xml', 'rb') as mtd_file:
local_metadata = mtd_file.read()
northwind = pyodata.Client(SERVICE_URL, requests.Session(), metadata=local_metadata)
Dealing with errors during parsing metadata
-------------------------------------------
In the case where you need to consume a service which has not fully valid metadata document and is not under your control, you can configure the metadata parser to try to recover from detected problems.
Parser recovery measures include actions such as using a stub entity type if the parser cannot find a referenced entity type. The stub entity type allows the parser to continue processing the given metadata but causes fatal errors when accessed from the client.
Class config provides easy to use wrapper for all parser configuration. These are:
- XML namespaces
- Parser policies (how parser act in case of invalid XML tag). We now support three types of policies:
- Policy fatal - the policy raises exception and terminates the parser
- Policy warning - the policy reports the detected problem, executes a fallback code and then continues normally
- Policy ignore - the policy executes a fallback code without reporting the problem and then continues normally
Parser policies can be specified individually for each XML tag (See enum ParserError for more details). If no policy is specified for the tag, the default policy is used.
For parser to use your custom configuration, it needs to be passed as an argument to the client.
.. code-block:: python
import pyodata
from pyodata.v2.model import PolicyFatal, PolicyWarning, PolicyIgnore, ParserError, Config
import requests
SERVICE_URL = 'http://services.odata.org/V2/Northwind/Northwind.svc/'
namespaces = {
'edmx': 'customEdmxUrl.com',
'edm': 'customEdmUrl.com'
}
custom_config = Config(
xml_namespaces=namespaces,
default_error_policy=PolicyFatal(),
custom_error_policies={
ParserError.ANNOTATION: PolicyWarning(),
ParserError.ASSOCIATION: PolicyIgnore()
})
northwind = pyodata.Client(SERVICE_URL, requests.Session(), config=custom_config)
Additionally, Schema class has Boolean atribute 'is_valid' that returns if the parser encountered errors. It's value does not depends on used Parser policy.
.. code-block:: python
northwind.schema.is_valid
Prevent substitution by default values
--------------------------------------
Per default, missing properties get filled in by type specific default values. While convenient, this throws away
the knowledge of whether a value was missing in the first place.
To prevent this, the class config mentioned in the section above takes an additional parameter, `retain_null`.
.. code-block:: python
import pyodata
import requests
SERVICE_URL = 'http://services.odata.org/V2/Northwind/Northwind.svc/'
northwind = pyodata.Client(SERVICE_URL, requests.Session(), config=pyodata.v2.model.Config(retain_null=True))
unknown_shipped_date = northwind.entity_sets.Orders_Qries.get_entity(OrderID=11058,
CompanyName='Blauer See Delikatessen').execute()
print(
f'Shipped date: {"unknown" if unknown_shipped_date.ShippedDate is None else unknown_shipped_date.ShippedDate}')
Changing `retain_null` to `False` will print `Shipped date: 1753-01-01 00:00:00+00:00`.
Set custom namespaces (Deprecated - use config instead)
-------------------------------------------------------
Let's assume you need to work with a service which uses namespaces not directly supported by this library e. g. ones
hosted on private urls such as *customEdmxUrl.com* and *customEdmUrl.com*:
.. code-block:: python
import pyodata
import requests
SERVICE_URL = 'http://services.odata.org/V2/Northwind/Northwind.svc/'
namespaces = {
'edmx': 'customEdmxUrl.com'
'edm': 'customEdmUrl.com'
}
northwind = pyodata.Client(SERVICE_URL, requests.Session(), namespaces=namespaces)
|