entryscape 1.3.0__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.
Files changed (82) hide show
  1. entryscape/__init__.py +219 -0
  2. entryscape/api/__init__.py +26 -0
  3. entryscape/api/app_token_api.py +835 -0
  4. entryscape/api/auth_api.py +965 -0
  5. entryscape/api/catalog_api.py +4473 -0
  6. entryscape/api/contact_api.py +3135 -0
  7. entryscape/api/dataservice_api.py +3135 -0
  8. entryscape/api/dataset_api.py +3804 -0
  9. entryscape/api/distribution_api.py +3135 -0
  10. entryscape/api/document_api.py +2740 -0
  11. entryscape/api/idea_api.py +2740 -0
  12. entryscape/api/job_api.py +360 -0
  13. entryscape/api/model_api.py +2446 -0
  14. entryscape/api/model_class_api.py +2611 -0
  15. entryscape/api/model_diagram_api.py +2611 -0
  16. entryscape/api/model_field_api.py +2611 -0
  17. entryscape/api/model_form_api.py +2611 -0
  18. entryscape/api/model_namespace_api.py +2611 -0
  19. entryscape/api/model_property_api.py +2611 -0
  20. entryscape/api/organization_api.py +3135 -0
  21. entryscape/api/search_api.py +1513 -0
  22. entryscape/api/showcase_api.py +2740 -0
  23. entryscape/api/suggestion_api.py +2740 -0
  24. entryscape/api/terminology_api.py +1726 -0
  25. entryscape/api/upload_api.py +814 -0
  26. entryscape/api_client.py +763 -0
  27. entryscape/api_response.py +20 -0
  28. entryscape/configuration.py +673 -0
  29. entryscape/exceptions.py +222 -0
  30. entryscape/models/__init__.py +71 -0
  31. entryscape/models/app_token.py +156 -0
  32. entryscape/models/app_token_create_request.py +98 -0
  33. entryscape/models/app_token_create_response.py +169 -0
  34. entryscape/models/app_token_status.py +35 -0
  35. entryscape/models/app_token_verify_request.py +109 -0
  36. entryscape/models/entity_list_item.py +175 -0
  37. entryscape/models/entity_list_response.py +139 -0
  38. entryscape/models/entity_reference.py +189 -0
  39. entryscape/models/entity_type.py +57 -0
  40. entryscape/models/error.py +135 -0
  41. entryscape/models/facet_field.py +115 -0
  42. entryscape/models/facet_field_values_inner.py +100 -0
  43. entryscape/models/facet_list.py +113 -0
  44. entryscape/models/facet_list_available_inner.py +107 -0
  45. entryscape/models/job_response.py +112 -0
  46. entryscape/models/job_status.py +189 -0
  47. entryscape/models/job_status_progress.py +100 -0
  48. entryscape/models/job_status_value.py +37 -0
  49. entryscape/models/job_type.py +37 -0
  50. entryscape/models/list_catalogs_entry_type_parameter.py +37 -0
  51. entryscape/models/list_catalogs_graph_type_parameter.py +44 -0
  52. entryscape/models/list_catalogs_resource_type_parameter.py +37 -0
  53. entryscape/models/login_request.py +108 -0
  54. entryscape/models/login_response.py +115 -0
  55. entryscape/models/metadata_format.py +38 -0
  56. entryscape/models/metadata_request.py +99 -0
  57. entryscape/models/parent_reference.py +130 -0
  58. entryscape/models/search_request.py +158 -0
  59. entryscape/models/search_request_filters.py +144 -0
  60. entryscape/models/search_request_filters_graph_type.py +44 -0
  61. entryscape/models/search_request_filters_resource_type.py +37 -0
  62. entryscape/models/search_request_sort.py +38 -0
  63. entryscape/models/search_request_sort_order.py +35 -0
  64. entryscape/models/search_response.py +175 -0
  65. entryscape/models/search_result.py +175 -0
  66. entryscape/models/search_result_graph_type.py +44 -0
  67. entryscape/models/search_result_resource_type.py +37 -0
  68. entryscape/models/search_sort_order_parameter.py +35 -0
  69. entryscape/models/terminology_import_mode.py +35 -0
  70. entryscape/models/validation_profile.py +36 -0
  71. entryscape/models/validation_response.py +149 -0
  72. entryscape/models/validation_result.py +162 -0
  73. entryscape/models/validation_severity.py +36 -0
  74. entryscape/models/validation_summary.py +122 -0
  75. entryscape/models/whoami_response.py +104 -0
  76. entryscape/py.typed +0 -0
  77. entryscape/rest.py +210 -0
  78. entryscape-1.3.0.dist-info/METADATA +696 -0
  79. entryscape-1.3.0.dist-info/RECORD +82 -0
  80. entryscape-1.3.0.dist-info/WHEEL +5 -0
  81. entryscape-1.3.0.dist-info/licenses/LICENSE.txt +165 -0
  82. entryscape-1.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,673 @@
1
+ """
2
+ EntryScape API
3
+
4
+ This API provides structured access to EntryScape data following DCAT-AP standards. All entity endpoints follow a consistent pattern: - GET /{entity} - List all entities - POST /{entity} - Create a new entity (DCAT types only) - GET /{entity}/{context_id}/{entry_id} - Get entity reference info - DELETE /{entity}/{context_id}/{entry_id} - Delete an entity (DCAT types only) - GET /{entity}/{context_id}/{entry_id}/metadata - Get raw RDF metadata - PUT /{entity}/{context_id}/{entry_id}/metadata - Replace raw RDF metadata (JSON-LD, Turtle, RDF/XML, or N-Triples passthrough) ## Reading and consistency Reads of a single entity — `GET /{entity}/{context_id}/{entry_id}` and its `/metadata` — go straight to the store and always reflect the latest write. Lists and search do not. They are answered from a search index that is updated asynchronously, so for a short interval after a write: - an entry that was just created may be missing from the list it belongs to, while reading it by id already works; - an entry that was just deleted may still appear; - `results` counts what the index holds, not what the store holds. Write a client that tolerates this: after creating an entry, use the id the create returned rather than searching for what you just wrote. `results` is also an upper bound rather than an exact count when the caller cannot read every hit (see the field's own description). Separately, responses are cached for 15 minutes by default, which is a second and unrelated source of staleness. A write invalidates the cache entries it affects, but a read that lands in the indexing gap can cache a list that does not yet show it. ## Authentication Most endpoints require an authenticated session. Reading public data works without authentication, while creating, updating, and deleting entities requires a valid user session. 1. **Log in** with `POST /auth/login`, supplying an EntryStore username and password. The response returns an `auth_token`. 2. **Send the token** on subsequent requests via the `X-Auth-Token` header. This is the recommended method for SDK and programmatic access. Browser clients can instead rely on the `auth_token` cookie, which the server sets automatically on login (`SameSite=Lax; Secure`). 3. **Inspect the session** with `GET /auth/whoami`, which returns the current user or an anonymous/guest identity when no valid token is supplied. 4. **Log out** with `POST /auth/logout` to invalidate the token and clear the cookie. ### Writes and CSRF Send the token as the `X-Auth-Token` **header** and there is nothing else to do: creates, updates and deletes work as they are, which is how the SDKs and the MCP server are built. The `auth_token` cookie is different. A cookie is attached by the browser automatically, so a state-changing request authenticated by it alone is what cross-site request forgery abuses, and those requests additionally need a double-submit token: read the `XSRF-TOKEN` cookie the server sets on any response and send its value back as the `X-XSRF-TOKEN` header. Without it the request is rejected with `403` before it reaches the endpoint. A custom header cannot be forged this way — cross-origin JavaScript cannot set one without a CORS preflight, which this API grants only to configured origins — so the header is the recommended path for anything that is not a browser session. ### App Tokens An app token identifies an integration. Create one with `POST /app-token` (unauthenticated). Creation requires an email address: the token starts in the `pending` state and a 6-digit code is emailed to the owner. Confirm the code with `POST /app-token/verify` to activate the token and receive its value, which is shown only once — store it securely. `GET /app-token/{app_token_id}` then returns its details and quota usage, authenticated by the token itself via the `X-App-Token` header. **The `X-App-Token` header is accepted only by `GET /app-token/{app_token_id}`.** No other operation reads it, and the read quota is recorded but not yet enforced: reading public data needs no token of any kind, and sending an app token with a read neither grants nor meters anything. Metered public read access is the intent of the two-token model, not a description of this release — see the `security` declared on each operation for what it actually accepts. ## SDKs Generated clients for Python, TypeScript, JavaScript and C# wrap every operation below, and each operation on this page shows the SDK call beside the `curl` command. Two of them install from a public registry: ``` pip install entryscape # Python npm install @entryscape/api-client # TypeScript and JavaScript ``` `@entryscape/api-client` ships an ESM and a CommonJS build, so `import` and `require()` both work and TypeScript is not required to use it — the name describes what it is written in, not what you have to write. There is a separate generated JavaScript SDK, and it is deliberately **not** on npm; this package is the npm client for both languages. Every SDK, including the C# and JavaScript ones, is also published as a tarball. The downloads are linked in the sidebar, they are the route to take when a registry is unreachable or a build has to vendor its dependencies, and each archive carries the SDK's own README: - **Python** — `pip install entryscape`, or `python-sdk.tar.gz`. - **TypeScript and JavaScript** — `npm install @entryscape/api-client`, or `typescript-sdk.tar.gz`. - **JavaScript (the separate generated client)** — not published to a registry, by design; `javascript-sdk.tar.gz` only. - **C#** — not published to NuGet yet; `csharp-sdk.tar.gz` only. - **MCP server** — `npx -y @entryscape/mcp-server`, or `mcp-server.tar.gz`. ## MCP Server A Model Context Protocol (MCP) server is generated from this specification and published alongside the SDKs (`@entryscape/mcp-server`), exposing the API as tools for AI assistants and agents. Each operation becomes one tool named after its `operationId` — `listDatasets`, `createCatalog`, `search`, `getJobStatus`. The `auth` operations are the exception: the server reads its token once at startup, so a `login` tool could install nothing and a `logout` tool would only invalidate the token the server was started with. On connect the server also hands the assistant a short set of rules that hold across every tool: a token is bound to one EntryStore instance, the search index lags a write, uploads finish as jobs. ### Install The server is on npm, and MCP servers are normally launched straight from there, so nothing has to be installed first: ``` npx -y @entryscape/mcp-server ``` `npm install -g @entryscape/mcp-server` works too and puts an `entryscape-mcp` binary on `PATH`. It is also listed in the MCP Registry as `com.entryscape/mcp-server`, which is where clients and marketplaces look it up. Without npm, download `mcp-server.tar.gz` from the SDK downloads and unpack it. The package ships prebuilt; install its runtime dependency once: ``` mkdir entryscape-mcp && tar -xzf mcp-server.tar.gz -C entryscape-mcp cd entryscape-mcp && npm install --omit=dev ``` ### Configure The server is a stdio MCP server configured through environment variables, read once at startup: - `ENTRYSCAPE_API_URL` — base URL of the API server, path included. Required: there is no default, and the server refuses to start without it. - `ENTRYSCAPE_AUTH_TOKEN` — a session token from `POST /auth/login`, sent as the `X-Auth-Token` header. Optional: without it reads see public entries and every write is refused. A token is valid only for the EntryStore instance that issued it. - `ENTRYSCAPE_ENTRYSTORE_HOST` — the EntryStore instance the API server should use, for example `dev.entryscape.com/store/`. Unset means the API server's own default. - `ENTRYSCAPE_MCP_UPLOAD_ROOT` — the directory the upload tools may read files from. **Unset means uploads are disabled**: `addFileToDistribution`, `replaceFileInDistribution` and `importTerminology` refuse and say so. - `ENTRYSCAPE_MCP_TAGS` — comma-separated tags to serve tools for, for example `catalog,dataset,distribution,search`. Unset serves every tool. The package's `README.md` lists the remaining variables (timeouts and size caps). Register the server with any MCP-capable client by adding it to the client's `mcpServers` configuration: ```json { \"mcpServers\": { \"entryscape\": { \"command\": \"npx\", \"args\": [\"-y\", \"@entryscape/mcp-server\"], \"env\": { \"ENTRYSCAPE_API_URL\": \"https://<your EntryScape API host>\", \"ENTRYSCAPE_AUTH_TOKEN\": \"your-session-token\" } } } } ``` From an unpacked download instead, the command is `\"command\": \"node\", \"args\": [\"/path/to/entryscape-mcp/dist/bin/entryscape-mcp.js\"]`. ### Claude Code The unpacked package is also a Claude Code plugin, so one install gives the server and the skills below together. Claude Code installs plugins from marketplaces, and the package carries a one-entry marketplace pointing at itself: ``` claude plugin marketplace add /path/to/entryscape-mcp claude plugin install entryscape@entryscape --scope user ``` The plugin passes `ENTRYSCAPE_API_URL`, `ENTRYSCAPE_AUTH_TOKEN`, `ENTRYSCAPE_ENTRYSTORE_HOST`, `ENTRYSCAPE_MCP_TAGS` and `ENTRYSCAPE_MCP_UPLOAD_ROOT` through from the shell Claude Code was started in, so set them there. To try the plugin for one session without installing it, start Claude Code with `--plugin-dir /path/to/entryscape-mcp`. ### Skills `skills/` in the package holds procedures that span several tools, in the Agent Skills format (a `SKILL.md` with `name` and `description`): which tools to call in which order, what to check between calls, and where to stop. Claude Code loads them from the plugin; other clients that read the format can be pointed at the directory. The `find` skill covers discovery, facets and selective metadata reads; `publish-dataset` takes a dataset from catalog to validated distribution with its file; `edit-metadata` changes an entry that already exists without dropping the rest of its graph, models included; `migrate-from-taskrunner` moves an integration off the deprecated Taskrunner API. Every tool a skill names is checked against this specification when the package is built. ## Deprecation Policy When endpoints or features are deprecated: 1. The operation is marked with `deprecated: true` in this spec 2. The operation description documents the replacement endpoint and sunset date 3. Deprecated endpoints remain functional for at least 6 months after announcement 4. The server returns a `Sunset` header with the planned removal date 5. After the sunset date, the endpoint may be removed in a future release
5
+
6
+ The version of the OpenAPI document: 1.3.0
7
+ Contact: contact@entryscape.com
8
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
9
+
10
+ Do not edit the class manually.
11
+ """ # noqa: E501
12
+
13
+ import aiohttp_retry
14
+ import base64
15
+ import copy
16
+ import http.client as httplib
17
+ import logging
18
+ from logging import FileHandler
19
+ import sys
20
+ from typing import Any, ClassVar, Dict, List, Literal, Optional, TypedDict, Union
21
+ from typing_extensions import NotRequired, Self
22
+
23
+ JSON_SCHEMA_VALIDATION_KEYWORDS = {
24
+ "multipleOf",
25
+ "maximum",
26
+ "exclusiveMaximum",
27
+ "minimum",
28
+ "exclusiveMinimum",
29
+ "maxLength",
30
+ "minLength",
31
+ "pattern",
32
+ "maxItems",
33
+ "minItems",
34
+ }
35
+
36
+ ServerVariablesT = Dict[str, str]
37
+
38
+ GenericAuthSetting = TypedDict(
39
+ "GenericAuthSetting",
40
+ {
41
+ "type": str,
42
+ "in": str,
43
+ "key": str,
44
+ "value": str,
45
+ },
46
+ )
47
+
48
+
49
+ OAuth2AuthSetting = TypedDict(
50
+ "OAuth2AuthSetting",
51
+ {
52
+ "type": Literal["oauth2"],
53
+ "in": Literal["header"],
54
+ "key": Literal["Authorization"],
55
+ "value": str,
56
+ },
57
+ )
58
+
59
+
60
+ APIKeyAuthSetting = TypedDict(
61
+ "APIKeyAuthSetting",
62
+ {
63
+ "type": Literal["api_key"],
64
+ "in": str,
65
+ "key": str,
66
+ "value": Optional[str],
67
+ },
68
+ )
69
+
70
+
71
+ BasicAuthSetting = TypedDict(
72
+ "BasicAuthSetting",
73
+ {
74
+ "type": Literal["basic"],
75
+ "in": Literal["header"],
76
+ "key": Literal["Authorization"],
77
+ "value": Optional[str],
78
+ },
79
+ )
80
+
81
+
82
+ BearerFormatAuthSetting = TypedDict(
83
+ "BearerFormatAuthSetting",
84
+ {
85
+ "type": Literal["bearer"],
86
+ "in": Literal["header"],
87
+ "format": Literal["JWT"],
88
+ "key": Literal["Authorization"],
89
+ "value": str,
90
+ },
91
+ )
92
+
93
+
94
+ BearerAuthSetting = TypedDict(
95
+ "BearerAuthSetting",
96
+ {
97
+ "type": Literal["bearer"],
98
+ "in": Literal["header"],
99
+ "key": Literal["Authorization"],
100
+ "value": str,
101
+ },
102
+ )
103
+
104
+
105
+ HTTPSignatureAuthSetting = TypedDict(
106
+ "HTTPSignatureAuthSetting",
107
+ {
108
+ "type": Literal["http-signature"],
109
+ "in": Literal["header"],
110
+ "key": Literal["Authorization"],
111
+ "value": None,
112
+ },
113
+ )
114
+
115
+
116
+ AuthSettings = TypedDict(
117
+ "AuthSettings",
118
+ {
119
+ "auth_token": APIKeyAuthSetting,
120
+ "auth_header": APIKeyAuthSetting,
121
+ "app_token_header": APIKeyAuthSetting,
122
+ },
123
+ total=False,
124
+ )
125
+
126
+
127
+ class HostSettingVariable(TypedDict):
128
+ description: str
129
+ default_value: str
130
+ enum_values: List[str]
131
+
132
+
133
+ class HostSetting(TypedDict):
134
+ url: str
135
+ description: str
136
+ variables: NotRequired[Dict[str, HostSettingVariable]]
137
+
138
+
139
+ class Configuration:
140
+ """This class contains various settings of the API client.
141
+
142
+ :param host: Base url.
143
+ :param ignore_operation_servers
144
+ Boolean to ignore operation servers for the API client.
145
+ Config will use `host` as the base url regardless of the operation servers.
146
+ :param api_key: Dict to store API key(s).
147
+ Each entry in the dict specifies an API key.
148
+ The dict key is the name of the security scheme in the OAS specification.
149
+ The dict value is the API key secret.
150
+ :param api_key_prefix: Dict to store API prefix (e.g. Bearer).
151
+ The dict key is the name of the security scheme in the OAS specification.
152
+ The dict value is an API key prefix when generating the auth data.
153
+ :param username: Username for HTTP basic authentication.
154
+ :param password: Password for HTTP basic authentication.
155
+ :param access_token: Access token.
156
+ :param server_index: Index to servers configuration.
157
+ :param server_variables: Mapping with string values to replace variables in
158
+ templated server configuration. The validation of enums is performed for
159
+ variables with defined enum values before.
160
+ :param server_operation_index: Mapping from operation ID to an index to server
161
+ configuration.
162
+ :param server_operation_variables: Mapping from operation ID to a mapping with
163
+ string values to replace variables in templated server configuration.
164
+ The validation of enums is performed for variables with defined enum
165
+ values before.
166
+ :param verify_ssl: bool - Set this to false to skip verifying SSL certificate
167
+ when calling API from https server.
168
+ :param ssl_ca_cert: str - the path to a file of concatenated CA certificates
169
+ in PEM format.
170
+ :param retries: int | aiohttp_retry.RetryOptionsBase - Retry configuration.
171
+ :param ca_cert_data: verify the peer using concatenated CA certificate data
172
+ in PEM (str) or DER (bytes) format.
173
+ :param cert_file: the path to a client certificate file, for mTLS.
174
+ :param key_file: the path to a client key file, for mTLS.
175
+ :param assert_hostname: Set this to True/False to enable/disable SSL hostname verification.
176
+ :param tls_server_name: SSL/TLS Server Name Indication (SNI). Set this to the SNI value expected by the server.
177
+ :param connection_pool_maxsize: Connection pool max size. None in the constructor is coerced to 100 for async and cpu_count * 5 for sync.
178
+ :param proxy: Proxy URL.
179
+ :param proxy_headers: Proxy headers.
180
+ :param safe_chars_for_path_param: Safe characters for path parameter encoding.
181
+ :param client_side_validation: Enable client-side validation. Default True.
182
+ :param socket_options: Options to pass down to the underlying urllib3 socket.
183
+ :param datetime_format: Datetime format string for serialization.
184
+ :param date_format: Date format string for serialization.
185
+
186
+ :Example:
187
+
188
+ API Key Authentication Example.
189
+ Given the following security scheme in the OpenAPI specification:
190
+ components:
191
+ securitySchemes:
192
+ cookieAuth: # name for the security scheme
193
+ type: apiKey
194
+ in: cookie
195
+ name: JSESSIONID # cookie name
196
+
197
+ You can programmatically set the cookie:
198
+
199
+ conf = entryscape.Configuration(
200
+ api_key={'cookieAuth': 'abc123'}
201
+ api_key_prefix={'cookieAuth': 'JSESSIONID'}
202
+ )
203
+
204
+ The following cookie will be added to the HTTP request:
205
+ Cookie: JSESSIONID abc123
206
+ """
207
+
208
+ _default: ClassVar[Optional[Self]] = None
209
+
210
+ def __init__(
211
+ self,
212
+ host: Optional[str] = None,
213
+ api_key: Optional[Dict[str, str]] = None,
214
+ api_key_prefix: Optional[Dict[str, str]] = None,
215
+ username: Optional[str] = None,
216
+ password: Optional[str] = None,
217
+ access_token: Optional[str] = None,
218
+ server_index: Optional[int] = None,
219
+ server_variables: Optional[ServerVariablesT] = None,
220
+ server_operation_index: Optional[Dict[int, int]] = None,
221
+ server_operation_variables: Optional[Dict[int, ServerVariablesT]] = None,
222
+ ignore_operation_servers: bool = False,
223
+ ssl_ca_cert: Optional[str] = None,
224
+ retries: Optional[Union[int, aiohttp_retry.RetryOptionsBase]] = None,
225
+ ca_cert_data: Optional[Union[str, bytes]] = None,
226
+ cert_file: Optional[str] = None,
227
+ key_file: Optional[str] = None,
228
+ verify_ssl: bool = True,
229
+ assert_hostname: Optional[bool] = None,
230
+ tls_server_name: Optional[str] = None,
231
+ connection_pool_maxsize: Optional[int] = None,
232
+ proxy: Optional[str] = None,
233
+ proxy_headers: Optional[Any] = None,
234
+ safe_chars_for_path_param: str = "",
235
+ client_side_validation: bool = True,
236
+ socket_options: Optional[Any] = None,
237
+ datetime_format: str = "%Y-%m-%dT%H:%M:%S.%f%z",
238
+ date_format: str = "%Y-%m-%d",
239
+ *,
240
+ debug: Optional[bool] = None,
241
+ ) -> None:
242
+ """Constructor"""
243
+ self._base_path = "https://api.entryscape.com" if host is None else host
244
+ """Default Base url
245
+ """
246
+ self.server_index = 0 if server_index is None and host is None else server_index
247
+ self.server_operation_index = server_operation_index or {}
248
+ """Default server index
249
+ """
250
+ self.server_variables = server_variables or {}
251
+ self.server_operation_variables = server_operation_variables or {}
252
+ """Default server variables
253
+ """
254
+ self.ignore_operation_servers = ignore_operation_servers
255
+ """Ignore operation servers
256
+ """
257
+ self.temp_folder_path = None
258
+ """Temp file folder for downloading files
259
+ """
260
+ # Authentication Settings
261
+ self.api_key = {}
262
+ if api_key:
263
+ self.api_key = api_key
264
+ """dict to store API key(s)
265
+ """
266
+ self.api_key_prefix = {}
267
+ if api_key_prefix:
268
+ self.api_key_prefix = api_key_prefix
269
+ """dict to store API prefix (e.g. Bearer)
270
+ """
271
+ self.refresh_api_key_hook = None
272
+ """function hook to refresh API key if expired
273
+ """
274
+ self.username = username
275
+ """Username for HTTP basic authentication
276
+ """
277
+ self.password = password
278
+ """Password for HTTP basic authentication
279
+ """
280
+ self.access_token = access_token
281
+ """Access token
282
+ """
283
+ self.logger = {}
284
+ """Logging Settings
285
+ """
286
+ self.logger["package_logger"] = logging.getLogger("entryscape")
287
+ self.logger_format = "%(asctime)s %(levelname)s %(message)s"
288
+ """Log format
289
+ """
290
+ self.logger_stream_handler = None
291
+ """Log stream handler
292
+ """
293
+ self.logger_file_handler: Optional[FileHandler] = None
294
+ """Log file handler
295
+ """
296
+ self.logger_file = None
297
+ """Debug file location
298
+ """
299
+ if debug is not None:
300
+ self.debug = debug
301
+ else:
302
+ self.__debug = False
303
+ """Debug switch
304
+ """
305
+
306
+ self.verify_ssl = verify_ssl
307
+ """SSL/TLS verification
308
+ Set this to false to skip verifying SSL certificate when calling API
309
+ from https server.
310
+ """
311
+ self.ssl_ca_cert = ssl_ca_cert
312
+ """Set this to customize the certificate file to verify the peer.
313
+ """
314
+ self.ca_cert_data = ca_cert_data
315
+ """Set this to verify the peer using PEM (str) or DER (bytes)
316
+ certificate data.
317
+ """
318
+ self.cert_file = cert_file
319
+ """client certificate file
320
+ """
321
+ self.key_file = key_file
322
+ """client key file
323
+ """
324
+ self.assert_hostname = assert_hostname
325
+ """Set this to True/False to enable/disable SSL hostname verification.
326
+ """
327
+ self.tls_server_name = tls_server_name
328
+ """SSL/TLS Server Name Indication (SNI)
329
+ Set this to the SNI value expected by the server.
330
+ """
331
+
332
+ self.connection_pool_maxsize = (
333
+ connection_pool_maxsize if connection_pool_maxsize is not None else 100
334
+ )
335
+ """This value is passed to the aiohttp to limit simultaneous connections.
336
+ None in the constructor is coerced to default 100.
337
+ """
338
+
339
+ self.proxy = proxy
340
+ """Proxy URL
341
+ """
342
+ self.proxy_headers = proxy_headers
343
+ """Proxy headers
344
+ """
345
+ self.safe_chars_for_path_param = safe_chars_for_path_param
346
+ """Safe chars for path_param
347
+ """
348
+ self.retries = retries
349
+ """Retry configuration
350
+ """
351
+ # Enable client side validation
352
+ self.client_side_validation = client_side_validation
353
+
354
+ self.socket_options = socket_options
355
+ """Options to pass down to the underlying urllib3 socket
356
+ """
357
+
358
+ self.datetime_format = datetime_format
359
+ """datetime format
360
+ """
361
+
362
+ self.date_format = date_format
363
+ """date format
364
+ """
365
+
366
+ def __deepcopy__(self, memo: Dict[int, Any]) -> Self:
367
+ cls = self.__class__
368
+ result = cls.__new__(cls)
369
+ memo[id(self)] = result
370
+ for k, v in self.__dict__.items():
371
+ if k not in ("logger", "logger_file_handler"):
372
+ setattr(result, k, copy.deepcopy(v, memo))
373
+ # shallow copy of loggers
374
+ result.logger = copy.copy(self.logger)
375
+ # use setters to configure loggers
376
+ result.logger_file = self.logger_file
377
+ result.debug = self.debug
378
+ return result
379
+
380
+ def __setattr__(self, name: str, value: Any) -> None:
381
+ object.__setattr__(self, name, value)
382
+
383
+ @classmethod
384
+ def set_default(cls, default: Optional[Self]) -> None:
385
+ """Set default instance of configuration.
386
+
387
+ It stores default configuration, which can be
388
+ returned by get_default_copy method.
389
+
390
+ :param default: object of Configuration
391
+ """
392
+ cls._default = default
393
+
394
+ @classmethod
395
+ def get_default_copy(cls) -> Self:
396
+ """Deprecated. Please use `get_default` instead.
397
+
398
+ Deprecated. Please use `get_default` instead.
399
+
400
+ :return: The configuration object.
401
+ """
402
+ return cls.get_default()
403
+
404
+ @classmethod
405
+ def get_default(cls) -> Self:
406
+ """Return the default configuration.
407
+
408
+ This method returns newly created, based on default constructor,
409
+ object of Configuration class or returns a copy of default
410
+ configuration.
411
+
412
+ :return: The configuration object.
413
+ """
414
+ if cls._default is None:
415
+ cls._default = cls()
416
+ return cls._default
417
+
418
+ @property
419
+ def logger_file(self) -> Optional[str]:
420
+ """The logger file.
421
+
422
+ If the logger_file is None, then add stream handler and remove file
423
+ handler. Otherwise, add file handler and remove stream handler.
424
+
425
+ :param value: The logger_file path.
426
+ :type: str
427
+ """
428
+ return self.__logger_file
429
+
430
+ @logger_file.setter
431
+ def logger_file(self, value: Optional[str]) -> None:
432
+ """The logger file.
433
+
434
+ If the logger_file is None, then add stream handler and remove file
435
+ handler. Otherwise, add file handler and remove stream handler.
436
+
437
+ :param value: The logger_file path.
438
+ :type: str
439
+ """
440
+ self.__logger_file = value
441
+ if self.__logger_file:
442
+ # If set logging file,
443
+ # then add file handler and remove stream handler.
444
+ self.logger_file_handler = logging.FileHandler(self.__logger_file)
445
+ self.logger_file_handler.setFormatter(self.logger_formatter)
446
+ for _, logger in self.logger.items():
447
+ logger.addHandler(self.logger_file_handler)
448
+
449
+ @property
450
+ def debug(self) -> bool:
451
+ """Debug status
452
+
453
+ :param value: The debug status, True or False.
454
+ :type: bool
455
+ """
456
+ return self.__debug
457
+
458
+ @debug.setter
459
+ def debug(self, value: bool) -> None:
460
+ """Debug status
461
+
462
+ :param value: The debug status, True or False.
463
+ :type: bool
464
+ """
465
+ self.__debug = value
466
+ if self.__debug:
467
+ # if debug status is True, turn on debug logging
468
+ for _, logger in self.logger.items():
469
+ logger.setLevel(logging.DEBUG)
470
+ # turn on httplib debug
471
+ httplib.HTTPConnection.debuglevel = 1
472
+ else:
473
+ # if debug status is False, turn off debug logging,
474
+ # setting log level to default `logging.WARNING`
475
+ for _, logger in self.logger.items():
476
+ logger.setLevel(logging.WARNING)
477
+ # turn off httplib debug
478
+ httplib.HTTPConnection.debuglevel = 0
479
+
480
+ @property
481
+ def logger_format(self) -> str:
482
+ """The logger format.
483
+
484
+ The logger_formatter will be updated when sets logger_format.
485
+
486
+ :param value: The format string.
487
+ :type: str
488
+ """
489
+ return self.__logger_format
490
+
491
+ @logger_format.setter
492
+ def logger_format(self, value: str) -> None:
493
+ """The logger format.
494
+
495
+ The logger_formatter will be updated when sets logger_format.
496
+
497
+ :param value: The format string.
498
+ :type: str
499
+ """
500
+ self.__logger_format = value
501
+ self.logger_formatter = logging.Formatter(self.__logger_format)
502
+
503
+ def get_api_key_with_prefix(
504
+ self, identifier: str, alias: Optional[str] = None
505
+ ) -> Optional[str]:
506
+ """Gets API key (with prefix if set).
507
+
508
+ :param identifier: The identifier of apiKey.
509
+ :param alias: The alternative identifier of apiKey.
510
+ :return: The token for api key authentication.
511
+ """
512
+ if self.refresh_api_key_hook is not None:
513
+ self.refresh_api_key_hook(self)
514
+ key = self.api_key.get(
515
+ identifier, self.api_key.get(alias) if alias is not None else None
516
+ )
517
+ if key:
518
+ prefix = self.api_key_prefix.get(identifier)
519
+ if prefix:
520
+ return "%s %s" % (prefix, key)
521
+ else:
522
+ return key
523
+
524
+ return None
525
+
526
+ def get_basic_auth_token(self) -> Optional[str]:
527
+ """Gets HTTP basic authentication header (string).
528
+
529
+ :return: The token for basic HTTP authentication.
530
+ """
531
+ username = ""
532
+ if self.username is not None:
533
+ username = self.username
534
+ password = ""
535
+ if self.password is not None:
536
+ password = self.password
537
+
538
+ return "Basic " + base64.b64encode(
539
+ (username + ":" + password).encode("utf-8")
540
+ ).decode("utf-8")
541
+
542
+ def auth_settings(self) -> AuthSettings:
543
+ """Gets Auth Settings dict for api client.
544
+
545
+ :return: The Auth Settings information dict.
546
+ """
547
+ auth: AuthSettings = {}
548
+ if "auth_token" in self.api_key:
549
+ auth["auth_token"] = {
550
+ "type": "api_key",
551
+ "in": "cookie",
552
+ "key": "auth_token",
553
+ "value": self.get_api_key_with_prefix(
554
+ "auth_token",
555
+ ),
556
+ }
557
+ if "auth_header" in self.api_key:
558
+ auth["auth_header"] = {
559
+ "type": "api_key",
560
+ "in": "header",
561
+ "key": "X-Auth-Token",
562
+ "value": self.get_api_key_with_prefix(
563
+ "auth_header",
564
+ ),
565
+ }
566
+ if "app_token_header" in self.api_key:
567
+ auth["app_token_header"] = {
568
+ "type": "api_key",
569
+ "in": "header",
570
+ "key": "X-App-Token",
571
+ "value": self.get_api_key_with_prefix(
572
+ "app_token_header",
573
+ ),
574
+ }
575
+ return auth
576
+
577
+ def to_debug_report(self) -> str:
578
+ """Gets the essential information for debugging.
579
+
580
+ :return: The report for debugging.
581
+ """
582
+ return (
583
+ "Python SDK Debug Report:\n"
584
+ "OS: {env}\n"
585
+ "Python Version: {pyversion}\n"
586
+ "Version of the API: 1.3.0\n"
587
+ "SDK Package Version: 1.3.0".format(env=sys.platform, pyversion=sys.version)
588
+ )
589
+
590
+ def get_host_settings(self) -> List[HostSetting]:
591
+ """Gets an array of host settings
592
+
593
+ :return: An array of host settings
594
+ """
595
+ return [
596
+ {
597
+ "url": "https://api.entryscape.com",
598
+ "description": "Production server",
599
+ },
600
+ {
601
+ "url": "https://test.api.entryscape.com",
602
+ "description": "Test server, tracks the latest green master build",
603
+ },
604
+ {
605
+ "url": "http://localhost:8080",
606
+ "description": "Local development server",
607
+ },
608
+ {
609
+ "url": "http://metasolutions.local:8080",
610
+ "description": "Internal network server",
611
+ },
612
+ ]
613
+
614
+ def get_host_from_settings(
615
+ self,
616
+ index: Optional[int],
617
+ variables: Optional[ServerVariablesT] = None,
618
+ servers: Optional[List[HostSetting]] = None,
619
+ ) -> str:
620
+ """Gets host URL based on the index and variables
621
+ :param index: array index of the host settings
622
+ :param variables: hash of variable and the corresponding value
623
+ :param servers: an array of host settings or None
624
+ :return: URL based on host settings
625
+ """
626
+ if index is None:
627
+ return self._base_path
628
+
629
+ variables = {} if variables is None else variables
630
+ servers = self.get_host_settings() if servers is None else servers
631
+
632
+ try:
633
+ server = servers[index]
634
+ except IndexError:
635
+ raise ValueError(
636
+ "Invalid index {0} when selecting the host settings. "
637
+ "Must be less than {1}".format(index, len(servers))
638
+ )
639
+
640
+ url = server["url"]
641
+
642
+ # go through variables and replace placeholders
643
+ for variable_name, variable in server.get("variables", {}).items():
644
+ used_value = variables.get(variable_name, variable["default_value"])
645
+
646
+ if (
647
+ "enum_values" in variable
648
+ and variable["enum_values"]
649
+ and used_value not in variable["enum_values"]
650
+ ):
651
+ raise ValueError(
652
+ "The variable `{0}` in the host URL has invalid value "
653
+ "{1}. Must be {2}.".format(
654
+ variable_name, variables[variable_name], variable["enum_values"]
655
+ )
656
+ )
657
+
658
+ url = url.replace("{" + variable_name + "}", used_value)
659
+
660
+ return url
661
+
662
+ @property
663
+ def host(self) -> str:
664
+ """Return generated host."""
665
+ return self.get_host_from_settings(
666
+ self.server_index, variables=self.server_variables
667
+ )
668
+
669
+ @host.setter
670
+ def host(self, value: str) -> None:
671
+ """Fix base path."""
672
+ self._base_path = value
673
+ self.server_index = None