@kattebak/openapi-generator-ts 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +189 -0
- package/dist/cli/commands/generate.d.ts +23 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +116 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/list.d.ts +2 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +26 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +6 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +80 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/convert-template.d.ts +31 -0
- package/dist/cli/convert-template.d.ts.map +1 -0
- package/dist/cli/convert-template.js +204 -0
- package/dist/cli/convert-template.js.map +1 -0
- package/dist/cli/convert-template.test.d.ts +2 -0
- package/dist/cli/convert-template.test.d.ts.map +1 -0
- package/dist/cli/convert-template.test.js +74 -0
- package/dist/cli/convert-template.test.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +246 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/config.d.ts +252 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +31 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/generator.d.ts +99 -0
- package/dist/core/generator.d.ts.map +1 -0
- package/dist/core/generator.js +492 -0
- package/dist/core/generator.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/types.d.ts +47 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +5 -0
- package/dist/core/types.js.map +1 -0
- package/dist/generators/generators.test.d.ts +6 -0
- package/dist/generators/generators.test.d.ts.map +1 -0
- package/dist/generators/generators.test.js +247 -0
- package/dist/generators/generators.test.js.map +1 -0
- package/dist/generators/go.d.ts +10 -0
- package/dist/generators/go.d.ts.map +1 -0
- package/dist/generators/go.js +327 -0
- package/dist/generators/go.js.map +1 -0
- package/dist/generators/index.d.ts +29 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +58 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/php.d.ts +14 -0
- package/dist/generators/php.d.ts.map +1 -0
- package/dist/generators/php.js +241 -0
- package/dist/generators/php.js.map +1 -0
- package/dist/generators/python.d.ts +10 -0
- package/dist/generators/python.d.ts.map +1 -0
- package/dist/generators/python.js +273 -0
- package/dist/generators/python.js.map +1 -0
- package/dist/generators/typescript-fetch.d.ts +14 -0
- package/dist/generators/typescript-fetch.d.ts.map +1 -0
- package/dist/generators/typescript-fetch.js +217 -0
- package/dist/generators/typescript-fetch.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/models/codegen-model.d.ts +123 -0
- package/dist/models/codegen-model.d.ts.map +1 -0
- package/dist/models/codegen-model.js +64 -0
- package/dist/models/codegen-model.js.map +1 -0
- package/dist/models/codegen-model.test.d.ts +6 -0
- package/dist/models/codegen-model.test.d.ts.map +1 -0
- package/dist/models/codegen-model.test.js +143 -0
- package/dist/models/codegen-model.test.js.map +1 -0
- package/dist/models/codegen-operation.d.ts +95 -0
- package/dist/models/codegen-operation.d.ts.map +1 -0
- package/dist/models/codegen-operation.js +54 -0
- package/dist/models/codegen-operation.js.map +1 -0
- package/dist/models/codegen-parameter.d.ts +86 -0
- package/dist/models/codegen-parameter.d.ts.map +1 -0
- package/dist/models/codegen-parameter.js +47 -0
- package/dist/models/codegen-parameter.js.map +1 -0
- package/dist/models/codegen-property.d.ts +95 -0
- package/dist/models/codegen-property.d.ts.map +1 -0
- package/dist/models/codegen-property.js +47 -0
- package/dist/models/codegen-property.js.map +1 -0
- package/dist/models/codegen-response.d.ts +64 -0
- package/dist/models/codegen-response.d.ts.map +1 -0
- package/dist/models/codegen-response.js +42 -0
- package/dist/models/codegen-response.js.map +1 -0
- package/dist/models/codegen-security.d.ts +34 -0
- package/dist/models/codegen-security.d.ts.map +1 -0
- package/dist/models/codegen-security.js +18 -0
- package/dist/models/codegen-security.js.map +1 -0
- package/dist/models/index.d.ts +8 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +7 -0
- package/dist/models/index.js.map +1 -0
- package/dist/parser/index.d.ts +4 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +4 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/openapi-parser.d.ts +71 -0
- package/dist/parser/openapi-parser.d.ts.map +1 -0
- package/dist/parser/openapi-parser.js +195 -0
- package/dist/parser/openapi-parser.js.map +1 -0
- package/dist/parser/openapi-parser.test.d.ts +6 -0
- package/dist/parser/openapi-parser.test.d.ts.map +1 -0
- package/dist/parser/openapi-parser.test.js +238 -0
- package/dist/parser/openapi-parser.test.js.map +1 -0
- package/dist/parser/operation-transformer.d.ts +95 -0
- package/dist/parser/operation-transformer.d.ts.map +1 -0
- package/dist/parser/operation-transformer.js +634 -0
- package/dist/parser/operation-transformer.js.map +1 -0
- package/dist/parser/schema-transformer.d.ts +134 -0
- package/dist/parser/schema-transformer.d.ts.map +1 -0
- package/dist/parser/schema-transformer.js +557 -0
- package/dist/parser/schema-transformer.js.map +1 -0
- package/dist/template/engine-adapter.d.ts +55 -0
- package/dist/template/engine-adapter.d.ts.map +1 -0
- package/dist/template/engine-adapter.js +178 -0
- package/dist/template/engine-adapter.js.map +1 -0
- package/dist/template/engine-adapter.test.d.ts +6 -0
- package/dist/template/engine-adapter.test.d.ts.map +1 -0
- package/dist/template/engine-adapter.test.js +186 -0
- package/dist/template/engine-adapter.test.js.map +1 -0
- package/dist/template/index.d.ts +5 -0
- package/dist/template/index.d.ts.map +1 -0
- package/dist/template/index.js +5 -0
- package/dist/template/index.js.map +1 -0
- package/dist/template/lambdas/indent-lambdas.d.ts +40 -0
- package/dist/template/lambdas/indent-lambdas.d.ts.map +1 -0
- package/dist/template/lambdas/indent-lambdas.js +98 -0
- package/dist/template/lambdas/indent-lambdas.js.map +1 -0
- package/dist/template/lambdas/index.d.ts +44 -0
- package/dist/template/lambdas/index.d.ts.map +1 -0
- package/dist/template/lambdas/index.js +79 -0
- package/dist/template/lambdas/index.js.map +1 -0
- package/dist/template/lambdas/string-lambdas.d.ts +78 -0
- package/dist/template/lambdas/string-lambdas.d.ts.map +1 -0
- package/dist/template/lambdas/string-lambdas.js +148 -0
- package/dist/template/lambdas/string-lambdas.js.map +1 -0
- package/dist/template/lambdas/string-lambdas.test.d.ts +6 -0
- package/dist/template/lambdas/string-lambdas.test.d.ts.map +1 -0
- package/dist/template/lambdas/string-lambdas.test.js +158 -0
- package/dist/template/lambdas/string-lambdas.test.js.map +1 -0
- package/dist/template/template-locator.d.ts +72 -0
- package/dist/template/template-locator.d.ts.map +1 -0
- package/dist/template/template-locator.js +173 -0
- package/dist/template/template-locator.js.map +1 -0
- package/dist/template/template-manager.d.ts +65 -0
- package/dist/template/template-manager.d.ts.map +1 -0
- package/dist/template/template-manager.js +185 -0
- package/dist/template/template-manager.js.map +1 -0
- package/package.json +67 -0
- package/templates/go/.travis.yml +8 -0
- package/templates/go/README.mustache +236 -0
- package/templates/go/api.mustache +452 -0
- package/templates/go/api_doc.mustache +92 -0
- package/templates/go/api_test.mustache +59 -0
- package/templates/go/client.mustache +761 -0
- package/templates/go/configuration.mustache +332 -0
- package/templates/go/git_push.sh.mustache +57 -0
- package/templates/go/gitignore.mustache +24 -0
- package/templates/go/go.mod.mustache +16 -0
- package/templates/go/go.sum.mustache +19 -0
- package/templates/go/model.mustache +21 -0
- package/templates/go/model_anyof.mustache +93 -0
- package/templates/go/model_doc.mustache +97 -0
- package/templates/go/model_enum.mustache +101 -0
- package/templates/go/model_oneof.mustache +160 -0
- package/templates/go/model_simple.mustache +572 -0
- package/templates/go/nullable_model.mustache +35 -0
- package/templates/go/openapi.mustache +1 -0
- package/templates/go/partial_header.mustache +18 -0
- package/templates/go/response.mustache +38 -0
- package/templates/go/signing.mustache +453 -0
- package/templates/go/utils.mustache +352 -0
- package/templates/php/.php-cs-fixer.dist.php +29 -0
- package/templates/php/.travis.yml +8 -0
- package/templates/php/ApiException.mustache +111 -0
- package/templates/php/Configuration.mustache +606 -0
- package/templates/php/FormDataProcessor.mustache +238 -0
- package/templates/php/HeaderSelector.mustache +265 -0
- package/templates/php/ModelInterface.mustache +103 -0
- package/templates/php/ObjectSerializer.mustache +591 -0
- package/templates/php/README.mustache +151 -0
- package/templates/php/api.mustache +891 -0
- package/templates/php/api_doc.mustache +105 -0
- package/templates/php/api_test.mustache +80 -0
- package/templates/php/composer.mustache +44 -0
- package/templates/php/git_push.sh.mustache +57 -0
- package/templates/php/gitignore +15 -0
- package/templates/php/libraries/psr-18/ApiException.mustache +114 -0
- package/templates/php/libraries/psr-18/DebugPlugin.mustache +93 -0
- package/templates/php/libraries/psr-18/README.mustache +161 -0
- package/templates/php/libraries/psr-18/api.mustache +833 -0
- package/templates/php/libraries/psr-18/api_doc.mustache +79 -0
- package/templates/php/libraries/psr-18/composer.mustache +56 -0
- package/templates/php/model.mustache +47 -0
- package/templates/php/model_doc.mustache +10 -0
- package/templates/php/model_enum.mustache +33 -0
- package/templates/php/model_generic.mustache +565 -0
- package/templates/php/model_test.mustache +88 -0
- package/templates/php/partial_header.mustache +18 -0
- package/templates/php/php_doc_auth_partial.mustache +23 -0
- package/templates/php/phpunit.xml.mustache +18 -0
- package/templates/python/README.mustache +60 -0
- package/templates/python/README_onlypackage.mustache +50 -0
- package/templates/python/__init__.mustache +1 -0
- package/templates/python/__init__api.mustache +19 -0
- package/templates/python/__init__model.mustache +22 -0
- package/templates/python/__init__package.mustache +49 -0
- package/templates/python/api.mustache +244 -0
- package/templates/python/api_client.mustache +822 -0
- package/templates/python/api_doc.mustache +81 -0
- package/templates/python/api_doc_example.mustache +38 -0
- package/templates/python/api_response.mustache +21 -0
- package/templates/python/api_test.mustache +48 -0
- package/templates/python/asyncio/rest.mustache +209 -0
- package/templates/python/common_README.mustache +85 -0
- package/templates/python/configuration.mustache +806 -0
- package/templates/python/exceptions.mustache +210 -0
- package/templates/python/exports_api.mustache +3 -0
- package/templates/python/exports_model.mustache +3 -0
- package/templates/python/exports_package.mustache +20 -0
- package/templates/python/git_push.sh.mustache +57 -0
- package/templates/python/github-workflow.mustache +35 -0
- package/templates/python/gitignore.mustache +66 -0
- package/templates/python/gitlab-ci.mustache +31 -0
- package/templates/python/httpx/rest.mustache +190 -0
- package/templates/python/model.mustache +16 -0
- package/templates/python/model_anyof.mustache +182 -0
- package/templates/python/model_doc.mustache +40 -0
- package/templates/python/model_enum.mustache +36 -0
- package/templates/python/model_generic.mustache +403 -0
- package/templates/python/model_oneof.mustache +209 -0
- package/templates/python/model_test.mustache +60 -0
- package/templates/python/partial_api.mustache +52 -0
- package/templates/python/partial_api_args.mustache +18 -0
- package/templates/python/partial_header.mustache +19 -0
- package/templates/python/py.typed.mustache +1 -0
- package/templates/python/pyproject.mustache +172 -0
- package/templates/python/python_doc_auth_partial.mustache +108 -0
- package/templates/python/requirements.mustache +23 -0
- package/templates/python/rest.mustache +254 -0
- package/templates/python/setup.mustache +60 -0
- package/templates/python/setup_cfg.mustache +2 -0
- package/templates/python/signing.mustache +422 -0
- package/templates/python/test-requirements.mustache +6 -0
- package/templates/python/tornado/rest.mustache +148 -0
- package/templates/python/tox.mustache +9 -0
- package/templates/python/travis.mustache +17 -0
- package/templates/typescript-fetch/ApiEntitiesRecord.mustache +26 -0
- package/templates/typescript-fetch/ApiEntitiesReducer.mustache +21 -0
- package/templates/typescript-fetch/ApiEntitiesSelectors.mustache +5 -0
- package/templates/typescript-fetch/README.mustache +127 -0
- package/templates/typescript-fetch/allSagas.mustache +19 -0
- package/templates/typescript-fetch/api_doc.mustache +63 -0
- package/templates/typescript-fetch/api_example.mustache +44 -0
- package/templates/typescript-fetch/apis.index.mustache +16 -0
- package/templates/typescript-fetch/apis.mustache +483 -0
- package/templates/typescript-fetch/apisAssignQueryParam.mustache +12 -0
- package/templates/typescript-fetch/gitignore +4 -0
- package/templates/typescript-fetch/index.mustache +17 -0
- package/templates/typescript-fetch/licenseInfo.mustache +11 -0
- package/templates/typescript-fetch/modelEnum.mustache +28 -0
- package/templates/typescript-fetch/modelEnumInterfaces.mustache +37 -0
- package/templates/typescript-fetch/modelGeneric.mustache +261 -0
- package/templates/typescript-fetch/modelGenericInterfaces.mustache +50 -0
- package/templates/typescript-fetch/modelOneOf.mustache +255 -0
- package/templates/typescript-fetch/modelOneOfInterfaces.mustache +6 -0
- package/templates/typescript-fetch/model_doc.mustache +41 -0
- package/templates/typescript-fetch/models.index.mustache +32 -0
- package/templates/typescript-fetch/models.mustache +24 -0
- package/templates/typescript-fetch/npmignore.mustache +1 -0
- package/templates/typescript-fetch/package.mustache +43 -0
- package/templates/typescript-fetch/recordGeneric.mustache +295 -0
- package/templates/typescript-fetch/records.mustache +17 -0
- package/templates/typescript-fetch/runtime.mustache +424 -0
- package/templates/typescript-fetch/runtimeSagasAndRecords.mustache +120 -0
- package/templates/typescript-fetch/sagaApiManager.mustache +28 -0
- package/templates/typescript-fetch/sagas.mustache +245 -0
- package/templates/typescript-fetch/sourceLibraryIndex.mustache +1 -0
- package/templates/typescript-fetch/tsconfig.esm.mustache +7 -0
- package/templates/typescript-fetch/tsconfig.mustache +25 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
{{>partial_header}}
|
|
2
|
+
|
|
3
|
+
from base64 import b64encode
|
|
4
|
+
from Crypto.IO import PEM, PKCS8
|
|
5
|
+
from Crypto.Hash import SHA256, SHA512
|
|
6
|
+
from Crypto.Hash.SHA512 import SHA512Hash
|
|
7
|
+
from Crypto.Hash.SHA256 import SHA256Hash
|
|
8
|
+
from Crypto.PublicKey import RSA, ECC
|
|
9
|
+
from Crypto.Signature import PKCS1_v1_5, pss, DSS
|
|
10
|
+
from datetime import timedelta
|
|
11
|
+
from email.utils import formatdate
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
from time import time
|
|
15
|
+
from typing import List, Optional, Union
|
|
16
|
+
from urllib.parse import urlencode, urlparse
|
|
17
|
+
|
|
18
|
+
# The constants below define a subset of HTTP headers that can be included in the
|
|
19
|
+
# HTTP signature scheme. Additional headers may be included in the signature.
|
|
20
|
+
|
|
21
|
+
# The '(request-target)' header is a calculated field that includes the HTTP verb,
|
|
22
|
+
# the URL path and the URL query.
|
|
23
|
+
HEADER_REQUEST_TARGET = '(request-target)'
|
|
24
|
+
# The time when the HTTP signature was generated.
|
|
25
|
+
HEADER_CREATED = '(created)'
|
|
26
|
+
# The time when the HTTP signature expires. The API server should reject HTTP requests
|
|
27
|
+
# that have expired.
|
|
28
|
+
HEADER_EXPIRES = '(expires)'
|
|
29
|
+
# The 'Host' header.
|
|
30
|
+
HEADER_HOST = 'Host'
|
|
31
|
+
# The 'Date' header.
|
|
32
|
+
HEADER_DATE = 'Date'
|
|
33
|
+
# When the 'Digest' header is included in the HTTP signature, the client automatically
|
|
34
|
+
# computes the digest of the HTTP request body, per RFC 3230.
|
|
35
|
+
HEADER_DIGEST = 'Digest'
|
|
36
|
+
# The 'Authorization' header is automatically generated by the client. It includes
|
|
37
|
+
# the list of signed headers and a base64-encoded signature.
|
|
38
|
+
HEADER_AUTHORIZATION = 'Authorization'
|
|
39
|
+
|
|
40
|
+
# The constants below define the cryptographic schemes for the HTTP signature scheme.
|
|
41
|
+
SCHEME_HS2019 = 'hs2019'
|
|
42
|
+
SCHEME_RSA_SHA256 = 'rsa-sha256'
|
|
43
|
+
SCHEME_RSA_SHA512 = 'rsa-sha512'
|
|
44
|
+
|
|
45
|
+
# The constants below define the signature algorithms that can be used for the HTTP
|
|
46
|
+
# signature scheme.
|
|
47
|
+
ALGORITHM_RSASSA_PSS = 'RSASSA-PSS'
|
|
48
|
+
ALGORITHM_RSASSA_PKCS1v15 = 'RSASSA-PKCS1-v1_5'
|
|
49
|
+
|
|
50
|
+
ALGORITHM_ECDSA_MODE_FIPS_186_3 = 'fips-186-3'
|
|
51
|
+
ALGORITHM_ECDSA_MODE_DETERMINISTIC_RFC6979 = 'deterministic-rfc6979'
|
|
52
|
+
ALGORITHM_ECDSA_KEY_SIGNING_ALGORITHMS = {
|
|
53
|
+
ALGORITHM_ECDSA_MODE_FIPS_186_3,
|
|
54
|
+
ALGORITHM_ECDSA_MODE_DETERMINISTIC_RFC6979
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# The cryptographic hash algorithm for the message signature.
|
|
58
|
+
HASH_SHA256 = 'sha256'
|
|
59
|
+
HASH_SHA512 = 'sha512'
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class HttpSigningConfiguration:
|
|
63
|
+
"""The configuration parameters for the HTTP signature security scheme.
|
|
64
|
+
|
|
65
|
+
The HTTP signature security scheme is used to sign HTTP requests with a private key
|
|
66
|
+
which is in possession of the API client.
|
|
67
|
+
|
|
68
|
+
An ``Authorization`` header is calculated by creating a hash of select headers,
|
|
69
|
+
and optionally the body of the HTTP request, then signing the hash value using
|
|
70
|
+
a private key. The ``Authorization`` header is added to outbound HTTP requests.
|
|
71
|
+
|
|
72
|
+
:param key_id: A string value specifying the identifier of the cryptographic key,
|
|
73
|
+
when signing HTTP requests.
|
|
74
|
+
:param signing_scheme: A string value specifying the signature scheme, when
|
|
75
|
+
signing HTTP requests.
|
|
76
|
+
Supported value are: ``hs2019``, ``rsa-sha256``, ``rsa-sha512``.
|
|
77
|
+
Avoid using ``rsa-sha256``, ``rsa-sha512`` as they are deprecated. These values are
|
|
78
|
+
available for server-side applications that only support the older
|
|
79
|
+
HTTP signature algorithms.
|
|
80
|
+
:param private_key_path: A string value specifying the path of the file containing
|
|
81
|
+
a private key. The private key is used to sign HTTP requests.
|
|
82
|
+
:param private_key_passphrase: A string value specifying the passphrase to decrypt
|
|
83
|
+
the private key.
|
|
84
|
+
:param signed_headers: A list of strings. Each value is the name of a HTTP header
|
|
85
|
+
that must be included in the HTTP signature calculation.
|
|
86
|
+
The two special signature headers ``(request-target)`` and ``(created)`` SHOULD be
|
|
87
|
+
included in SignedHeaders.
|
|
88
|
+
The ``(created)`` header expresses when the signature was created.
|
|
89
|
+
The ``(request-target)`` header is a concatenation of the lowercased :method, an
|
|
90
|
+
ASCII space, and the :path pseudo-headers.
|
|
91
|
+
When signed_headers is not specified, the client defaults to a single value,
|
|
92
|
+
``(created)``, in the list of HTTP headers.
|
|
93
|
+
When SignedHeaders contains the 'Digest' value, the client performs the
|
|
94
|
+
following operations:
|
|
95
|
+
1. Calculate a digest of request body, as specified in `RFC3230,
|
|
96
|
+
section 4.[3].2<https://datatracker.ietf.org/doc/html/rfc3230#section-4.[3].2>`_.
|
|
97
|
+
2. Set the ``Digest`` header in the request body.
|
|
98
|
+
3. Include the ``Digest`` header and value in the HTTP signature.
|
|
99
|
+
:param signing_algorithm: A string value specifying the signature algorithm, when
|
|
100
|
+
signing HTTP requests.
|
|
101
|
+
Supported values are:
|
|
102
|
+
1. For RSA keys: RSASSA-PSS, RSASSA-PKCS1-v1_5.
|
|
103
|
+
2. For ECDSA keys: fips-186-3, deterministic-rfc6979.
|
|
104
|
+
If None, the signing algorithm is inferred from the private key.
|
|
105
|
+
The default signing algorithm for RSA keys is RSASSA-PSS.
|
|
106
|
+
The default signing algorithm for ECDSA keys is fips-186-3.
|
|
107
|
+
:param hash_algorithm: The hash algorithm for the signature. Supported values are
|
|
108
|
+
sha256 and sha512.
|
|
109
|
+
If the signing_scheme is rsa-sha256, the hash algorithm must be set
|
|
110
|
+
to None or sha256.
|
|
111
|
+
If the signing_scheme is rsa-sha512, the hash algorithm must be set
|
|
112
|
+
to None or sha512.
|
|
113
|
+
:param signature_max_validity: The signature max validity, expressed as
|
|
114
|
+
a datetime.timedelta value. It must be a positive value.
|
|
115
|
+
"""
|
|
116
|
+
def __init__(self,
|
|
117
|
+
key_id: str,
|
|
118
|
+
signing_scheme: str,
|
|
119
|
+
private_key_path: str,
|
|
120
|
+
private_key_passphrase: Union[None, str]=None,
|
|
121
|
+
signed_headers: Optional[List[str]]=None,
|
|
122
|
+
signing_algorithm: Optional[str]=None,
|
|
123
|
+
hash_algorithm: Optional[str]=None,
|
|
124
|
+
signature_max_validity: Optional[timedelta]=None,
|
|
125
|
+
) -> None:
|
|
126
|
+
self.key_id = key_id
|
|
127
|
+
if signing_scheme not in {SCHEME_HS2019, SCHEME_RSA_SHA256, SCHEME_RSA_SHA512}:
|
|
128
|
+
raise Exception("Unsupported security scheme: {0}".format(signing_scheme))
|
|
129
|
+
self.signing_scheme = signing_scheme
|
|
130
|
+
if not os.path.exists(private_key_path):
|
|
131
|
+
raise Exception("Private key file does not exist")
|
|
132
|
+
self.private_key_path = private_key_path
|
|
133
|
+
self.private_key_passphrase = private_key_passphrase
|
|
134
|
+
self.signing_algorithm = signing_algorithm
|
|
135
|
+
self.hash_algorithm = hash_algorithm
|
|
136
|
+
if signing_scheme == SCHEME_RSA_SHA256:
|
|
137
|
+
if self.hash_algorithm is None:
|
|
138
|
+
self.hash_algorithm = HASH_SHA256
|
|
139
|
+
elif self.hash_algorithm != HASH_SHA256:
|
|
140
|
+
raise Exception("Hash algorithm must be sha256 when security scheme is %s" %
|
|
141
|
+
SCHEME_RSA_SHA256)
|
|
142
|
+
elif signing_scheme == SCHEME_RSA_SHA512:
|
|
143
|
+
if self.hash_algorithm is None:
|
|
144
|
+
self.hash_algorithm = HASH_SHA512
|
|
145
|
+
elif self.hash_algorithm != HASH_SHA512:
|
|
146
|
+
raise Exception("Hash algorithm must be sha512 when security scheme is %s" %
|
|
147
|
+
SCHEME_RSA_SHA512)
|
|
148
|
+
elif signing_scheme == SCHEME_HS2019:
|
|
149
|
+
if self.hash_algorithm is None:
|
|
150
|
+
self.hash_algorithm = HASH_SHA256
|
|
151
|
+
elif self.hash_algorithm not in {HASH_SHA256, HASH_SHA512}:
|
|
152
|
+
raise Exception("Invalid hash algorithm")
|
|
153
|
+
if signature_max_validity is not None and signature_max_validity.total_seconds() < 0:
|
|
154
|
+
raise Exception("The signature max validity must be a positive value")
|
|
155
|
+
self.signature_max_validity = signature_max_validity
|
|
156
|
+
# If the user has not provided any signed_headers, the default must be set to '(created)',
|
|
157
|
+
# as specified in the 'HTTP signature' standard.
|
|
158
|
+
if signed_headers is None or len(signed_headers) == 0:
|
|
159
|
+
signed_headers = [HEADER_CREATED]
|
|
160
|
+
if self.signature_max_validity is None and HEADER_EXPIRES in signed_headers:
|
|
161
|
+
raise Exception(
|
|
162
|
+
"Signature max validity must be set when "
|
|
163
|
+
"'(expires)' signature parameter is specified")
|
|
164
|
+
if len(signed_headers) != len(set(signed_headers)):
|
|
165
|
+
raise Exception("Cannot have duplicates in the signed_headers parameter")
|
|
166
|
+
if HEADER_AUTHORIZATION in signed_headers:
|
|
167
|
+
raise Exception("'Authorization' header cannot be included in signed headers")
|
|
168
|
+
self.signed_headers = signed_headers
|
|
169
|
+
self.private_key: Optional[Union[ECC.EccKey, RSA.RsaKey]] = None
|
|
170
|
+
"""The private key used to sign HTTP requests.
|
|
171
|
+
Initialized when the PEM-encoded private key is loaded from a file.
|
|
172
|
+
"""
|
|
173
|
+
self.host: Optional[str] = None
|
|
174
|
+
"""The host name, optionally followed by a colon and TCP port number.
|
|
175
|
+
"""
|
|
176
|
+
self._load_private_key()
|
|
177
|
+
|
|
178
|
+
def get_http_signature_headers(self, resource_path, method, headers, body, query_params):
|
|
179
|
+
"""Create a cryptographic message signature for the HTTP request and add the signed headers.
|
|
180
|
+
|
|
181
|
+
:param resource_path : A string representation of the HTTP request resource path.
|
|
182
|
+
:param method: A string representation of the HTTP request method, e.g. GET, POST.
|
|
183
|
+
:param headers: A dict containing the HTTP request headers.
|
|
184
|
+
:param body: The object representing the HTTP request body.
|
|
185
|
+
:param query_params: A string representing the HTTP request query parameters.
|
|
186
|
+
:return: A dict of HTTP headers that must be added to the outbound HTTP request.
|
|
187
|
+
"""
|
|
188
|
+
if method is None:
|
|
189
|
+
raise Exception("HTTP method must be set")
|
|
190
|
+
if resource_path is None:
|
|
191
|
+
raise Exception("Resource path must be set")
|
|
192
|
+
|
|
193
|
+
signed_headers_list, request_headers_dict = self._get_signed_header_info(
|
|
194
|
+
resource_path, method, headers, body, query_params)
|
|
195
|
+
|
|
196
|
+
header_items = [
|
|
197
|
+
"{0}: {1}".format(key.lower(), value) for key, value in signed_headers_list]
|
|
198
|
+
string_to_sign = "\n".join(header_items)
|
|
199
|
+
|
|
200
|
+
digest, digest_prefix = self._get_message_digest(string_to_sign.encode())
|
|
201
|
+
b64_signed_msg = self._sign_digest(digest)
|
|
202
|
+
|
|
203
|
+
request_headers_dict[HEADER_AUTHORIZATION] = self._get_authorization_header(
|
|
204
|
+
signed_headers_list, b64_signed_msg)
|
|
205
|
+
|
|
206
|
+
return request_headers_dict
|
|
207
|
+
|
|
208
|
+
def get_public_key(self):
|
|
209
|
+
"""Returns the public key object associated with the private key.
|
|
210
|
+
"""
|
|
211
|
+
pubkey: Optional[Union[ECC.EccKey, RSA.RsaKey]] = None
|
|
212
|
+
if isinstance(self.private_key, RSA.RsaKey):
|
|
213
|
+
pubkey = self.private_key.publickey()
|
|
214
|
+
elif isinstance(self.private_key, ECC.EccKey):
|
|
215
|
+
pubkey = self.private_key.public_key()
|
|
216
|
+
return pubkey
|
|
217
|
+
|
|
218
|
+
def _load_private_key(self):
|
|
219
|
+
"""Load the private key used to sign HTTP requests.
|
|
220
|
+
The private key is used to sign HTTP requests as defined in
|
|
221
|
+
https://datatracker.ietf.org/doc/draft-cavage-http-signatures/.
|
|
222
|
+
"""
|
|
223
|
+
if self.private_key is not None:
|
|
224
|
+
return
|
|
225
|
+
with open(self.private_key_path, 'r') as f:
|
|
226
|
+
pem_data = f.read()
|
|
227
|
+
# Verify PEM Pre-Encapsulation Boundary
|
|
228
|
+
r = re.compile(r"\s*-----BEGIN (.*)-----\s+")
|
|
229
|
+
m = r.match(pem_data)
|
|
230
|
+
if not m:
|
|
231
|
+
raise ValueError("Not a valid PEM pre boundary")
|
|
232
|
+
pem_header = m.group(1)
|
|
233
|
+
if pem_header == 'RSA PRIVATE KEY':
|
|
234
|
+
self.private_key = RSA.importKey(pem_data, self.private_key_passphrase)
|
|
235
|
+
elif pem_header == 'EC PRIVATE KEY':
|
|
236
|
+
self.private_key = ECC.import_key(pem_data, self.private_key_passphrase)
|
|
237
|
+
elif pem_header in {'PRIVATE KEY', 'ENCRYPTED PRIVATE KEY'}:
|
|
238
|
+
# Key is in PKCS8 format, which is capable of holding many different
|
|
239
|
+
# types of private keys, not just EC keys.
|
|
240
|
+
if self.private_key_passphrase is not None:
|
|
241
|
+
passphrase = self.private_key_passphrase.encode("utf-8")
|
|
242
|
+
else:
|
|
243
|
+
passphrase = None
|
|
244
|
+
(key_binary, pem_header, is_encrypted) = PEM.decode(pem_data, passphrase)
|
|
245
|
+
(oid, privkey, params) = \
|
|
246
|
+
PKCS8.unwrap(key_binary, passphrase=self.private_key_passphrase)
|
|
247
|
+
if oid == '1.[2].840.[10045].2.1':
|
|
248
|
+
self.private_key = ECC.import_key(pem_data, self.private_key_passphrase)
|
|
249
|
+
else:
|
|
250
|
+
raise Exception("Unsupported key: {0}. OID: {1}".format(pem_header, oid))
|
|
251
|
+
else:
|
|
252
|
+
raise Exception("Unsupported key: {0}".format(pem_header))
|
|
253
|
+
# Validate the specified signature algorithm is compatible with the private key.
|
|
254
|
+
if self.signing_algorithm is not None:
|
|
255
|
+
supported_algs = None
|
|
256
|
+
if isinstance(self.private_key, RSA.RsaKey):
|
|
257
|
+
supported_algs = {ALGORITHM_RSASSA_PSS, ALGORITHM_RSASSA_PKCS1v15}
|
|
258
|
+
elif isinstance(self.private_key, ECC.EccKey):
|
|
259
|
+
supported_algs = ALGORITHM_ECDSA_KEY_SIGNING_ALGORITHMS
|
|
260
|
+
if supported_algs is not None and self.signing_algorithm not in supported_algs:
|
|
261
|
+
raise Exception(
|
|
262
|
+
"Signing algorithm {0} is not compatible with private key".format(
|
|
263
|
+
self.signing_algorithm))
|
|
264
|
+
|
|
265
|
+
def _get_signed_header_info(self, resource_path, method, headers, body, query_params):
|
|
266
|
+
"""Build the HTTP headers (name, value) that need to be included in
|
|
267
|
+
the HTTP signature scheme.
|
|
268
|
+
|
|
269
|
+
:param resource_path : A string representation of the HTTP request resource path.
|
|
270
|
+
:param method: A string representation of the HTTP request method, e.g. GET, POST.
|
|
271
|
+
:param headers: A dict containing the HTTP request headers.
|
|
272
|
+
:param body: The object (e.g. a dict) representing the HTTP request body.
|
|
273
|
+
:param query_params: A string representing the HTTP request query parameters.
|
|
274
|
+
:return: A tuple containing two dict objects:
|
|
275
|
+
The first dict contains the HTTP headers that are used to calculate
|
|
276
|
+
the HTTP signature.
|
|
277
|
+
The second dict contains the HTTP headers that must be added to
|
|
278
|
+
the outbound HTTP request.
|
|
279
|
+
"""
|
|
280
|
+
|
|
281
|
+
if body is None:
|
|
282
|
+
body = ''
|
|
283
|
+
else:
|
|
284
|
+
body = body.to_json()
|
|
285
|
+
|
|
286
|
+
# Build the '(request-target)' HTTP signature parameter.
|
|
287
|
+
target_host = urlparse(self.host).netloc
|
|
288
|
+
target_path = urlparse(self.host).path
|
|
289
|
+
request_target = method.lower() + " " + target_path + resource_path
|
|
290
|
+
if query_params:
|
|
291
|
+
request_target += "?" + urlencode(query_params)
|
|
292
|
+
|
|
293
|
+
# Get UNIX time, e.g. seconds since epoch, not including leap seconds.
|
|
294
|
+
now = time()
|
|
295
|
+
# Format date per RFC 7231 section-7.[1].1.[2]. An example is:
|
|
296
|
+
# Date: Wed, 21 Oct 2015 07:28:00 GMT
|
|
297
|
+
cdate = formatdate(timeval=now, localtime=False, usegmt=True)
|
|
298
|
+
# The '(created)' value MUST be a Unix timestamp integer value.
|
|
299
|
+
# Subsecond precision is not supported.
|
|
300
|
+
created = int(now)
|
|
301
|
+
if self.signature_max_validity is not None:
|
|
302
|
+
expires = now + self.signature_max_validity.total_seconds()
|
|
303
|
+
|
|
304
|
+
signed_headers_list = []
|
|
305
|
+
request_headers_dict = {}
|
|
306
|
+
for hdr_key in self.signed_headers:
|
|
307
|
+
hdr_key = hdr_key.lower()
|
|
308
|
+
if hdr_key == HEADER_REQUEST_TARGET:
|
|
309
|
+
value = request_target
|
|
310
|
+
elif hdr_key == HEADER_CREATED:
|
|
311
|
+
value = '{0}'.format(created)
|
|
312
|
+
elif hdr_key == HEADER_EXPIRES:
|
|
313
|
+
value = '{0}'.format(expires)
|
|
314
|
+
elif hdr_key == HEADER_DATE.lower():
|
|
315
|
+
value = cdate
|
|
316
|
+
request_headers_dict[HEADER_DATE] = '{0}'.format(cdate)
|
|
317
|
+
elif hdr_key == HEADER_DIGEST.lower():
|
|
318
|
+
request_body = body.encode()
|
|
319
|
+
body_digest, digest_prefix = self._get_message_digest(request_body)
|
|
320
|
+
b64_body_digest = b64encode(body_digest.digest())
|
|
321
|
+
value = digest_prefix + b64_body_digest.decode('ascii')
|
|
322
|
+
request_headers_dict[HEADER_DIGEST] = '{0}{1}'.format(
|
|
323
|
+
digest_prefix, b64_body_digest.decode('ascii'))
|
|
324
|
+
elif hdr_key == HEADER_HOST.lower():
|
|
325
|
+
if isinstance(target_host, bytes):
|
|
326
|
+
value = target_host.decode('ascii')
|
|
327
|
+
else:
|
|
328
|
+
value = target_host
|
|
329
|
+
request_headers_dict[HEADER_HOST] = value
|
|
330
|
+
else:
|
|
331
|
+
value = next((v for k, v in headers.items() if k.lower() == hdr_key), None)
|
|
332
|
+
if value is None:
|
|
333
|
+
raise Exception(
|
|
334
|
+
"Cannot sign HTTP request. "
|
|
335
|
+
"Request does not contain the '{0}' header".format(hdr_key))
|
|
336
|
+
signed_headers_list.append((hdr_key, value))
|
|
337
|
+
|
|
338
|
+
return signed_headers_list, request_headers_dict
|
|
339
|
+
|
|
340
|
+
def _get_message_digest(self, data):
|
|
341
|
+
"""Calculates and returns a cryptographic digest of a specified HTTP request.
|
|
342
|
+
|
|
343
|
+
:param data: The string representation of the date to be hashed with a cryptographic hash.
|
|
344
|
+
:return: A tuple of (digest, prefix).
|
|
345
|
+
The digest is a hashing object that contains the cryptographic digest of
|
|
346
|
+
the HTTP request.
|
|
347
|
+
The prefix is a string that identifies the cryptographic hash. It is used
|
|
348
|
+
to generate the 'Digest' header as specified in RFC 3230.
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
digest: Union[SHA256Hash, SHA512Hash]
|
|
352
|
+
|
|
353
|
+
if self.hash_algorithm == HASH_SHA512:
|
|
354
|
+
digest = SHA512.new()
|
|
355
|
+
prefix = 'SHA-512='
|
|
356
|
+
elif self.hash_algorithm == HASH_SHA256:
|
|
357
|
+
digest = SHA256.new()
|
|
358
|
+
prefix = 'SHA-256='
|
|
359
|
+
else:
|
|
360
|
+
raise Exception("Unsupported hash algorithm: {0}".format(self.hash_algorithm))
|
|
361
|
+
digest.update(data)
|
|
362
|
+
return digest, prefix
|
|
363
|
+
|
|
364
|
+
def _sign_digest(self, digest):
|
|
365
|
+
"""Signs a message digest with a private key specified in the signing_info.
|
|
366
|
+
|
|
367
|
+
:param digest: A hashing object that contains the cryptographic digest of the HTTP request.
|
|
368
|
+
:return: A base-64 string representing the cryptographic signature of the input digest.
|
|
369
|
+
"""
|
|
370
|
+
sig_alg = self.signing_algorithm
|
|
371
|
+
if isinstance(self.private_key, RSA.RsaKey):
|
|
372
|
+
if sig_alg is None or sig_alg == ALGORITHM_RSASSA_PSS:
|
|
373
|
+
# RSASSA-PSS in Section 8.1 of RFC8017.
|
|
374
|
+
signature = pss.new(self.private_key).sign(digest)
|
|
375
|
+
elif sig_alg == ALGORITHM_RSASSA_PKCS1v15:
|
|
376
|
+
# RSASSA-PKCS1-v1_5 in Section 8.2 of RFC8017.
|
|
377
|
+
signature = PKCS1_v1_5.new(self.private_key).sign(digest)
|
|
378
|
+
else:
|
|
379
|
+
raise Exception("Unsupported signature algorithm: {0}".format(sig_alg))
|
|
380
|
+
elif isinstance(self.private_key, ECC.EccKey):
|
|
381
|
+
if sig_alg is None:
|
|
382
|
+
sig_alg = ALGORITHM_ECDSA_MODE_FIPS_186_3
|
|
383
|
+
if sig_alg in ALGORITHM_ECDSA_KEY_SIGNING_ALGORITHMS:
|
|
384
|
+
# draft-ietf-httpbis-message-signatures-00 does not specify the ECDSA encoding.
|
|
385
|
+
# Issue: https://github.com/w3c-ccg/http-signatures/issues/107
|
|
386
|
+
signature = DSS.new(key=self.private_key, mode=sig_alg,
|
|
387
|
+
encoding='der').sign(digest)
|
|
388
|
+
else:
|
|
389
|
+
raise Exception("Unsupported signature algorithm: {0}".format(sig_alg))
|
|
390
|
+
else:
|
|
391
|
+
raise Exception("Unsupported private key: {0}".format(type(self.private_key)))
|
|
392
|
+
return b64encode(signature)
|
|
393
|
+
|
|
394
|
+
def _get_authorization_header(self, signed_headers, signed_msg):
|
|
395
|
+
"""Calculates and returns the value of the 'Authorization' header when signing HTTP requests.
|
|
396
|
+
|
|
397
|
+
:param signed_headers : A list of tuples. Each value is the name of a HTTP header that
|
|
398
|
+
must be included in the HTTP signature calculation.
|
|
399
|
+
:param signed_msg: A base-64 encoded string representation of the signature.
|
|
400
|
+
:return: The string value of the 'Authorization' header, representing the signature
|
|
401
|
+
of the HTTP request.
|
|
402
|
+
"""
|
|
403
|
+
created_ts = None
|
|
404
|
+
expires_ts = None
|
|
405
|
+
for k, v in signed_headers:
|
|
406
|
+
if k == HEADER_CREATED:
|
|
407
|
+
created_ts = v
|
|
408
|
+
elif k == HEADER_EXPIRES:
|
|
409
|
+
expires_ts = v
|
|
410
|
+
lower_keys = [k.lower() for k, v in signed_headers]
|
|
411
|
+
headers_value = " ".join(lower_keys)
|
|
412
|
+
|
|
413
|
+
auth_str = "Signature keyId=\"{0}\",algorithm=\"{1}\",".format(
|
|
414
|
+
self.key_id, self.signing_scheme)
|
|
415
|
+
if created_ts is not None:
|
|
416
|
+
auth_str = auth_str + "created={0},".format(created_ts)
|
|
417
|
+
if expires_ts is not None:
|
|
418
|
+
auth_str = auth_str + "expires={0},".format(expires_ts)
|
|
419
|
+
auth_str = auth_str + "headers=\"{0}\",signature=\"{1}\"".format(
|
|
420
|
+
headers_value, signed_msg.decode('ascii'))
|
|
421
|
+
|
|
422
|
+
return auth_str
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
{{>partial_header}}
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import io
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
from urllib.parse import urlencode
|
|
11
|
+
import tornado
|
|
12
|
+
import tornado.gen
|
|
13
|
+
from tornado import httpclient
|
|
14
|
+
from urllib3.filepost import encode_multipart_formdata
|
|
15
|
+
|
|
16
|
+
from {{packageName}}.exceptions import ApiException, ApiValueError
|
|
17
|
+
|
|
18
|
+
RESTResponseType = httpclient.HTTPResponse
|
|
19
|
+
|
|
20
|
+
class RESTResponse(io.IOBase):
|
|
21
|
+
|
|
22
|
+
def __init__(self, resp) -> None:
|
|
23
|
+
self.response = resp
|
|
24
|
+
self.status = resp.code
|
|
25
|
+
self.reason = resp.reason
|
|
26
|
+
self.data = None
|
|
27
|
+
|
|
28
|
+
def read(self):
|
|
29
|
+
if self.data is None:
|
|
30
|
+
self.data = self.response.body
|
|
31
|
+
return self.data
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def headers(self):
|
|
35
|
+
"""Returns a CIMultiDictProxy of response headers."""
|
|
36
|
+
return self.response.headers
|
|
37
|
+
|
|
38
|
+
def getheaders(self):
|
|
39
|
+
"""Returns a CIMultiDictProxy of the response headers; use ``headers`` instead."""
|
|
40
|
+
return self.response.headers
|
|
41
|
+
|
|
42
|
+
def getheader(self, name, default=None):
|
|
43
|
+
"""Returns a given response header; use ``headers`` instead."""
|
|
44
|
+
return self.response.headers.get(name, default)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class RESTClientObject:
|
|
48
|
+
|
|
49
|
+
def __init__(self, configuration) -> None:
|
|
50
|
+
|
|
51
|
+
self.ca_certs = configuration.ssl_ca_cert
|
|
52
|
+
self.client_key = configuration.key_file
|
|
53
|
+
self.client_cert = configuration.cert_file
|
|
54
|
+
|
|
55
|
+
self.proxy_port = self.proxy_host = None
|
|
56
|
+
|
|
57
|
+
# https pool manager
|
|
58
|
+
if configuration.proxy:
|
|
59
|
+
self.proxy_port = 80
|
|
60
|
+
self.proxy_host = configuration.proxy
|
|
61
|
+
|
|
62
|
+
self.pool_manager = httpclient.AsyncHTTPClient()
|
|
63
|
+
|
|
64
|
+
@tornado.gen.coroutine
|
|
65
|
+
def request(
|
|
66
|
+
self,
|
|
67
|
+
method,
|
|
68
|
+
url,
|
|
69
|
+
headers=None,
|
|
70
|
+
body=None,
|
|
71
|
+
post_params=None,
|
|
72
|
+
_request_timeout=None
|
|
73
|
+
):
|
|
74
|
+
"""Execute Request
|
|
75
|
+
|
|
76
|
+
:param method: http request method
|
|
77
|
+
:param url: http request url
|
|
78
|
+
:param headers: http request headers
|
|
79
|
+
:param body: request json body, for `application/json`
|
|
80
|
+
:param post_params: request post parameters,
|
|
81
|
+
`application/x-www-form-urlencoded`
|
|
82
|
+
and `multipart/form-data`
|
|
83
|
+
:param _request_timeout: timeout setting for this request. If one
|
|
84
|
+
number provided, it will be total request
|
|
85
|
+
timeout. It can also be a pair (tuple) of
|
|
86
|
+
(connection, read) timeouts.
|
|
87
|
+
"""
|
|
88
|
+
method = method.upper()
|
|
89
|
+
assert method in [
|
|
90
|
+
'GET',
|
|
91
|
+
'HEAD',
|
|
92
|
+
'DELETE',
|
|
93
|
+
'POST',
|
|
94
|
+
'PUT',
|
|
95
|
+
'PATCH',
|
|
96
|
+
'OPTIONS'
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
if post_params and body:
|
|
100
|
+
raise ApiValueError(
|
|
101
|
+
"body parameter cannot be used with post_params parameter."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
request = httpclient.HTTPRequest(url)
|
|
105
|
+
request.allow_nonstandard_methods = True
|
|
106
|
+
request.ca_certs = self.ca_certs
|
|
107
|
+
request.client_key = self.client_key
|
|
108
|
+
request.client_cert = self.client_cert
|
|
109
|
+
request.proxy_host = self.proxy_host
|
|
110
|
+
request.proxy_port = self.proxy_port
|
|
111
|
+
request.method = method
|
|
112
|
+
if headers:
|
|
113
|
+
request.headers = headers
|
|
114
|
+
if 'Content-Type' not in headers:
|
|
115
|
+
request.headers['Content-Type'] = 'application/json'
|
|
116
|
+
request.request_timeout = _request_timeout or 5 * 60
|
|
117
|
+
|
|
118
|
+
post_params = post_params or {}
|
|
119
|
+
|
|
120
|
+
# For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
|
|
121
|
+
if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
|
|
122
|
+
if re.search('json', headers['Content-Type'], re.IGNORECASE):
|
|
123
|
+
if body:
|
|
124
|
+
body = json.dumps(body)
|
|
125
|
+
request.body = body
|
|
126
|
+
elif headers['Content-Type'] == 'application/x-www-form-urlencoded':
|
|
127
|
+
request.body = urlencode(post_params)
|
|
128
|
+
elif headers['Content-Type'] == 'multipart/form-data':
|
|
129
|
+
multipart = encode_multipart_formdata(post_params)
|
|
130
|
+
request.body, headers['Content-Type'] = multipart
|
|
131
|
+
# Pass a `bytes` parameter directly in the body to support
|
|
132
|
+
# other content types than Json when `body` argument is provided
|
|
133
|
+
# in serialized form
|
|
134
|
+
elif isinstance(body, bytes):
|
|
135
|
+
request.body = body
|
|
136
|
+
else:
|
|
137
|
+
# Cannot generate the request from given parameters
|
|
138
|
+
msg = """Cannot prepare a request message for provided
|
|
139
|
+
arguments. Please check that your arguments match
|
|
140
|
+
declared content type."""
|
|
141
|
+
raise ApiException(status=0, reason=msg)
|
|
142
|
+
|
|
143
|
+
r = yield self.pool_manager.fetch(request, raise_error=False)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
r = RESTResponse(r)
|
|
147
|
+
|
|
148
|
+
raise tornado.gen.Return(r)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# ref: https://docs.travis-ci.com/user/languages/python
|
|
2
|
+
language: python
|
|
3
|
+
python:
|
|
4
|
+
- "3.9"
|
|
5
|
+
- "3.10"
|
|
6
|
+
- "3.11"
|
|
7
|
+
- "3.12"
|
|
8
|
+
- "3.13"
|
|
9
|
+
# uncomment the following if needed
|
|
10
|
+
#- "3.13-dev" # 3.13 development branch
|
|
11
|
+
#- "nightly" # nightly build
|
|
12
|
+
# command to install dependencies
|
|
13
|
+
install:
|
|
14
|
+
- "pip install -r requirements.txt"
|
|
15
|
+
- "pip install -r test-requirements.txt"
|
|
16
|
+
# command to run tests
|
|
17
|
+
script: pytest --cov={{{packageName}}}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {Map, Record, RecordOf} from 'immutable';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
{{#models}}
|
|
5
|
+
{{#model}}
|
|
6
|
+
{{#isEntity}}
|
|
7
|
+
{{classname}}RecordEntity,
|
|
8
|
+
{{/isEntity}}
|
|
9
|
+
{{/model}}
|
|
10
|
+
{{/models}}
|
|
11
|
+
} from "./models/index{{importFileExtension}}"
|
|
12
|
+
|
|
13
|
+
export const ApiEntitiesRecordProps = {
|
|
14
|
+
recType: "ApiEntitiesRecord" as "ApiEntitiesRecord",
|
|
15
|
+
{{#models}}
|
|
16
|
+
{{#model}}
|
|
17
|
+
{{#isEntity}}
|
|
18
|
+
{{#camelcase}}{{classname}}{{/camelcase}}: ({{classname}}RecordEntity(), Map<string, {{classname}}RecordEntity>()),
|
|
19
|
+
{{/isEntity}}
|
|
20
|
+
{{/model}}
|
|
21
|
+
{{/models}}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type ApiEntitiesRecordPropsType = typeof ApiEntitiesRecordProps;
|
|
25
|
+
export const ApiEntitiesRecord = Record(ApiEntitiesRecordProps, ApiEntitiesRecordProps.recType);
|
|
26
|
+
export type ApiEntitiesRecord = RecordOf<ApiEntitiesRecordPropsType>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {ApiEntitiesRecord} from "./ApiEntitiesRecord{{importFileExtension}}";
|
|
2
|
+
import {ReducerBuilder} from "redux-ts-simple";
|
|
3
|
+
import {normalizedEntities} from "./runtimeSagasAndRecords{{importFileExtension}}";
|
|
4
|
+
|
|
5
|
+
export const ApiEntitiesReducer = new ReducerBuilder(ApiEntitiesRecord())
|
|
6
|
+
.on(normalizedEntities, (state, action): ApiEntitiesRecord => {
|
|
7
|
+
const {entities} = action.payload;
|
|
8
|
+
return state.withMutations(mutableState => {
|
|
9
|
+
for (const entityKey in entities) {
|
|
10
|
+
const entityMap = entities[entityKey];
|
|
11
|
+
const currentEntityMap = mutableState.get(entityKey as any);
|
|
12
|
+
if (currentEntityMap) {
|
|
13
|
+
let mergedEntityMap = currentEntityMap.mergeDeep(entityMap);
|
|
14
|
+
if (!mergedEntityMap.equals(currentEntityMap)) {
|
|
15
|
+
mutableState.set(entityKey as any, mergedEntityMap);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
})
|
|
21
|
+
.build();
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export let getApiEntitiesState: (state: any) => any = (state: any) => state.app.apiEntities;
|
|
2
|
+
|
|
3
|
+
export function setApiEntitiesStateGetter(getter: (state: any) => any) { // Use this to customize the location where you have placed your ApiEntitiesRecord in your project.
|
|
4
|
+
getApiEntitiesState = getter;
|
|
5
|
+
}
|