From 27c7310fde34aca0844ac9807da0c00e3a55608a Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Sat, 27 Jul 2024 08:40:54 +0530 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8Source=20Trustpilot:=20Migrate=20Pytho?= =?UTF-8?q?n=20CDK=20to=20Low-code=20CDK=20(#36955)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Natik Gadzhi --- .../source-trustpilot/.dockerignore | 6 - .../connectors/source-trustpilot/README.md | 5 +- .../{unit_tests => }/__init__.py | 0 .../acceptance-test-config.yml | 5 + .../integration_tests/abnormal_state.json | 25 +- .../integration_tests/invalid_config.json | 8 +- .../integration_tests/sample_config.json | 4 +- .../integration_tests/sample_state.json | 25 +- .../source-trustpilot/metadata.yaml | 4 +- .../connectors/source-trustpilot/poetry.lock | 509 ++++++++++++++---- .../source-trustpilot/pyproject.toml | 10 +- .../source_trustpilot/auth.py | 61 --- .../source_trustpilot/manifest.yaml | 339 ++++++++++++ .../schemas/business_units.json | 5 +- .../schemas/configured_business_units.json | 7 +- .../schemas/private_reviews.json | 37 +- .../source_trustpilot/source.py | 91 +--- .../source_trustpilot/spec.yaml | 85 --- .../source_trustpilot/streams.py | 276 ---------- .../unit_tests/test_incremental_streams.py | 66 --- .../unit_tests/test_source.py | 27 - .../unit_tests/test_streams.py | 88 --- docs/integrations/sources/trustpilot.md | 3 +- 23 files changed, 836 insertions(+), 850 deletions(-) delete mode 100644 airbyte-integrations/connectors/source-trustpilot/.dockerignore rename airbyte-integrations/connectors/source-trustpilot/{unit_tests => }/__init__.py (100%) delete mode 100644 airbyte-integrations/connectors/source-trustpilot/source_trustpilot/auth.py create mode 100644 airbyte-integrations/connectors/source-trustpilot/source_trustpilot/manifest.yaml delete mode 100644 airbyte-integrations/connectors/source-trustpilot/source_trustpilot/spec.yaml delete mode 100644 airbyte-integrations/connectors/source-trustpilot/source_trustpilot/streams.py delete mode 100644 airbyte-integrations/connectors/source-trustpilot/unit_tests/test_incremental_streams.py delete mode 100644 airbyte-integrations/connectors/source-trustpilot/unit_tests/test_source.py delete mode 100644 airbyte-integrations/connectors/source-trustpilot/unit_tests/test_streams.py diff --git a/airbyte-integrations/connectors/source-trustpilot/.dockerignore b/airbyte-integrations/connectors/source-trustpilot/.dockerignore deleted file mode 100644 index 099f26da1017..000000000000 --- a/airbyte-integrations/connectors/source-trustpilot/.dockerignore +++ /dev/null @@ -1,6 +0,0 @@ -* -!Dockerfile -!main.py -!source_trustpilot -!setup.py -!secrets diff --git a/airbyte-integrations/connectors/source-trustpilot/README.md b/airbyte-integrations/connectors/source-trustpilot/README.md index 5c1455a82ad7..9ffa24b435b5 100644 --- a/airbyte-integrations/connectors/source-trustpilot/README.md +++ b/airbyte-integrations/connectors/source-trustpilot/README.md @@ -1,7 +1,7 @@ # Trustpilot source connector -This is the repository for the Trustpilot source connector, written in Python. +This is the repository for the Trustpilot configuration based source connector. For information about how to use this connector within Airbyte, see [the documentation](https://docs.airbyte.com/integrations/sources/trustpilot). ## Local development @@ -20,12 +20,13 @@ poetry install --with dev ### Create credentials **If you are a community contributor**, follow the instructions in the [documentation](https://docs.airbyte.com/integrations/sources/trustpilot) -to generate the necessary credentials. Then create a file `secrets/config.json` conforming to the `source_trustpilot/spec.yaml` file. +to generate the necessary credentials. Then create a file `secrets/config.json` conforming to the `source_trustpilot/spec.yaml` file or spec in the `manifest.yaml`. Note that any directory named `secrets` is gitignored across the entire Airbyte repo, so there is no danger of accidentally checking in sensitive information. See `sample_files/sample_config.json` for a sample config file. ### Locally running the connector + ``` poetry run source-trustpilot spec poetry run source-trustpilot check --config secrets/config.json diff --git a/airbyte-integrations/connectors/source-trustpilot/unit_tests/__init__.py b/airbyte-integrations/connectors/source-trustpilot/__init__.py similarity index 100% rename from airbyte-integrations/connectors/source-trustpilot/unit_tests/__init__.py rename to airbyte-integrations/connectors/source-trustpilot/__init__.py diff --git a/airbyte-integrations/connectors/source-trustpilot/acceptance-test-config.yml b/airbyte-integrations/connectors/source-trustpilot/acceptance-test-config.yml index 6ee6dc17190a..3a913874deaf 100644 --- a/airbyte-integrations/connectors/source-trustpilot/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-trustpilot/acceptance-test-config.yml @@ -1,10 +1,13 @@ # See [Connector Acceptance Tests](https://docs.airbyte.com/connector-development/testing-connectors/connector-acceptance-tests-reference) # for more information about how to configure these tests connector_image: airbyte/source-trustpilot:dev +test_strictness_level: low acceptance_tests: spec: tests: - spec_path: "source_trustpilot/spec.yaml" + backward_compatibility_tests_config: + disable_for_version: 0.1.0 connection: tests: - config_path: "secrets/config.json" @@ -14,6 +17,8 @@ acceptance_tests: discovery: tests: - config_path: "secrets/config.json" + backward_compatibility_tests_config: + disable_for_version: 0.1.0 basic_read: tests: - config_path: "secrets/config.json" diff --git a/airbyte-integrations/connectors/source-trustpilot/integration_tests/abnormal_state.json b/airbyte-integrations/connectors/source-trustpilot/integration_tests/abnormal_state.json index 22a101035140..82ae8ce93b82 100644 --- a/airbyte-integrations/connectors/source-trustpilot/integration_tests/abnormal_state.json +++ b/airbyte-integrations/connectors/source-trustpilot/integration_tests/abnormal_state.json @@ -1,5 +1,22 @@ -{ - "private_reviews": { - "5f5e954ec15b2700017c834f_createdAt": "2099-12-31T23:59:59Z" +[ + { + "streamDescriptor": { + "name": "private_reviews" + }, + "streamState": { + "states": [ + { + "cursor": { + "createdAt": "2099-12-18T15:48:56Z" + }, + "partition": { + "parent_slice": { + "business_unit": "example.com" + }, + "business_unit_id": "657e1a3ca4677d24861a2fd9" + } + } + ] + } } -} +] diff --git a/airbyte-integrations/connectors/source-trustpilot/integration_tests/invalid_config.json b/airbyte-integrations/connectors/source-trustpilot/integration_tests/invalid_config.json index 18ba88b6b60f..0e654fe9033f 100644 --- a/airbyte-integrations/connectors/source-trustpilot/integration_tests/invalid_config.json +++ b/airbyte-integrations/connectors/source-trustpilot/integration_tests/invalid_config.json @@ -1,12 +1,12 @@ { "credentials": { "auth_type": "oauth2.0", - "client_id": "aoVLPAXHFwryzNepiAY3yR7urlx9hhGr", - "client_secret": "EDLVnp9t11Oa375D", - "access_token": "MB4o9G3MpM5yrdIKo0OcYsOjeaBT", + "client_id": "invalid", + "client_secret": "invalid", + "access_token": "invalid", "token_expiry_date": "2023-03-15T00:11:00Z", "refresh_token": "ttfqulk6gPg2TOPL4wS23m6ZBR7wEsqG" }, "business_units": [], - "start_date": "2023-03-01T00:00:00Z" + "start_date": "2023-10-01T00:00:00Z" } diff --git a/airbyte-integrations/connectors/source-trustpilot/integration_tests/sample_config.json b/airbyte-integrations/connectors/source-trustpilot/integration_tests/sample_config.json index 75908bd4f8a9..43577c5e42f3 100644 --- a/airbyte-integrations/connectors/source-trustpilot/integration_tests/sample_config.json +++ b/airbyte-integrations/connectors/source-trustpilot/integration_tests/sample_config.json @@ -7,6 +7,6 @@ "token_expiry_date": "2023-03-15T00:11:00Z", "refresh_token": "ttfqulk6gPg2TOPL4wS23m6ZBR7wEsqG" }, - "business_units": ["my_domain.com"], - "start_date": "2023-03-01T00:00:00Z" + "business_units": [], + "start_date": "2023-10-01T00:00:00Z" } diff --git a/airbyte-integrations/connectors/source-trustpilot/integration_tests/sample_state.json b/airbyte-integrations/connectors/source-trustpilot/integration_tests/sample_state.json index d7a4a08e6bce..504c44a9d1c2 100644 --- a/airbyte-integrations/connectors/source-trustpilot/integration_tests/sample_state.json +++ b/airbyte-integrations/connectors/source-trustpilot/integration_tests/sample_state.json @@ -1,5 +1,22 @@ -{ - "private_reviews": { - "5f5e954ec15b2700017c834f_createdAt": "2023-03-01T00:00:00Z" +[ + { + "streamDescriptor": { + "name": "private_reviews" + }, + "streamState": { + "states": [ + { + "cursor": { + "createdAt": "2023-12-18T15:48:56Z" + }, + "partition": { + "parent_slice": { + "business_unit": "example.com" + }, + "business_unit_id": "657e1a3ca4677d24861a2fd9" + } + } + ] + } } -} +] diff --git a/airbyte-integrations/connectors/source-trustpilot/metadata.yaml b/airbyte-integrations/connectors/source-trustpilot/metadata.yaml index e1ff897f1f3d..834ba473fc00 100644 --- a/airbyte-integrations/connectors/source-trustpilot/metadata.yaml +++ b/airbyte-integrations/connectors/source-trustpilot/metadata.yaml @@ -2,7 +2,7 @@ data: connectorSubtype: api connectorType: source definitionId: d7e23ea6-d741-4314-9209-a33c91a2e945 - dockerImageTag: 0.1.1 + dockerImageTag: 0.2.0 dockerRepository: airbyte/source-trustpilot githubIssueLabel: source-trustpilot icon: trustpilot.svg @@ -21,7 +21,7 @@ data: documentationUrl: https://docs.airbyte.com/integrations/sources/trustpilot tags: - language:python - - cdk:python + - cdk:low-code ab_internal: sl: 100 ql: 100 diff --git a/airbyte-integrations/connectors/source-trustpilot/poetry.lock b/airbyte-integrations/connectors/source-trustpilot/poetry.lock index bf6f1521b3ff..c661d546e56f 100644 --- a/airbyte-integrations/connectors/source-trustpilot/poetry.lock +++ b/airbyte-integrations/connectors/source-trustpilot/poetry.lock @@ -1,31 +1,35 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "airbyte-cdk" -version = "0.80.0" +version = "1.8.0" description = "A framework for writing Airbyte Connectors." optional = false python-versions = "<4.0,>=3.9" files = [ - {file = "airbyte_cdk-0.80.0-py3-none-any.whl", hash = "sha256:060e92323a73674fa4e9e2e4a1eb312b9b9d072c9bbe5fa28f54ef21cb4974f3"}, - {file = "airbyte_cdk-0.80.0.tar.gz", hash = "sha256:1383512a83917fecca5b24cea4c72aa5c561cf96dd464485fbcefda48fe574c5"}, + {file = "airbyte_cdk-1.8.0-py3-none-any.whl", hash = "sha256:ca23d7877005fe87ffc4a3a3de29ee55eed625d874eb59b49664b156f9ae9ee2"}, + {file = "airbyte_cdk-1.8.0.tar.gz", hash = "sha256:ac82fbfd6b650b7ed015900748e30fdd2a4c574caa54d1bcc03cb584a17f1533"}, ] [package.dependencies] -airbyte-protocol-models = "0.5.1" +airbyte-protocol-models = ">=0.9.0,<1.0" backoff = "*" cachetools = "*" +cryptography = ">=42.0.5,<43.0.0" Deprecated = ">=1.2,<1.3" -dpath = ">=2.0.1,<2.1.0" +dpath = ">=2.1.6,<3.0.0" genson = "1.2.2" isodate = ">=0.6.1,<0.7.0" Jinja2 = ">=3.1.2,<3.2.0" jsonref = ">=0.2,<0.3" jsonschema = ">=3.2.0,<3.3.0" +langchain_core = "0.1.42" pendulum = "<3.0.0" pydantic = ">=1.10.8,<2.0.0" +pyjwt = ">=2.8.0,<3.0.0" pyrate-limiter = ">=3.1.0,<3.2.0" python-dateutil = "*" +pytz = "2024.1" PyYAML = ">=6.0.1,<7.0.0" requests = "*" requests_cache = "*" @@ -34,32 +38,22 @@ wcmatch = "8.4" [package.extras] file-based = ["avro (>=1.11.2,<1.12.0)", "fastavro (>=1.8.0,<1.9.0)", "markdown", "pdf2image (==1.16.3)", "pdfminer.six (==20221105)", "pyarrow (>=15.0.0,<15.1.0)", "pytesseract (==0.3.10)", "unstructured.pytesseract (>=0.3.12)", "unstructured[docx,pptx] (==0.10.27)"] sphinx-docs = ["Sphinx (>=4.2,<4.3)", "sphinx-rtd-theme (>=1.0,<1.1)"] -vector-db-based = ["cohere (==4.21)", "langchain (==0.0.271)", "openai[embeddings] (==0.27.9)", "tiktoken (==0.4.0)"] +vector-db-based = ["cohere (==4.21)", "langchain (==0.1.16)", "openai[embeddings] (==0.27.9)", "tiktoken (==0.4.0)"] [[package]] name = "airbyte-protocol-models" -version = "0.5.1" +version = "0.12.2" description = "Declares the Airbyte Protocol." optional = false python-versions = ">=3.8" files = [ - {file = "airbyte_protocol_models-0.5.1-py3-none-any.whl", hash = "sha256:dfe84e130e51ce2ae81a06d5aa36f6c5ce3152b9e36e6f0195fad6c3dab0927e"}, - {file = "airbyte_protocol_models-0.5.1.tar.gz", hash = "sha256:7c8b16c7c1c7956b1996052e40585a3a93b1e44cb509c4e97c1ee4fe507ea086"}, + {file = "airbyte_protocol_models-0.12.2-py3-none-any.whl", hash = "sha256:1780db5b26285865b858d26502933def8e11919c9436ccf7b8b9cb0170b07c2a"}, + {file = "airbyte_protocol_models-0.12.2.tar.gz", hash = "sha256:b7c4d9a7c32c0691601c2b9416af090a858e126666e2c8c880d7a1798eb519f0"}, ] [package.dependencies] pydantic = ">=1.9.2,<2.0.0" -[[package]] -name = "atomicwrites" -version = "1.4.1" -description = "Atomic file writes." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, -] - [[package]] name = "attrs" version = "23.2.0" @@ -103,13 +97,13 @@ files = [ [[package]] name = "cachetools" -version = "5.3.3" +version = "5.4.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, - {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, + {file = "cachetools-5.4.0-py3-none-any.whl", hash = "sha256:3ae3b49a3d5e28a77a0be2b37dbcb89005058959cb2323858c2657c4a8cab474"}, + {file = "cachetools-5.4.0.tar.gz", hash = "sha256:b8adc2e7c07f105ced7bc56dbb6dfbe7c4a00acce20e2227b3f355be89bc6827"}, ] [[package]] @@ -139,15 +133,79 @@ ujson = ["ujson (>=5.7.0)"] [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + [[package]] name = "charset-normalizer" version = "3.3.2" @@ -258,6 +316,60 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "cryptography" +version = "42.0.8" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, + {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, + {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, + {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, + {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, + {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, + {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + [[package]] name = "deprecated" version = "1.2.14" @@ -277,24 +389,24 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] [[package]] name = "dpath" -version = "2.0.8" +version = "2.2.0" description = "Filesystem-like pathing and searching for dictionaries" optional = false python-versions = ">=3.7" files = [ - {file = "dpath-2.0.8-py3-none-any.whl", hash = "sha256:f92f595214dd93a00558d75d4b858beee519f4cffca87f02616ad6cd013f3436"}, - {file = "dpath-2.0.8.tar.gz", hash = "sha256:a3440157ebe80d0a3ad794f1b61c571bef125214800ffdb9afc9424e8250fe9b"}, + {file = "dpath-2.2.0-py3-none-any.whl", hash = "sha256:b330a375ded0a0d2ed404440f6c6a715deae5313af40bbb01c8a41d891900576"}, + {file = "dpath-2.2.0.tar.gz", hash = "sha256:34f7e630dc55ea3f219e555726f5da4b4b25f2200319c8e6902c394258dd6a3e"}, ] [[package]] name = "exceptiongroup" -version = "1.2.1" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -363,6 +475,31 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jsonpatch" +version = "1.33" +description = "Apply JSON-Patches (RFC 6902)" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +files = [ + {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, + {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, +] + +[package.dependencies] +jsonpointer = ">=1.9" + +[[package]] +name = "jsonpointer" +version = "3.0.0" +description = "Identify specific nodes in a JSON document (RFC 6901)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, + {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, +] + [[package]] name = "jsonref" version = "0.2" @@ -395,6 +532,44 @@ six = ">=1.11.0" format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] format-nongpl = ["idna", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "webcolors"] +[[package]] +name = "langchain-core" +version = "0.1.42" +description = "Building applications with LLMs through composability" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langchain_core-0.1.42-py3-none-any.whl", hash = "sha256:c5653ffa08a44f740295c157a24c0def4a753333f6a2c41f76bf431cd00be8b5"}, + {file = "langchain_core-0.1.42.tar.gz", hash = "sha256:40751bf60ea5d8e2b2efe65290db434717ee3834870c002e40e2811f09d814e6"}, +] + +[package.dependencies] +jsonpatch = ">=1.33,<2.0" +langsmith = ">=0.1.0,<0.2.0" +packaging = ">=23.2,<24.0" +pydantic = ">=1,<3" +PyYAML = ">=5.3" +tenacity = ">=8.1.0,<9.0.0" + +[package.extras] +extended-testing = ["jinja2 (>=3,<4)"] + +[[package]] +name = "langsmith" +version = "0.1.93" +description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langsmith-0.1.93-py3-none-any.whl", hash = "sha256:811210b9d5f108f36431bd7b997eb9476a9ecf5a2abd7ddbb606c1cdcf0f43ce"}, + {file = "langsmith-0.1.93.tar.gz", hash = "sha256:285b6ad3a54f50fa8eb97b5f600acc57d0e37e139dd8cf2111a117d0435ba9b4"}, +] + +[package.dependencies] +orjson = ">=3.9.14,<4.0.0" +pydantic = {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""} +requests = ">=2,<3" + [[package]] name = "markupsafe" version = "2.1.5" @@ -464,15 +639,75 @@ files = [ {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] +[[package]] +name = "orjson" +version = "3.10.6" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.6-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:fb0ee33124db6eaa517d00890fc1a55c3bfe1cf78ba4a8899d71a06f2d6ff5c7"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c1c4b53b24a4c06547ce43e5fee6ec4e0d8fe2d597f4647fc033fd205707365"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eadc8fd310edb4bdbd333374f2c8fec6794bbbae99b592f448d8214a5e4050c0"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61272a5aec2b2661f4fa2b37c907ce9701e821b2c1285d5c3ab0207ebd358d38"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57985ee7e91d6214c837936dc1608f40f330a6b88bb13f5a57ce5257807da143"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:633a3b31d9d7c9f02d49c4ab4d0a86065c4a6f6adc297d63d272e043472acab5"}, + {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1c680b269d33ec444afe2bdc647c9eb73166fa47a16d9a75ee56a374f4a45f43"}, + {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f759503a97a6ace19e55461395ab0d618b5a117e8d0fbb20e70cfd68a47327f2"}, + {file = "orjson-3.10.6-cp310-none-win32.whl", hash = "sha256:95a0cce17f969fb5391762e5719575217bd10ac5a189d1979442ee54456393f3"}, + {file = "orjson-3.10.6-cp310-none-win_amd64.whl", hash = "sha256:df25d9271270ba2133cc88ee83c318372bdc0f2cd6f32e7a450809a111efc45c"}, + {file = "orjson-3.10.6-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b1ec490e10d2a77c345def52599311849fc063ae0e67cf4f84528073152bb2ba"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d43d3feb8f19d07e9f01e5b9be4f28801cf7c60d0fa0d279951b18fae1932b"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3045267e98fe749408eee1593a142e02357c5c99be0802185ef2170086a863"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c27bc6a28ae95923350ab382c57113abd38f3928af3c80be6f2ba7eb8d8db0b0"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d27456491ca79532d11e507cadca37fb8c9324a3976294f68fb1eff2dc6ced5a"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05ac3d3916023745aa3b3b388e91b9166be1ca02b7c7e41045da6d12985685f0"}, + {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1335d4ef59ab85cab66fe73fd7a4e881c298ee7f63ede918b7faa1b27cbe5212"}, + {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4bbc6d0af24c1575edc79994c20e1b29e6fb3c6a570371306db0993ecf144dc5"}, + {file = "orjson-3.10.6-cp311-none-win32.whl", hash = "sha256:450e39ab1f7694465060a0550b3f6d328d20297bf2e06aa947b97c21e5241fbd"}, + {file = "orjson-3.10.6-cp311-none-win_amd64.whl", hash = "sha256:227df19441372610b20e05bdb906e1742ec2ad7a66ac8350dcfd29a63014a83b"}, + {file = "orjson-3.10.6-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ea2977b21f8d5d9b758bb3f344a75e55ca78e3ff85595d248eee813ae23ecdfb"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6f3d167d13a16ed263b52dbfedff52c962bfd3d270b46b7518365bcc2121eed"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f710f346e4c44a4e8bdf23daa974faede58f83334289df80bc9cd12fe82573c7"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7275664f84e027dcb1ad5200b8b18373e9c669b2a9ec33d410c40f5ccf4b257e"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0943e4c701196b23c240b3d10ed8ecd674f03089198cf503105b474a4f77f21f"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:446dee5a491b5bc7d8f825d80d9637e7af43f86a331207b9c9610e2f93fee22a"}, + {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:64c81456d2a050d380786413786b057983892db105516639cb5d3ee3c7fd5148"}, + {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:960db0e31c4e52fa0fc3ecbaea5b2d3b58f379e32a95ae6b0ebeaa25b93dfd34"}, + {file = "orjson-3.10.6-cp312-none-win32.whl", hash = "sha256:a6ea7afb5b30b2317e0bee03c8d34c8181bc5a36f2afd4d0952f378972c4efd5"}, + {file = "orjson-3.10.6-cp312-none-win_amd64.whl", hash = "sha256:874ce88264b7e655dde4aeaacdc8fd772a7962faadfb41abe63e2a4861abc3dc"}, + {file = "orjson-3.10.6-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:66680eae4c4e7fc193d91cfc1353ad6d01b4801ae9b5314f17e11ba55e934183"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caff75b425db5ef8e8f23af93c80f072f97b4fb3afd4af44482905c9f588da28"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3722fddb821b6036fd2a3c814f6bd9b57a89dc6337b9924ecd614ebce3271394"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2c116072a8533f2fec435fde4d134610f806bdac20188c7bd2081f3e9e0133f"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6eeb13218c8cf34c61912e9df2de2853f1d009de0e46ea09ccdf3d757896af0a"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:965a916373382674e323c957d560b953d81d7a8603fbeee26f7b8248638bd48b"}, + {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03c95484d53ed8e479cade8628c9cea00fd9d67f5554764a1110e0d5aa2de96e"}, + {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e060748a04cccf1e0a6f2358dffea9c080b849a4a68c28b1b907f272b5127e9b"}, + {file = "orjson-3.10.6-cp38-none-win32.whl", hash = "sha256:738dbe3ef909c4b019d69afc19caf6b5ed0e2f1c786b5d6215fbb7539246e4c6"}, + {file = "orjson-3.10.6-cp38-none-win_amd64.whl", hash = "sha256:d40f839dddf6a7d77114fe6b8a70218556408c71d4d6e29413bb5f150a692ff7"}, + {file = "orjson-3.10.6-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:697a35a083c4f834807a6232b3e62c8b280f7a44ad0b759fd4dce748951e70db"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd502f96bf5ea9a61cbc0b2b5900d0dd68aa0da197179042bdd2be67e51a1e4b"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f215789fb1667cdc874c1b8af6a84dc939fd802bf293a8334fce185c79cd359b"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2debd8ddce948a8c0938c8c93ade191d2f4ba4649a54302a7da905a81f00b56"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5410111d7b6681d4b0d65e0f58a13be588d01b473822483f77f513c7f93bd3b2"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb1f28a137337fdc18384079fa5726810681055b32b92253fa15ae5656e1dddb"}, + {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bf2fbbce5fe7cd1aa177ea3eab2b8e6a6bc6e8592e4279ed3db2d62e57c0e1b2"}, + {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:79b9b9e33bd4c517445a62b90ca0cc279b0f1f3970655c3df9e608bc3f91741a"}, + {file = "orjson-3.10.6-cp39-none-win32.whl", hash = "sha256:30b0a09a2014e621b1adf66a4f705f0809358350a757508ee80209b2d8dae219"}, + {file = "orjson-3.10.6-cp39-none-win_amd64.whl", hash = "sha256:49e3bc615652617d463069f91b867a4458114c5b104e13b7ae6872e5f79d0844"}, + {file = "orjson-3.10.6.tar.gz", hash = "sha256:e54b63d0a7c6c54a5f5f726bc93a2078111ef060fec4ecbf34c5db800ca3b3a7"}, +] + [[package]] name = "packaging" -version = "24.0" +version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] @@ -541,59 +776,66 @@ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "pycparser" +version = "2.22" +description = "C parser in Python" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.8" files = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] [[package]] name = "pydantic" -version = "1.10.15" +version = "1.10.17" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22ed12ee588b1df028a2aa5d66f07bf8f8b4c8579c2e96d5a9c1f96b77f3bb55"}, - {file = "pydantic-1.10.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75279d3cac98186b6ebc2597b06bcbc7244744f6b0b44a23e4ef01e5683cc0d2"}, - {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f1666a9940d3d68683c9d96e39640f709d7a72ff8702987dab1761036206bb"}, - {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82790d4753ee5d00739d6cb5cf56bceb186d9d6ce134aca3ba7befb1eedbc2c8"}, - {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d207d5b87f6cbefbdb1198154292faee8017d7495a54ae58db06762004500d00"}, - {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e49db944fad339b2ccb80128ffd3f8af076f9f287197a480bf1e4ca053a866f0"}, - {file = "pydantic-1.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:d3b5c4cbd0c9cb61bbbb19ce335e1f8ab87a811f6d589ed52b0254cf585d709c"}, - {file = "pydantic-1.10.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3d5731a120752248844676bf92f25a12f6e45425e63ce22e0849297a093b5b0"}, - {file = "pydantic-1.10.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c365ad9c394f9eeffcb30a82f4246c0006417f03a7c0f8315d6211f25f7cb654"}, - {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3287e1614393119c67bd4404f46e33ae3be3ed4cd10360b48d0a4459f420c6a3"}, - {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be51dd2c8596b25fe43c0a4a59c2bee4f18d88efb8031188f9e7ddc6b469cf44"}, - {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6a51a1dd4aa7b3f1317f65493a182d3cff708385327c1c82c81e4a9d6d65b2e4"}, - {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4e316e54b5775d1eb59187f9290aeb38acf620e10f7fd2f776d97bb788199e53"}, - {file = "pydantic-1.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:0d142fa1b8f2f0ae11ddd5e3e317dcac060b951d605fda26ca9b234b92214986"}, - {file = "pydantic-1.10.15-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ea210336b891f5ea334f8fc9f8f862b87acd5d4a0cbc9e3e208e7aa1775dabf"}, - {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3453685ccd7140715e05f2193d64030101eaad26076fad4e246c1cc97e1bb30d"}, - {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bea1f03b8d4e8e86702c918ccfd5d947ac268f0f0cc6ed71782e4b09353b26f"}, - {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:005655cabc29081de8243126e036f2065bd7ea5b9dff95fde6d2c642d39755de"}, - {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:af9850d98fc21e5bc24ea9e35dd80a29faf6462c608728a110c0a30b595e58b7"}, - {file = "pydantic-1.10.15-cp37-cp37m-win_amd64.whl", hash = "sha256:d31ee5b14a82c9afe2bd26aaa405293d4237d0591527d9129ce36e58f19f95c1"}, - {file = "pydantic-1.10.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e09c19df304b8123938dc3c53d3d3be6ec74b9d7d0d80f4f4b5432ae16c2022"}, - {file = "pydantic-1.10.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7ac9237cd62947db00a0d16acf2f3e00d1ae9d3bd602b9c415f93e7a9fc10528"}, - {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:584f2d4c98ffec420e02305cf675857bae03c9d617fcfdc34946b1160213a948"}, - {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbc6989fad0c030bd70a0b6f626f98a862224bc2b1e36bfc531ea2facc0a340c"}, - {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d573082c6ef99336f2cb5b667b781d2f776d4af311574fb53d908517ba523c22"}, - {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bd7030c9abc80134087d8b6e7aa957e43d35714daa116aced57269a445b8f7b"}, - {file = "pydantic-1.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:3350f527bb04138f8aff932dc828f154847fbdc7a1a44c240fbfff1b57f49a12"}, - {file = "pydantic-1.10.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:51d405b42f1b86703555797270e4970a9f9bd7953f3990142e69d1037f9d9e51"}, - {file = "pydantic-1.10.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a980a77c52723b0dc56640ced396b73a024d4b74f02bcb2d21dbbac1debbe9d0"}, - {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f1a1fb467d3f49e1708a3f632b11c69fccb4e748a325d5a491ddc7b5d22383"}, - {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:676ed48f2c5bbad835f1a8ed8a6d44c1cd5a21121116d2ac40bd1cd3619746ed"}, - {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92229f73400b80c13afcd050687f4d7e88de9234d74b27e6728aa689abcf58cc"}, - {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2746189100c646682eff0bce95efa7d2e203420d8e1c613dc0c6b4c1d9c1fde4"}, - {file = "pydantic-1.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:394f08750bd8eaad714718812e7fab615f873b3cdd0b9d84e76e51ef3b50b6b7"}, - {file = "pydantic-1.10.15-py3-none-any.whl", hash = "sha256:28e552a060ba2740d0d2aabe35162652c1459a0b9069fe0db7f4ee0e18e74d58"}, - {file = "pydantic-1.10.15.tar.gz", hash = "sha256:ca832e124eda231a60a041da4f013e3ff24949d94a01154b137fc2f2a43c3ffb"}, + {file = "pydantic-1.10.17-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fa51175313cc30097660b10eec8ca55ed08bfa07acbfe02f7a42f6c242e9a4b"}, + {file = "pydantic-1.10.17-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7e8988bb16988890c985bd2093df9dd731bfb9d5e0860db054c23034fab8f7a"}, + {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:371dcf1831f87c9e217e2b6a0c66842879a14873114ebb9d0861ab22e3b5bb1e"}, + {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4866a1579c0c3ca2c40575398a24d805d4db6cb353ee74df75ddeee3c657f9a7"}, + {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:543da3c6914795b37785703ffc74ba4d660418620cc273490d42c53949eeeca6"}, + {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7623b59876f49e61c2e283551cc3647616d2fbdc0b4d36d3d638aae8547ea681"}, + {file = "pydantic-1.10.17-cp310-cp310-win_amd64.whl", hash = "sha256:409b2b36d7d7d19cd8310b97a4ce6b1755ef8bd45b9a2ec5ec2b124db0a0d8f3"}, + {file = "pydantic-1.10.17-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fa43f362b46741df8f201bf3e7dff3569fa92069bcc7b4a740dea3602e27ab7a"}, + {file = "pydantic-1.10.17-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a72d2a5ff86a3075ed81ca031eac86923d44bc5d42e719d585a8eb547bf0c9b"}, + {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4ad32aed3bf5eea5ca5decc3d1bbc3d0ec5d4fbcd72a03cdad849458decbc63"}, + {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb4e741782e236ee7dc1fb11ad94dc56aabaf02d21df0e79e0c21fe07c95741"}, + {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d2f89a719411cb234105735a520b7c077158a81e0fe1cb05a79c01fc5eb59d3c"}, + {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db3b48d9283d80a314f7a682f7acae8422386de659fffaba454b77a083c3937d"}, + {file = "pydantic-1.10.17-cp311-cp311-win_amd64.whl", hash = "sha256:9c803a5113cfab7bbb912f75faa4fc1e4acff43e452c82560349fff64f852e1b"}, + {file = "pydantic-1.10.17-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:820ae12a390c9cbb26bb44913c87fa2ff431a029a785642c1ff11fed0a095fcb"}, + {file = "pydantic-1.10.17-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c1e51d1af306641b7d1574d6d3307eaa10a4991542ca324f0feb134fee259815"}, + {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e53fb834aae96e7b0dadd6e92c66e7dd9cdf08965340ed04c16813102a47fab"}, + {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2495309b1266e81d259a570dd199916ff34f7f51f1b549a0d37a6d9b17b4dc"}, + {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:098ad8de840c92ea586bf8efd9e2e90c6339d33ab5c1cfbb85be66e4ecf8213f"}, + {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:525bbef620dac93c430d5d6bdbc91bdb5521698d434adf4434a7ef6ffd5c4b7f"}, + {file = "pydantic-1.10.17-cp312-cp312-win_amd64.whl", hash = "sha256:6654028d1144df451e1da69a670083c27117d493f16cf83da81e1e50edce72ad"}, + {file = "pydantic-1.10.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c87cedb4680d1614f1d59d13fea353faf3afd41ba5c906a266f3f2e8c245d655"}, + {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11289fa895bcbc8f18704efa1d8020bb9a86314da435348f59745473eb042e6b"}, + {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94833612d6fd18b57c359a127cbfd932d9150c1b72fea7c86ab58c2a77edd7c7"}, + {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d4ecb515fa7cb0e46e163ecd9d52f9147ba57bc3633dca0e586cdb7a232db9e3"}, + {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7017971ffa7fd7808146880aa41b266e06c1e6e12261768a28b8b41ba55c8076"}, + {file = "pydantic-1.10.17-cp37-cp37m-win_amd64.whl", hash = "sha256:e840e6b2026920fc3f250ea8ebfdedf6ea7a25b77bf04c6576178e681942ae0f"}, + {file = "pydantic-1.10.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bfbb18b616abc4df70591b8c1ff1b3eabd234ddcddb86b7cac82657ab9017e33"}, + {file = "pydantic-1.10.17-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebb249096d873593e014535ab07145498957091aa6ae92759a32d40cb9998e2e"}, + {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c209af63ccd7b22fba94b9024e8b7fd07feffee0001efae50dd99316b27768"}, + {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b40c9e13a0b61583e5599e7950490c700297b4a375b55b2b592774332798b7"}, + {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c31d281c7485223caf6474fc2b7cf21456289dbaa31401844069b77160cab9c7"}, + {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae5184e99a060a5c80010a2d53c99aee76a3b0ad683d493e5f0620b5d86eeb75"}, + {file = "pydantic-1.10.17-cp38-cp38-win_amd64.whl", hash = "sha256:ad1e33dc6b9787a6f0f3fd132859aa75626528b49cc1f9e429cdacb2608ad5f0"}, + {file = "pydantic-1.10.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17c0ee7192e54a10943f245dc79e36d9fe282418ea05b886e1c666063a7b54"}, + {file = "pydantic-1.10.17-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cafb9c938f61d1b182dfc7d44a7021326547b7b9cf695db5b68ec7b590214773"}, + {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95ef534e3c22e5abbdbdd6f66b6ea9dac3ca3e34c5c632894f8625d13d084cbe"}, + {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d96b8799ae3d782df7ec9615cb59fc32c32e1ed6afa1b231b0595f6516e8ab"}, + {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ab2f976336808fd5d539fdc26eb51f9aafc1f4b638e212ef6b6f05e753c8011d"}, + {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8ad363330557beac73159acfbeed220d5f1bfcd6b930302a987a375e02f74fd"}, + {file = "pydantic-1.10.17-cp39-cp39-win_amd64.whl", hash = "sha256:48db882e48575ce4b39659558b2f9f37c25b8d348e37a2b4e32971dd5a7d6227"}, + {file = "pydantic-1.10.17-py3-none-any.whl", hash = "sha256:e41b5b973e5c64f674b3b4720286ded184dcc26a691dd55f34391c62c6934688"}, + {file = "pydantic-1.10.17.tar.gz", hash = "sha256:f434160fb14b353caf634149baaf847206406471ba70e64657c1e8330277a991"}, ] [package.dependencies] @@ -603,6 +845,23 @@ typing-extensions = ">=4.2.0" dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] +[[package]] +name = "pyjwt" +version = "2.8.0" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, + {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, +] + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + [[package]] name = "pyrate-limiter" version = "3.1.1" @@ -661,27 +920,25 @@ files = [ [[package]] name = "pytest" -version = "6.2.5" +version = "8.3.2" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, - {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, + {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, + {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, ] [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -toml = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-mock" @@ -714,6 +971,17 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + [[package]] name = "pytzdata" version = "2020.1" @@ -787,13 +1055,13 @@ files = [ [[package]] name = "requests" -version = "2.32.1" +version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.1-py3-none-any.whl", hash = "sha256:21ac9465cdf8c1650fe1ecde8a71669a93d4e6f147550483a2967d08396a56a5"}, - {file = "requests-2.32.1.tar.gz", hash = "sha256:eb97e87e64c79e64e5b8ac75cee9dd1f97f49e289b083ee6be96268930725685"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -808,13 +1076,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-cache" -version = "1.2.0" +version = "1.2.1" description = "A persistent cache for python requests" optional = false python-versions = ">=3.8" files = [ - {file = "requests_cache-1.2.0-py3-none-any.whl", hash = "sha256:490324301bf0cb924ff4e6324bd2613453e7e1f847353928b08adb0fdfb7f722"}, - {file = "requests_cache-1.2.0.tar.gz", hash = "sha256:db1c709ca343cc1cd5b6c8b1a5387298eceed02306a6040760db538c885e3838"}, + {file = "requests_cache-1.2.1-py3-none-any.whl", hash = "sha256:1285151cddf5331067baa82598afe2d47c7495a1334bfe7a7d329b43e9fd3603"}, + {file = "requests_cache-1.2.1.tar.gz", hash = "sha256:68abc986fdc5b8d0911318fbb5f7c80eebcd4d01bfacc6685ecf8876052511d1"}, ] [package.dependencies] @@ -855,19 +1123,19 @@ fixture = ["fixtures"] [[package]] name = "setuptools" -version = "69.5.1" +version = "71.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-71.1.0-py3-none-any.whl", hash = "sha256:33874fdc59b3188304b2e7c80d9029097ea31627180896fb549c578ceb8a0855"}, + {file = "setuptools-71.1.0.tar.gz", hash = "sha256:032d42ee9fb536e33087fb66cac5f840eb9391ed05637b3f2a76a7c8fb477936"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -881,25 +1149,40 @@ files = [ ] [[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" +name = "tenacity" +version = "8.5.0" +description = "Retry code until it succeeds" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.8" files = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, + {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"}, + {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"}, +] + +[package.extras] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "typing-extensions" -version = "4.11.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] @@ -918,13 +1201,13 @@ six = "*" [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -1029,4 +1312,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.9,<3.12" -content-hash = "990042bd8aff2361370f7cea38b2dffbadb5bd28397a241166061ec2619f6426" +content-hash = "5b0cd125a4941563e47d35d2a1603eb16db2c74dd30a0ea65043dfabe1a82cd9" diff --git a/airbyte-integrations/connectors/source-trustpilot/pyproject.toml b/airbyte-integrations/connectors/source-trustpilot/pyproject.toml index f9cf7af4c8fc..91da0ab07500 100644 --- a/airbyte-integrations/connectors/source-trustpilot/pyproject.toml +++ b/airbyte-integrations/connectors/source-trustpilot/pyproject.toml @@ -3,7 +3,7 @@ requires = [ "poetry-core>=1.0.0",] build-backend = "poetry.core.masonry.api" [tool.poetry] -version = "0.1.1" +version = "0.2.0" name = "source-trustpilot" description = "Source implementation for Trustpilot." authors = [ "Airbyte ",] @@ -17,12 +17,12 @@ include = "source_trustpilot" [tool.poetry.dependencies] python = "^3.9,<3.12" -airbyte-cdk = "0.80.0" +airbyte-cdk = "^1" [tool.poetry.scripts] source-trustpilot = "source_trustpilot.run:run" [tool.poetry.group.dev.dependencies] -requests-mock = "^1.9.3" -pytest = "^6.2" -pytest-mock = "^3.6.1" +requests-mock = "*" +pytest-mock = "*" +pytest = "*" diff --git a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/auth.py b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/auth.py deleted file mode 100644 index fdf5c8948dd4..000000000000 --- a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/auth.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (c) 2023 Airbyte, Inc., all rights reserved. -# - -from typing import Any, Mapping, Tuple - -from airbyte_cdk.sources.streams.http.requests_native_auth import SingleUseRefreshTokenOauth2Authenticator, TokenAuthenticator - - -class TrustpilotApikeyAuthenticator(TokenAuthenticator): - """ - Requesting data from the Public API requires only the API key. - - See also https://documentation-apidocumentation.trustpilot.com/#Auth - """ - - def __init__(self, token: str): - super().__init__(token, auth_method=None, auth_header="apikey") - - @property - def token(self) -> str: - """ - Unfortunately, the TokenAuthenticator class does not support an empty - 'auth_method' so we have to hack this that only the API key is - provided in the HTTP header. - """ - return self._token - - -class TrustpilotOauth2Authenticator(SingleUseRefreshTokenOauth2Authenticator): - """ - Requesting data from APIs which require OAuth2 with refresh. - - See also https://documentation-apidocumentation.trustpilot.com/#Auth - """ - - def get_auth_header(self) -> Mapping[str, Any]: - headers = super().get_auth_header() - headers.update( - { - "apikey": self.get_client_id(), # The API key is required for OAuth2 requests as well ... - "Content-Type": "application/x-www-form-urlencoded", - } - ) - return headers - - def refresh_access_token(self) -> Tuple[str, int, str]: - """ - Required because Trustpilot passes the 'expires_in' parameter as string and not as integer. - - Would not be necessary when https://github.com/airbytehq/airbyte/pull/23921 gets merged. - """ - response_json = self._get_refresh_access_token_response() - return ( - response_json[self.get_access_token_name()], - int(response_json[self.get_expires_in_name()]), - response_json[self.get_refresh_token_name()], - ) - - -__all__ = ["TrustpilotApikeyAuthenticator", "TrustpilotOauth2Authenticator"] diff --git a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/manifest.yaml b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/manifest.yaml new file mode 100644 index 000000000000..df53db66afa3 --- /dev/null +++ b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/manifest.yaml @@ -0,0 +1,339 @@ +version: 0.71.0 +type: DeclarativeSource + +definitions: + oauth_authenticator: + type: OAuthAuthenticator + client_id: "{{ config['credentials']['client_id'] }}" + client_secret: "{{ config['credentials']['client_secret'] }}" + refresh_token: "{{ config['credentials']['refresh_token'] }}" + token_refresh_endpoint: "https://api.trustpilot.com/v1/oauth/oauth-business-users-for-applications/accesstoken?response_type=token" + grant_type: refresh_token + access_token_name: access_token + + apikey_authenticator: + type: ApiKeyAuthenticator + header: apikey + api_token: "{{ config['client_id'] }}" + + selective_authenticator: + type: SelectiveAuthenticator + authenticator_selection_path: ["credentials", "auth_type"] + authenticators: + oauth2.0: "#/definitions/oauth_authenticator" + apikey: "#/definitions/apikey_authenticator" + +check: + type: CheckStream + stream_names: + - configured_business_units +streams: + - name: configured_business_units + type: DeclarativeStream + retriever: + type: SimpleRetriever + paginator: + type: NoPagination + requester: + path: /business-units/find + type: HttpRequester + url_base: https://api.trustpilot.com/v1 + http_method: GET + authenticator: + $ref: "#/definitions/selective_authenticator" + error_handler: + type: CompositeErrorHandler + error_handlers: + - type: DefaultErrorHandler + response_filters: + - type: HttpResponseFilter + action: IGNORE + http_codes: + - 400 + - 403 + - 404 + error_message: Credentails might be expired, please validate + request_headers: + apikey: "{{ config['client_id'] }}" + request_body_json: {} + request_parameters: {} + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + partition_router: + - type: ListPartitionRouter + values: "{{ config['business_units'] }}" + cursor_field: business_unit + request_option: + type: RequestOption + field_name: name + inject_into: request_parameter + primary_key: + - id + transformations: + - type: RemoveFields + field_pointers: + - ["links"] + - name: private_reviews + type: DeclarativeStream + retriever: + type: SimpleRetriever + paginator: + type: DefaultPaginator + page_size_option: + type: RequestOption + field_name: perPage + inject_into: request_parameter + page_token_option: + type: RequestOption + field_name: page + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + requester: + path: >- + /private/business-units/{{ stream_partition['business_unit_id'] + }}/reviews + type: HttpRequester + url_base: https://api.trustpilot.com/v1 + http_method: GET + authenticator: + $ref: "#/definitions/selective_authenticator" + error_handler: + type: CompositeErrorHandler + error_handlers: + - type: DefaultErrorHandler + response_filters: + - type: HttpResponseFilter + action: IGNORE + http_codes: + - 400 + - 403 + - 404 + error_message: Business account needs oauth flow, refer documentation for more details + request_headers: {} + request_body_json: {} + request_parameters: + orderBy: createdat.desc + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - reviews + partition_router: + - type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + stream: + name: configured_business_units + type: DeclarativeStream + retriever: + type: SimpleRetriever + paginator: + type: NoPagination + requester: + path: /business-units/find + type: HttpRequester + url_base: https://api.trustpilot.com/v1 + http_method: GET + authenticator: + $ref: "#/definitions/selective_authenticator" + error_handler: + type: CompositeErrorHandler + error_handlers: + - type: DefaultErrorHandler + response_filters: + - type: HttpResponseFilter + action: IGNORE + http_codes: + - 400 + - 403 + - 404 + error_message: Credentails might be expired, please validate + request_headers: + apikey: "{{ config['client_id'] }}" + request_body_json: {} + request_parameters: {} + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + partition_router: + - type: ListPartitionRouter + values: "{{ config['business_units'] }}" + cursor_field: business_unit + request_option: + type: RequestOption + field_name: name + inject_into: request_parameter + primary_key: + - id + parent_key: id + partition_field: business_unit_id + primary_key: + - id + transformations: + - type: RemoveFields + field_pointers: + - ["links"] + incremental_sync: + type: DatetimeBasedCursor + cursor_field: createdAt + is_data_feed: true + start_datetime: + type: MinMaxDatetime + datetime: "{{ config['start_date'] }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + cursor_datetime_formats: + - "%Y-%m-%dT%H:%M:%SZ" + - name: business_units + type: DeclarativeStream + retriever: + type: SimpleRetriever + paginator: + type: DefaultPaginator + page_size_option: + type: RequestOption + field_name: perPage + inject_into: request_parameter + page_token_option: + type: RequestOption + field_name: page + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + requester: + path: /business-units/all + type: HttpRequester + url_base: https://api.trustpilot.com/v1 + http_method: GET + authenticator: + $ref: "#/definitions/selective_authenticator" + error_handler: + type: CompositeErrorHandler + error_handlers: + - type: DefaultErrorHandler + response_filters: + - type: HttpResponseFilter + action: IGNORE + http_codes: + - 400 + - 403 + - 404 + error_message: Credentails might be expired, please validate + request_headers: + apikey: "{{ config['client_id'] }}" + request_body_json: {} + request_parameters: {} + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - businessUnits + primary_key: + - id + transformations: + - type: RemoveFields + field_pointers: + - ["links"] +spec: + type: Spec + documentation_url: https://docs.airbyte.com/integrations/sources/trustpilot + connection_specification: + $schema: http://json-schema.org/draft-07/schema# + title: Trustpilot Spec + type: object + additionalProperties: true + required: + - credentials + - business_units + - start_date + properties: + credentials: + title: Authorization Method + type: object + order: 0 + oneOf: + - type: object + title: "OAuth 2.0" + required: + - client_id + - client_secret + - refresh_token + - access_token + - token_expiry_date + properties: + auth_type: + type: string + const: "oauth2.0" + client_id: + type: string + title: API key + description: The API key of the Trustpilot API application. (represents the OAuth Client ID) + airbyte_secret: true + client_secret: + type: string + title: Secret + description: The Secret of the Trustpilot API application. (represents the OAuth Client Secret) + airbyte_secret: true + access_token: + type: string + title: Access Token + description: Access Token for making authenticated requests. + airbyte_secret: true + token_expiry_date: + type: string + title: Token expiry date time + description: The date-time when the access token should be refreshed. + format: date-time + refresh_token: + type: string + title: Refresh token + description: The key to refresh the expired access_token. + airbyte_secret: true + + - type: object + title: "API Key" + description: The API key authentication method gives you access to only the streams which are part of the Public API. When you want to get streams available via the Consumer API (e.g. the private reviews) you need to use authentication method OAuth 2.0. + required: + - client_id + properties: + auth_type: + type: string + const: "apikey" + client_id: + type: string + title: API key + description: The API key of the Trustpilot API application. + airbyte_secret: true + business_units: + type: array + items: + type: string + title: Business Unit names + description: The names of business units which shall be synchronized. Some streams e.g. configured_business_units or private_reviews use this configuration. + examples: + - mydomain.com + - www.mydomain.com + start_date: + type: string + title: Start Date + description: For streams with sync. method incremental the start date time to be used + pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ + examples: + - "%Y-%m-%dT%H:%M:%SZ" +metadata: + autoImportSchema: + business_units: true + private_reviews: true + configured_business_units: true diff --git a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/business_units.json b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/business_units.json index 5649ecad8313..ba99626421d1 100644 --- a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/business_units.json +++ b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/business_units.json @@ -1,9 +1,10 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": true, "properties": { "id": { - "type": "string" + "type": ["string", "null"] }, "displayName": { "type": ["string", "null"] @@ -17,7 +18,7 @@ "referring": { "type": ["array", "null"], "items": { - "type": "string" + "type": ["string", "null"] } } } diff --git a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/configured_business_units.json b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/configured_business_units.json index 5649ecad8313..deda7dccafa9 100644 --- a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/configured_business_units.json +++ b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/configured_business_units.json @@ -1,15 +1,16 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": true, "properties": { "id": { - "type": "string" + "type": ["string", "null"] }, "displayName": { "type": ["string", "null"] }, "name": { - "type": "object", + "type": ["object", "null"], "properties": { "identifying": { "type": ["string", "null"] @@ -17,7 +18,7 @@ "referring": { "type": ["array", "null"], "items": { - "type": "string" + "type": ["string", "null"] } } } diff --git a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/private_reviews.json b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/private_reviews.json index 34f857b7f57a..ce6e97b9d964 100644 --- a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/private_reviews.json +++ b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/schemas/private_reviews.json @@ -1,15 +1,16 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": true, "properties": { "id": { - "type": "string" + "type": ["string", "null"] }, "consumer": { - "type": "object", + "type": ["object", "null"], "properties": { "id": { - "type": "string" + "type": ["string", "null"] }, "displayName": { "type": ["string", "null"] @@ -23,10 +24,10 @@ } }, "businessUnit": { - "type": "object", + "type": ["object", "null"], "properties": { "id": { - "type": "string" + "type": ["string", "null"] }, "identifyingName": { "type": ["string", "null"] @@ -106,9 +107,9 @@ } }, "tags": { - "type": "array", + "type": ["array", "null"], "items": { - "type": "object", + "type": ["object", "null"], "properties": { "group": { "type": ["string", "null"] @@ -120,15 +121,15 @@ } }, "findReviewer": { - "type": "object", + "type": ["object", "null"], "properties": { "isEligible": { - "type": "boolean" + "type": ["boolean", "null"] }, "requests": { - "type": "array", + "type": ["array", "null"], "items": { - "type": "object", + "type": ["object", "null"], "properties": { "status": { "type": ["string", "null"] @@ -176,7 +177,7 @@ } }, "isVerified": { - "type": "boolean" + "type": ["boolean", "null"] }, "numberOfLikes": { "type": "integer" @@ -198,7 +199,7 @@ "format": "date-time" }, "reasons": { - "type": "array", + "type": ["array", "null"], "items": { "type": "null", "$comment": "Data structure at this point is unknown" @@ -207,13 +208,13 @@ } }, "complianceLabels": { - "type": "array", + "type": ["array", "null"], "items": { - "type": "string" + "type": ["string", "null"] } }, "countsTowardsTrustScore": { - "type": "boolean" + "type": ["boolean", "null"] }, "countsTowardsLocationTrustScore": { "type": ["boolean", "null"] @@ -227,9 +228,9 @@ } }, "businessUnitHistory": { - "type": "array", + "type": ["array", "null"], "items": { - "type": "object", + "type": ["object", "null"], "properties": { "identifyingName": { "type": ["string", "null"] diff --git a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/source.py b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/source.py index 40f36cbd45b6..d6885cda4238 100644 --- a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/source.py +++ b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/source.py @@ -2,88 +2,17 @@ # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # +from airbyte_cdk.sources.declarative.yaml_declarative_source import YamlDeclarativeSource -from typing import Any, List, Mapping, MutableMapping, Optional, Tuple +""" +This file provides the necessary constructs to interpret a provided declarative YAML configuration file into +source connector. -import pendulum -from airbyte_cdk.models import SyncMode -from airbyte_cdk.sources import AbstractSource -from airbyte_cdk.sources.streams import Stream +WARNING: Do not modify this file. +""" -from .auth import TrustpilotApikeyAuthenticator, TrustpilotOauth2Authenticator -from .streams import BusinessUnits, ConfiguredBusinessUnits, PrivateReviews - -class SourceTrustpilot(AbstractSource): - def __init__(self, *args, **kargs): - super().__init__(*args, **kargs) - self.__public_auth_params: Mapping[str, Any] = {} - self.__oauth2_auth_params: Mapping[str, Any] = {} - self.__configured_business_units_stream: Optional[ConfiguredBusinessUnits] = None - - def _public_auth_params(self, config: MutableMapping[str, Any]): - """ - Creates the authorization parameters for the Trustpilot Public API. - - The Public API only requires the API key (stored on the credentials/client_id). - It does not require OAuth2 authentication. - - See also: https://documentation-apidocumentation.trustpilot.com/#Auth - """ - if not self.__public_auth_params: - auth = TrustpilotApikeyAuthenticator(token=config["credentials"]["client_id"]) - self.__public_auth_params = {"authenticator": auth} - return self.__public_auth_params - - def _oauth2_auth_params(self, config: MutableMapping[str, Any]): - if not self.__oauth2_auth_params: - auth = TrustpilotOauth2Authenticator( - config, token_refresh_endpoint="https://api.trustpilot.com/v1/oauth/oauth-business-users-for-applications/refresh" - ) - self.__oauth2_auth_params = {"authenticator": auth, "api_key": config["credentials"]["client_id"]} - return self.__oauth2_auth_params - - def _configured_business_units_stream(self, config: MutableMapping[str, Any]) -> ConfiguredBusinessUnits: - if not self.__configured_business_units_stream: - public_auth_params = self._public_auth_params(config) - self.__configured_business_units_stream = ConfiguredBusinessUnits( - business_unit_names=config["business_units"], **public_auth_params - ) - return self.__configured_business_units_stream - - def check_connection(self, logger, config) -> Tuple[bool, any]: - try: - # NOTE: When `config['credentials']['auth_type'] == 'oauth2.0'` is true, we - # could here use a stream which requires OAuth 2.0 access. - # - # We currently don't do that there. 'private_reviews' seems to be too heavy - # for that and it could be empty. - business_units = self._configured_business_units_stream(config) - for stream_slice in business_units.stream_slices(sync_mode=SyncMode.full_refresh): - next(business_units.read_records(sync_mode=SyncMode.full_refresh, stream_slice=stream_slice)) - return True, None - except Exception as error: - return False, f"Unable to connect to Trustpilot API with the provided credentials - {repr(error)}" - - def streams(self, config: Mapping[str, Any]) -> List[Stream]: - public_auth_params = self._public_auth_params(config) - - configured_business_units_stream = self._configured_business_units_stream(config) - - business_unit_names = config["business_units"] - common_kwargs = {"business_unit_names": business_unit_names} - incremental_kwargs = {"start_date": pendulum.parse(config["start_date"])} - pubic_common_kwargs = {**common_kwargs, **public_auth_params} - - # The Public API streams - streams = [configured_business_units_stream, BusinessUnits(**pubic_common_kwargs)] - - # API streams requiring OAuth 2.0 - if config["credentials"]["auth_type"] == "oauth2.0": - auth_params = self._oauth2_auth_params(config) - consumer_common_kwargs = {**common_kwargs, **auth_params} - consumer_incremental_kwargs = {**consumer_common_kwargs, **incremental_kwargs} - - streams.extend([PrivateReviews(**consumer_incremental_kwargs)]) - - return streams +# Declarative Source +class SourceTrustpilot(YamlDeclarativeSource): + def __init__(self): + super().__init__(**{"path_to_yaml": "manifest.yaml"}) diff --git a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/spec.yaml b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/spec.yaml deleted file mode 100644 index 4d2b0b50cedd..000000000000 --- a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/spec.yaml +++ /dev/null @@ -1,85 +0,0 @@ -documentationUrl: https://docsurl.com -connectionSpecification: - $schema: http://json-schema.org/draft-07/schema# - title: Trustpilot Spec - type: object - required: - - credentials - - business_units - - start_date - properties: - credentials: - title: Authorization Method - type: object - order: 0 - oneOf: - - type: object - title: "OAuth 2.0" - required: - - client_id - - client_secret - - refresh_token - - access_token - - token_expiry_date - properties: - auth_type: - type: string - const: "oauth2.0" - client_id: - type: string - title: API key - description: The API key of the Trustpilot API application. (represents the OAuth Client ID) - airbyte_secret: true - client_secret: - type: string - title: Secret - description: The Secret of the Trustpilot API application. (represents the OAuth Client Secret) - airbyte_secret: true - access_token: - type: string - title: Access Token - description: Access Token for making authenticated requests. - airbyte_secret: true - token_expiry_date: - type: string - title: Token expiry date time - description: The date-time when the access token should be refreshed. - format: date-time - refresh_token: - type: string - title: Refresh token - description: The key to refresh the expired access_token. - airbyte_secret: true - - - type: object - title: "API Key" - description: The API key authentication method gives you access to only the streams which are part of the Public API. When you want to get streams available via the Consumer API (e.g. the private reviews) you need to use authentication method OAuth 2.0. - required: - - client_id - properties: - auth_type: - type: string - const: "apikey" - client_id: - type: string - title: API key - description: The API key of the Trustpilot API application. - airbyte_secret: true - - business_units: - type: array - items: - type: string - title: Business Unit names - description: The names of business units which shall be synchronized. Some streams e.g. configured_business_units or private_reviews use this configuration. - examples: - - mydomain.com - - www.mydomain.com - - start_date: - type: string - title: Start Date - description: For streams with sync. method incremental the start date time to be used - pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ - examples: - - "%Y-%m-%dT%H:%M:%S" diff --git a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/streams.py b/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/streams.py deleted file mode 100644 index cd7bfb401cf9..000000000000 --- a/airbyte-integrations/connectors/source-trustpilot/source_trustpilot/streams.py +++ /dev/null @@ -1,276 +0,0 @@ -# -# Copyright (c) 2023 Airbyte, Inc., all rights reserved. -# - -from abc import ABC -from datetime import datetime -from typing import Any, Iterable, List, Mapping, MutableMapping, Optional -from urllib.parse import parse_qs, urlparse - -import pendulum -import requests -from airbyte_cdk.models import SyncMode -from airbyte_cdk.sources.streams.http import HttpStream -from requests.auth import AuthBase - -from .auth import TrustpilotApikeyAuthenticator - - -class TrustpilotStream(HttpStream, ABC): - url_base = "https://api.trustpilot.com/v1/" - - def __init__(self, api_key: str = None, authenticator: AuthBase = None, business_unit_names: List[str] = None): - super().__init__(authenticator=authenticator) - self._api_key = api_key - self._business_unit_names = business_unit_names - - @property - def data_field(self) -> str: - """ - Specifies root object name in a stream response - - If not specified, the whole response is passed as a single row. - """ - return None - - def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]: - return None - - def request_params( - self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, any] = None, next_page_token: Mapping[str, Any] = None - ) -> MutableMapping[str, Any]: - return dict(next_page_token or {}) - - def request_headers( - self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None - ) -> Mapping[str, Any]: - return {} - - def _clean_row(self, row: Mapping[str, Any]): - """ - A internal function to clean the data from API stuff which we do not want to - store in the stream. - """ - # We don't want to expose the 'links' in the data - if "links" in row: - del row["links"] - - return row - - def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]: - json_content = response.json() - - if self.data_field: - for row in json_content[self.data_field]: - yield self._clean_row(row) - else: # when no data_field is provided, we assume that each request represents - # a single row. - yield self._clean_row(json_content) - - -class TrustpilotPaginagedStream(TrustpilotStream): - per_page: int = 20 - """ - How many entries shall be get per page. Suggested to use always - the API max. page size to avoid too many API requests. - """ - - def request_params( - self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, any] = None, next_page_token: Mapping[str, Any] = None - ) -> MutableMapping[str, Any]: - params = super().request_params(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token) - if self.per_page: - params["perPage"] = self.per_page - return params - - def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]: - content = response.json() - - # used in e.g. https://documentation-apidocumentation.trustpilot.com/business-units-api-(public)#get-a-list-of-all-business-units - if "cursor" in content: - return {"cursor": content["cursor"]} - - # search for a 'next-page' URL in the 'links' part - # used in e.g. https://documentation-apidocumentation.trustpilot.com/business-units-api#business-unit-private-reviews - for link in content.get("links", []): - if link["method"] == "GET" and link["rel"] == "next-page": - next_page_url = link["href"] - if next_page_url: - next_url = urlparse(next_page_url) - return parse_qs(next_url.query) - - -class TrustpilotIncrementalStream(TrustpilotPaginagedStream, ABC): - cursor_field = "createdAt" - filter_param = "startDateTime" - _start_date: pendulum.DateTime = None - - _current_stream_slice: Mapping[str, any] = None - - def __init__(self, start_date: datetime = None, **kargs): - super().__init__(**kargs) - if start_date: - self._start_date = pendulum.instance(start_date) - - @property - def state_field(self): - if "business_unit_id" in self._current_stream_slice: - return f"{self._current_stream_slice['business_unit_id']}_{self.cursor_field}" - else: - return self.cursor_field - - def get_updated_state(self, current_stream_state: MutableMapping[str, Any], latest_record: Mapping[str, Any]) -> Mapping[str, Any]: - """ - Override to determine the latest state after reading the latest record. This typically compared the cursor_field from the latest record and - the current state and picks the 'most' recent cursor. This is how a stream's state is determined. Required for incremental. - """ - latest_state = current_stream_state.get(self.state_field) - if isinstance(latest_state, str): - latest_state = pendulum.parse(latest_state) - last_record_value = pendulum.parse(latest_record[self.cursor_field]) - new_cursor_value = max(last_record_value, latest_state or last_record_value) - current_stream_state[self.state_field] = str(new_cursor_value) - return current_stream_state - - def request_params( - self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, any] = None, next_page_token: Mapping[str, Any] = None - ) -> MutableMapping[str, Any]: - """Add incremental parameters""" - params = super().request_params(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token) - - self._current_stream_slice = stream_slice - - if self.filter_param not in params or self._current_stream_slice != stream_slice: - # use cursor as filter value only if it is not already a parameter (i.e. we are in the middle of the pagination) - stream_state = stream_state or {} - state_str = stream_state.get(self.state_field) - state = pendulum.parse(state_str) if state_str else self._start_date - # Note: The Trustpilot API does not specify here the time zone. But - # since we take the value from the records, we don't care about - # this ... - params[self.filter_param] = max(state, self._start_date).strftime("%Y-%m-%dT%H:%M:%S") - - return params - - -class ConfiguredBusinessUnits(TrustpilotStream): - """ - Iterate over all configured business unit names and returns their public - information. - - See also: https://documentation-apidocumentation.trustpilot.com/business-units-api-(public)#find-a-business-unit - """ - - primary_key = "id" - - def path( - self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None - ) -> str: - return "business-units/find" - - def request_params( - self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, any] = None, next_page_token: Mapping[str, Any] = None - ) -> MutableMapping[str, Any]: - params = super().request_params(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token) - params.update({"name": stream_slice["business_unit_name"]}) - return params - - def stream_slices(self, stream_state: Mapping[str, Any] = None, **kwargs) -> Iterable[Optional[Mapping[str, any]]]: - for business_unit_name in self._business_unit_names: - yield {"business_unit_name": business_unit_name} - - -class _AllBusinessUnitsIterator(TrustpilotPaginagedStream): - """ - Iterates over all available business units and return their minimum data - including the business unit id which is used further. - - See also https://documentation-apidocumentation.trustpilot.com/business-units-api-(public)#get-a-list-of-all-business-units - """ - - primary_key = "id" - data_field = "businessUnits" - per_page = 1000 - - def path( - self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None - ) -> str: - return "business-units/all" - - -class BusinessUnits(TrustpilotStream): - """ - Get the public business information for all business units. - - See also: https://documentation-apidocumentation.trustpilot.com/business-units-api-(public)#get-public-business-unit - """ - - primary_key = "id" - - def path( - self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None - ) -> str: - return f"business-units/{stream_slice['business_unit_id']}" - - def request_params( - self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, any] = None, next_page_token: Mapping[str, Any] = None - ) -> MutableMapping[str, Any]: - return {} - - def stream_slices(self, stream_state: Mapping[str, Any] = None, **kwargs) -> Iterable[Optional[Mapping[str, any]]]: - all_business_units = _AllBusinessUnitsIterator(authenticator=self._session.auth) - for busines_unit_data in all_business_units.read_records(sync_mode=SyncMode.full_refresh): - yield {"business_unit_id": busines_unit_data["id"]} - - -class PrivateReviews(TrustpilotIncrementalStream): - """ - Business Unit private reviews. - - See also: https://documentation-apidocumentation.trustpilot.com/business-units-api#business-unit-private-reviews - """ - - primary_key = "id" - data_field = "reviews" - per_page = 100 - - def path( - self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None - ) -> str: - return f"private/business-units/{stream_slice['business_unit_id']}/reviews" - - def request_params( - self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, any] = None, next_page_token: Mapping[str, Any] = None - ) -> MutableMapping[str, Any]: - params = super().request_params(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token) - params.update({"orderBy": "createdat.asc"}) - return params - - def stream_slices(self, stream_state: Mapping[str, Any] = None, **kwargs) -> Iterable[Optional[Mapping[str, any]]]: - """ - Currently we only support syncing from a specific pre-defined business unit name - given in the configuration. Probably in a future version when someone demands - it we could add support for generic business units sync. - """ - business_units_find = ConfiguredBusinessUnits( - authenticator=TrustpilotApikeyAuthenticator(token=self._api_key), business_unit_names=self._business_unit_names - ) - for stream_slice in business_units_find.stream_slices(sync_mode=SyncMode.full_refresh): - for busines_unit_data in business_units_find.read_records(sync_mode=SyncMode.full_refresh, stream_slice=stream_slice): - yield {"business_unit_id": busines_unit_data["id"]} - - def _clean_row(self, row: Mapping[str, Any]): - """ - A internal function to clean the data from unnecessary data. - """ - row = super()._clean_row(row) - - # remove nested 'links' - if "consumer" in row: - if "links" in row["consumer"]: - del row["consumer"]["links"] - if "businessUnit" in row: - if "links" in row["businessUnit"]: - del row["businessUnit"]["links"] - - return row diff --git a/airbyte-integrations/connectors/source-trustpilot/unit_tests/test_incremental_streams.py b/airbyte-integrations/connectors/source-trustpilot/unit_tests/test_incremental_streams.py deleted file mode 100644 index c8723363e522..000000000000 --- a/airbyte-integrations/connectors/source-trustpilot/unit_tests/test_incremental_streams.py +++ /dev/null @@ -1,66 +0,0 @@ -# -# Copyright (c) 2023 Airbyte, Inc., all rights reserved. -# - - -import pendulum -from airbyte_cdk.models import SyncMode -from pytest import fixture -from source_trustpilot.streams import TrustpilotIncrementalStream - - -@fixture -def patch_incremental_base_class(mocker): - # Mock abstract methods to enable instantiating abstract class - mocker.patch.object(TrustpilotIncrementalStream, "path", "v0/example_endpoint") - mocker.patch.object(TrustpilotIncrementalStream, "primary_key", "test_primary_key") - mocker.patch.object(TrustpilotIncrementalStream, "_start_date", pendulum.now("UTC").add(years=-1)) - mocker.patch.object(TrustpilotIncrementalStream, "_current_stream_slice", {"business_unit_id": "5f5e954ec15b2700017c834f"}) - - mocker.patch.object(TrustpilotIncrementalStream, "__abstractmethods__", set()) - - -def test_cursor_field(patch_incremental_base_class): - stream = TrustpilotIncrementalStream() - expected_cursor_field = "createdAt" - assert stream.cursor_field == expected_cursor_field - - -def test_get_updated_state(patch_incremental_base_class): - stream = TrustpilotIncrementalStream() - inputs = { - "current_stream_state": {"5f5e954ec15b2700017c834f_createdAt": "2023-03-01T00:00:00+00:00"}, - "latest_record": {"createdAt": "2023-03-23T15:12:17Z"}, - } - expected_state = {"5f5e954ec15b2700017c834f_createdAt": "2023-03-23T15:12:17+00:00"} - assert stream.get_updated_state(**inputs) == expected_state - - -def test_stream_slices(patch_incremental_base_class): - stream = TrustpilotIncrementalStream() - inputs = { - "sync_mode": SyncMode.incremental, - "cursor_field": ["createdAt"], - "stream_state": {"5f5e954ec15b2700017c834f_createdAt": "2023-03-01T00:00:00+00:00"}, - } - # TODO: replace this with your expected stream slices list - expected_stream_slice = [None] - assert stream.stream_slices(**inputs) == expected_stream_slice - - -def test_supports_incremental(patch_incremental_base_class, mocker): - mocker.patch.object(TrustpilotIncrementalStream, "cursor_field", "dummy_field") - stream = TrustpilotIncrementalStream() - assert stream.supports_incremental - - -def test_source_defined_cursor(patch_incremental_base_class): - stream = TrustpilotIncrementalStream() - assert stream.source_defined_cursor - - -def test_stream_checkpoint_interval(patch_incremental_base_class): - stream = TrustpilotIncrementalStream() - # TODO: replace this with your expected checkpoint interval - expected_checkpoint_interval = None - assert stream.state_checkpoint_interval == expected_checkpoint_interval diff --git a/airbyte-integrations/connectors/source-trustpilot/unit_tests/test_source.py b/airbyte-integrations/connectors/source-trustpilot/unit_tests/test_source.py deleted file mode 100644 index 1b64655ca03a..000000000000 --- a/airbyte-integrations/connectors/source-trustpilot/unit_tests/test_source.py +++ /dev/null @@ -1,27 +0,0 @@ -# -# Copyright (c) 2023 Airbyte, Inc., all rights reserved. -# - -import json -from unittest.mock import MagicMock - -from source_trustpilot.source import SourceTrustpilot - - -def test_check_connection(mocker): - source = SourceTrustpilot() - with open("secrets/config.json") as f: - logger_mock, config_mock = MagicMock(), json.load(f) - assert source.check_connection(logger_mock, config_mock) == (True, None) - - -def test_streams(mocker): - source = SourceTrustpilot() - config_mock = { - "credentials": {"auth_type": "__api_key__", "client_id": "__client_id__"}, - "business_units": ["my_domain.com"], - "start_date": "2023-01-01T00:00:00Z", - } - streams = source.streams(config_mock) - expected_streams_number = 2 - assert len(streams) == expected_streams_number diff --git a/airbyte-integrations/connectors/source-trustpilot/unit_tests/test_streams.py b/airbyte-integrations/connectors/source-trustpilot/unit_tests/test_streams.py deleted file mode 100644 index abb559d7276d..000000000000 --- a/airbyte-integrations/connectors/source-trustpilot/unit_tests/test_streams.py +++ /dev/null @@ -1,88 +0,0 @@ -# -# Copyright (c) 2023 Airbyte, Inc., all rights reserved. -# - -import json -from http import HTTPStatus -from unittest.mock import MagicMock - -import pytest -from source_trustpilot.streams import TrustpilotStream - - -@pytest.fixture -def patch_base_class(mocker): - # Mock abstract methods to enable instantiating abstract class - mocker.patch.object(TrustpilotStream, "path", "v0/example_endpoint") - mocker.patch.object(TrustpilotStream, "primary_key", "test_primary_key") - mocker.patch.object(TrustpilotStream, "__abstractmethods__", set()) - - -def test_request_params(patch_base_class): - stream = TrustpilotStream() - inputs = {"stream_slice": None, "stream_state": None, "next_page_token": None} - expected_params = {} - assert stream.request_params(**inputs) == expected_params - - -def test_next_page_token(patch_base_class): - stream = TrustpilotStream() - # TODO: replace this with your input parameters - inputs = {"response": MagicMock()} - # TODO: replace this with your expected next page token - expected_token = None - assert stream.next_page_token(**inputs) == expected_token - - -def test_parse_response(patch_base_class): - stream = TrustpilotStream() - response = MagicMock() - response.json = lambda: json.loads( - b"""{ - "links": { - "rel": "next-page", - "href": "http://..." - }, - "id": "12351241" -}""" - ) - inputs = {"response": response} - - expected_parsed_object = {"id": "12351241"} - assert next(stream.parse_response(**inputs)) == expected_parsed_object - - -def test_request_headers(patch_base_class): - stream = TrustpilotStream() - inputs = {"stream_slice": None, "stream_state": None, "next_page_token": None} - expected_headers = {} - assert stream.request_headers(**inputs) == expected_headers - - -def test_http_method(patch_base_class): - stream = TrustpilotStream() - expected_method = "GET" - assert stream.http_method == expected_method - - -@pytest.mark.parametrize( - ("http_status", "should_retry"), - [ - (HTTPStatus.OK, False), - (HTTPStatus.BAD_REQUEST, False), - (HTTPStatus.TOO_MANY_REQUESTS, True), - (HTTPStatus.INTERNAL_SERVER_ERROR, True), - ], -) -def test_should_retry(patch_base_class, http_status, should_retry): - response_mock = MagicMock() - response_mock.status_code = http_status - stream = TrustpilotStream() - assert stream.should_retry(response_mock) == should_retry - - -def test_backoff_time(patch_base_class): - response_mock = MagicMock() - stream = TrustpilotStream() - expected_backoff_time = None - assert stream.backoff_time(response_mock) == expected_backoff_time diff --git a/docs/integrations/sources/trustpilot.md b/docs/integrations/sources/trustpilot.md index c212b3f700da..36777653edbd 100644 --- a/docs/integrations/sources/trustpilot.md +++ b/docs/integrations/sources/trustpilot.md @@ -61,7 +61,8 @@ The Trustpilot connector should not run into any limits under normal usage. Plea | Version | Date | Pull Request | Subject | | :------ | :--------- | :------------------------------------------------------- | :-------------- | -| 0.1.1 | 2024-05-21 | [38487](https://github.com/airbytehq/airbyte/pull/38487) | [autopull] base image + poetry + up_to_date | +| `0.2.0` | 2024-08-01 | [36200](https://github.com/airbytehq/airbyte/pull/36200) | Migrate to Low Code | +| `0.1.1` | 2024-05-21 | [38487](https://github.com/airbytehq/airbyte/pull/38487) | [autopull] base image + poetry + up_to_date | | `0.1.0` | 2023-03-16 | [24009](https://github.com/airbytehq/airbyte/pull/24009) | Initial version | \ No newline at end of file